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