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