xref: /dpdk/lib/telemetry/telemetry_legacy.c (revision e5fb1a9698e7111473ca0980fdf6c0edb7acdf91)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #ifndef RTE_EXEC_ENV_WINDOWS
6 #include <unistd.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9 #include <pthread.h>
10 #endif /* !RTE_EXEC_ENV_WINDOWS */
11 
12 /* we won't link against libbsd, so just always use DPDKs-specific strlcpy */
13 #undef RTE_USE_LIBBSD
14 #include <rte_string_fns.h>
15 #include <rte_common.h>
16 #include <rte_spinlock.h>
17 
18 #include "telemetry_internal.h"
19 
20 #define MAX_LEN 128
21 #define BUF_SIZE 1024
22 #define CLIENTS_UNREG_ACTION "\"action\":2"
23 #define CLIENTS_CMD "\"command\":\"clients\""
24 #define CLIENTS_DATA "\"data\":{\"client_path\":\""
25 #define STATS_ACTION "\"action\":0"
26 #define DATA_REQ_LABEL "\"data\":"
27 #define TELEMETRY_LEGACY_MAX_CALLBACKS 4
28 
29 
30 static int
31 register_client(const char *cmd __rte_unused,
32 		const char *params __rte_unused,
33 		char *buffer, int buf_len);
34 
35 struct json_command {
36 	char action[MAX_LEN];
37 	char cmd[MAX_LEN];
38 	char data[MAX_LEN];
39 	telemetry_legacy_cb fn;
40 
41 };
42 
43 struct json_command callbacks[TELEMETRY_LEGACY_MAX_CALLBACKS] = {
44 		{
45 			.action = "\"action\":1",
46 			.cmd = CLIENTS_CMD,
47 			.data = CLIENTS_DATA,
48 			.fn = register_client
49 		}
50 };
51 int num_legacy_callbacks = 1;
52 static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER;
53 
54 int
55 rte_telemetry_legacy_register(const char *cmd,
56 		enum rte_telemetry_legacy_data_req data_req,
57 		telemetry_legacy_cb fn)
58 {
59 	if (fn == NULL)
60 		return -EINVAL;
61 	if (num_legacy_callbacks >= (int) RTE_DIM(callbacks))
62 		return -ENOENT;
63 
64 	rte_spinlock_lock(&callback_sl);
65 	strlcpy(callbacks[num_legacy_callbacks].action, STATS_ACTION, MAX_LEN);
66 	snprintf(callbacks[num_legacy_callbacks].cmd, MAX_LEN,
67 			"\"command\":\"%s\"", cmd);
68 	snprintf(callbacks[num_legacy_callbacks].data, MAX_LEN,
69 			data_req ? "%s{\"" : "%snull",
70 			DATA_REQ_LABEL);
71 	callbacks[num_legacy_callbacks].fn = fn;
72 	num_legacy_callbacks++;
73 	rte_spinlock_unlock(&callback_sl);
74 
75 	return 0;
76 }
77 
78 static int
79 register_client(const char *cmd __rte_unused, const char *params,
80 		char *buffer __rte_unused, int buf_len __rte_unused)
81 {
82 #ifndef RTE_EXEC_ENV_WINDOWS
83 	pthread_t th;
84 	char data[BUF_SIZE];
85 	int fd;
86 	struct sockaddr_un addrs;
87 #endif /* !RTE_EXEC_ENV_WINDOWS */
88 
89 	if (!strchr(params, ':')) {
90 		fprintf(stderr, "Invalid data\n");
91 		return -1;
92 	}
93 #ifndef RTE_EXEC_ENV_WINDOWS
94 	strlcpy(data, strchr(params, ':'), sizeof(data));
95 	memcpy(data, &data[strlen(":\"")], strlen(data));
96 	if (!strchr(data, '\"')) {
97 		fprintf(stderr, "Invalid client data\n");
98 		return -1;
99 	}
100 	*strchr(data, '\"') = 0;
101 
102 	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
103 	if (fd < 0) {
104 		perror("Failed to open socket");
105 		return -1;
106 	}
107 	addrs.sun_family = AF_UNIX;
108 	strlcpy(addrs.sun_path, data, sizeof(addrs.sun_path));
109 
110 	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
111 		perror("\nClient connection error\n");
112 		close(fd);
113 		return -1;
114 	}
115 	pthread_create(&th, NULL, &legacy_client_handler,
116 			(void *)(uintptr_t)fd);
117 #endif /* !RTE_EXEC_ENV_WINDOWS */
118 	return 0;
119 }
120 
121 #ifndef RTE_EXEC_ENV_WINDOWS
122 
123 static int
124 send_error_response(int s, int err)
125 {
126 	const char *desc;
127 	char out_buf[100000];
128 
129 	switch (err) {
130 	case -ENOMEM:
131 		desc = "Memory Allocation Error";
132 		break;
133 	case -EINVAL:
134 		desc = "Invalid Argument 404";
135 		break;
136 	case -EPERM:
137 		desc = "Unknown";
138 		break;
139 	default:
140 		/* Default case keeps behaviour of Telemetry library */
141 		printf("\nInvalid error type: %d\n", err);
142 		return -EINVAL;
143 	}
144 	int used = snprintf(out_buf, sizeof(out_buf), "{\"status_code\": "
145 			"\"Status Error: %s\", \"data\": null}", desc);
146 	if (write(s, out_buf, used) < 0) {
147 		perror("Error writing to socket");
148 		return -1;
149 	}
150 	return 0;
151 }
152 
153 static void
154 perform_command(telemetry_legacy_cb fn, const char *param, int s)
155 {
156 	char out_buf[100000];
157 	int ret, used = 0;
158 
159 	ret = fn("", param, out_buf, sizeof(out_buf));
160 	if (ret < 0) {
161 		ret = send_error_response(s, ret);
162 		if (ret < 0)
163 			printf("\nCould not send error response\n");
164 		return;
165 	}
166 	used += ret;
167 	if (write(s, out_buf, used) < 0)
168 		perror("Error writing to socket");
169 }
170 
171 static int
172 parse_client_request(char *buffer, int buf_len, int s)
173 {
174 	int i;
175 	char *data = buffer + buf_len;
176 	telemetry_legacy_cb fn = NULL;
177 	const char *valid_sep = ",}";
178 	if (buffer[0] != '{' || buffer[buf_len - 1] != '}')
179 		return -EPERM;
180 
181 	if (strstr(buffer, CLIENTS_UNREG_ACTION) && strstr(buffer, CLIENTS_CMD)
182 			&& strstr(buffer, CLIENTS_DATA))
183 		return 0;
184 
185 	for (i = 0; i < num_legacy_callbacks; i++) {
186 		char *action_ptr = strstr(buffer, callbacks[i].action);
187 		char *cmd_ptr = strstr(buffer, callbacks[i].cmd);
188 		char *data_ptr = strstr(buffer, callbacks[i].data);
189 		if (!action_ptr || !cmd_ptr || !data_ptr)
190 			continue;
191 
192 		char action_sep = action_ptr[strlen(callbacks[i].action)];
193 		char cmd_sep = cmd_ptr[strlen(callbacks[i].cmd)];
194 		if (!(strchr(valid_sep, action_sep) && strchr(valid_sep,
195 				cmd_sep)))
196 			return -EPERM;
197 		char data_sep;
198 
199 		if (!strchr(data_ptr, '{'))
200 			data_sep = data_ptr[strlen(callbacks[i].data)];
201 		else {
202 			if (!strchr(data_ptr, '}'))
203 				return -EINVAL;
204 			char *data_end = strchr(data_ptr, '}');
205 			data = data_ptr + strlen(DATA_REQ_LABEL);
206 			data_sep = data_end[1];
207 			data_end[1] = 0;
208 		}
209 		if (!strchr(valid_sep, data_sep))
210 			return -EPERM;
211 		fn = callbacks[i].fn;
212 		break;
213 	}
214 
215 	if (!fn)
216 		return -EINVAL;
217 	perform_command(fn, data, s);
218 	return 0;
219 }
220 
221 void *
222 legacy_client_handler(void *sock_id)
223 {
224 	int s = (int)(uintptr_t)sock_id;
225 	int ret;
226 	char buffer_recv[BUF_SIZE];
227 	/* receive data is not null terminated */
228 	int bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1);
229 
230 	while (bytes > 0) {
231 		buffer_recv[bytes] = 0;
232 		int i, j;
233 		char buffer[BUF_SIZE];
234 		for (i = 0, j = 0; buffer_recv[i] != '\0'; i++) {
235 			buffer[j] = buffer_recv[i];
236 			j += !isspace(buffer_recv[i]);
237 		}
238 		buffer[j] = 0;
239 		ret = parse_client_request(buffer, j, s);
240 		if (ret < 0) {
241 			ret = send_error_response(s, ret);
242 			if (ret < 0)
243 				printf("\nCould not send error response\n");
244 		}
245 		bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1);
246 	}
247 	close(s);
248 	return NULL;
249 }
250 
251 #endif /* !RTE_EXEC_ENV_WINDOWS */
252