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
rte_telemetry_legacy_register(const char * cmd,enum rte_telemetry_legacy_data_req data_req,telemetry_legacy_cb fn)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
register_client(const char * cmd __rte_unused,const char * params,char * buffer __rte_unused,int buf_len __rte_unused)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 memmove(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
send_error_response(int s,int err)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
perform_command(telemetry_legacy_cb fn,const char * param,int s)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
parse_client_request(char * buffer,int buf_len,int s)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 *
legacy_client_handler(void * sock_id)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