Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2020 Intel Corporation
3 : : */
4 : :
5 : : #include <ctype.h>
6 : : #include <errno.h>
7 : : #include <stdlib.h>
8 : : #ifndef RTE_EXEC_ENV_WINDOWS
9 : : #include <unistd.h>
10 : : #include <pthread.h>
11 : : #include <sys/socket.h>
12 : : #include <sys/un.h>
13 : : #include <sys/stat.h>
14 : : #endif /* !RTE_EXEC_ENV_WINDOWS */
15 : :
16 : : #include <eal_export.h>
17 : : #include <rte_string_fns.h>
18 : : #include <rte_common.h>
19 : : #include <rte_spinlock.h>
20 : : #include <rte_log.h>
21 : :
22 : : #include "rte_telemetry.h"
23 : : #include "telemetry_json.h"
24 : : #include "telemetry_data.h"
25 : : #include "telemetry_internal.h"
26 : :
27 : : #define MAX_CMD_LEN 56
28 : : #define MAX_OUTPUT_LEN (1024 * 16)
29 : : #define MAX_CONNECTIONS 10
30 : :
31 : : #ifndef RTE_EXEC_ENV_WINDOWS
32 : : static void *
33 : : client_handler(void *socket);
34 : : #endif /* !RTE_EXEC_ENV_WINDOWS */
35 : :
36 : : struct cmd_callback {
37 : : char cmd[MAX_CMD_LEN];
38 : : telemetry_cb fn;
39 : : telemetry_arg_cb fn_arg;
40 : : void *arg;
41 : : char help[RTE_TEL_MAX_STRING_LEN];
42 : : };
43 : :
44 : : #ifndef RTE_EXEC_ENV_WINDOWS
45 : : struct socket {
46 : : int sock;
47 : : char path[sizeof(((struct sockaddr_un *)0)->sun_path)];
48 : : telemetry_sock_handler fn;
49 : : RTE_ATOMIC(uint16_t) *num_clients;
50 : : };
51 : : static struct socket v2_socket; /* socket for v2 telemetry */
52 : : static struct socket v1_socket; /* socket for v1 telemetry */
53 : : #endif /* !RTE_EXEC_ENV_WINDOWS */
54 : :
55 : : static const char *telemetry_version; /* save rte_version */
56 : : static const char *socket_dir; /* runtime directory */
57 : : static rte_cpuset_t *thread_cpuset;
58 : :
59 [ - + ]: 303 : RTE_LOG_REGISTER_DEFAULT(logtype, WARNING);
60 : : #define RTE_LOGTYPE_TELEMETRY logtype
61 : : #define TMTY_LOG_LINE(l, ...) RTE_LOG_LINE(l, TELEMETRY, "" __VA_ARGS__)
62 : :
63 : : /* list of command callbacks, with one command registered by default */
64 : : static struct cmd_callback *callbacks;
65 : : static int num_callbacks; /* How many commands are registered */
66 : : /* Used when accessing or modifying list of command callbacks */
67 : : static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER;
68 : : #ifndef RTE_EXEC_ENV_WINDOWS
69 : : static RTE_ATOMIC(uint16_t) v2_clients;
70 : : #endif /* !RTE_EXEC_ENV_WINDOWS */
71 : :
72 : : static int
73 : 28135 : register_cmd(const char *cmd, const char *help,
74 : : telemetry_cb fn, telemetry_arg_cb fn_arg, void *arg)
75 : : {
76 : : struct cmd_callback *new_callbacks;
77 : : const char *cmdp = cmd;
78 : : int i = 0;
79 : :
80 [ + - + - : 28135 : if (strlen(cmd) >= MAX_CMD_LEN || (fn == NULL && fn_arg == NULL) || cmd[0] != '/'
+ - ]
81 [ + - ]: 28135 : || strlen(help) >= RTE_TEL_MAX_STRING_LEN)
82 : : return -EINVAL;
83 : :
84 [ + + ]: 500756 : while (*cmdp != '\0') {
85 [ + + + + : 472621 : if (!isalnum(*cmdp) && *cmdp != '_' && *cmdp != '/')
+ - ]
86 : : return -EINVAL;
87 : 472621 : cmdp++;
88 : : }
89 : :
90 : : rte_spinlock_lock(&callback_sl);
91 : 28135 : new_callbacks = realloc(callbacks, sizeof(callbacks[0]) * (num_callbacks + 1));
92 [ - + ]: 28135 : if (new_callbacks == NULL) {
93 : : rte_spinlock_unlock(&callback_sl);
94 : 0 : return -ENOMEM;
95 : : }
96 : 28135 : callbacks = new_callbacks;
97 : :
98 [ + + + + ]: 291994 : while (i < num_callbacks && strcmp(cmd, callbacks[i].cmd) > 0)
99 : 263859 : i++;
100 [ + + ]: 28135 : if (i != num_callbacks)
101 : : /* Move elements to keep the list alphabetical */
102 : 26922 : memmove(callbacks + i + 1, callbacks + i,
103 : 26922 : sizeof(struct cmd_callback) * (num_callbacks - i));
104 : :
105 : 28135 : strlcpy(callbacks[i].cmd, cmd, MAX_CMD_LEN);
106 : 28135 : callbacks[i].fn = fn;
107 : 28135 : callbacks[i].fn_arg = fn_arg;
108 : 28135 : callbacks[i].arg = arg;
109 : 28135 : strlcpy(callbacks[i].help, help, RTE_TEL_MAX_STRING_LEN);
110 : 28135 : num_callbacks++;
111 : : rte_spinlock_unlock(&callback_sl);
112 : :
113 : 28135 : return 0;
114 : : }
115 : :
116 : : RTE_EXPORT_SYMBOL(rte_telemetry_register_cmd)
117 : : int
118 : 22681 : rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help)
119 : : {
120 : 22681 : return register_cmd(cmd, help, fn, NULL, NULL);
121 : : }
122 : :
123 : : RTE_EXPORT_SYMBOL(rte_telemetry_register_cmd_arg)
124 : : int
125 : 5454 : rte_telemetry_register_cmd_arg(const char *cmd, telemetry_arg_cb fn, void *arg, const char *help)
126 : : {
127 : 5454 : return register_cmd(cmd, help, NULL, fn, arg);
128 : : }
129 : :
130 : : #ifndef RTE_EXEC_ENV_WINDOWS
131 : :
132 : : static int
133 : 287 : list_commands(const char *cmd __rte_unused, const char *params __rte_unused,
134 : : struct rte_tel_data *d)
135 : : {
136 : : int i;
137 : :
138 : 287 : rte_tel_data_start_array(d, RTE_TEL_STRING_VAL);
139 : : rte_spinlock_lock(&callback_sl);
140 [ + + ]: 27265 : for (i = 0; i < num_callbacks; i++)
141 : 26978 : rte_tel_data_add_array_string(d, callbacks[i].cmd);
142 : : rte_spinlock_unlock(&callback_sl);
143 : 287 : return 0;
144 : : }
145 : :
146 : : static int
147 : 3 : json_info(const char *cmd __rte_unused, const char *params __rte_unused,
148 : : struct rte_tel_data *d)
149 : : {
150 : 3 : rte_tel_data_start_dict(d);
151 : 3 : rte_tel_data_add_dict_string(d, "version", telemetry_version);
152 : 3 : rte_tel_data_add_dict_int(d, "pid", getpid());
153 : 3 : rte_tel_data_add_dict_int(d, "max_output_len", MAX_OUTPUT_LEN);
154 : 3 : return 0;
155 : : }
156 : :
157 : : static int
158 : 3 : command_help(const char *cmd __rte_unused, const char *params,
159 : : struct rte_tel_data *d)
160 : : {
161 : : int i;
162 : : /* if no parameters return our own help text */
163 [ + + ]: 3 : const char *to_lookup = (params == NULL ? cmd : params);
164 : :
165 : 3 : rte_tel_data_start_dict(d);
166 : : rte_spinlock_lock(&callback_sl);
167 [ + + ]: 270 : for (i = 0; i < num_callbacks; i++)
168 [ + + ]: 268 : if (strcmp(to_lookup, callbacks[i].cmd) == 0) {
169 [ + - ]: 1 : if (params == NULL)
170 : 1 : rte_tel_data_string(d, callbacks[i].help);
171 : : else
172 : 0 : rte_tel_data_add_dict_string(d, params, callbacks[i].help);
173 : : break;
174 : : }
175 : : rte_spinlock_unlock(&callback_sl);
176 [ + + ]: 3 : if (i == num_callbacks)
177 : 2 : return -1;
178 : : return 0;
179 : : }
180 : :
181 : : static int
182 : 48 : container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
183 : : {
184 : : size_t used = 0;
185 : : unsigned int i;
186 : :
187 [ + + + + ]: 48 : if (d->type != TEL_DICT && d->type != TEL_ARRAY_UINT &&
188 [ - + ]: 19 : d->type != TEL_ARRAY_INT && d->type != TEL_ARRAY_STRING)
189 : 0 : return snprintf(out_buf, buf_len, "null");
190 : :
191 [ + + ]: 48 : if (d->type == TEL_DICT)
192 : 5 : used = rte_tel_json_empty_obj(out_buf, buf_len, 0);
193 : : else
194 : 43 : used = rte_tel_json_empty_array(out_buf, buf_len, 0);
195 : :
196 [ + + ]: 48 : if (d->type == TEL_ARRAY_UINT)
197 [ + + ]: 65 : for (i = 0; i < d->data_len; i++)
198 : 48 : used = rte_tel_json_add_array_uint(out_buf,
199 : : buf_len, used,
200 : 48 : d->data.array[i].uval);
201 [ + + ]: 48 : if (d->type == TEL_ARRAY_INT)
202 [ + + ]: 30 : for (i = 0; i < d->data_len; i++)
203 : 23 : used = rte_tel_json_add_array_int(out_buf,
204 : : buf_len, used,
205 : 23 : d->data.array[i].ival);
206 [ + + ]: 48 : if (d->type == TEL_ARRAY_STRING)
207 [ + + ]: 33 : for (i = 0; i < d->data_len; i++)
208 : 14 : used = rte_tel_json_add_array_string(out_buf,
209 : : buf_len, used,
210 : 14 : d->data.array[i].sval);
211 [ + + ]: 48 : if (d->type == TEL_DICT)
212 [ + + ]: 25 : for (i = 0; i < d->data_len; i++) {
213 : : const struct tel_dict_entry *v = &d->data.dict[i];
214 [ + + + + : 20 : switch (v->type) {
- ]
215 : 2 : case RTE_TEL_STRING_VAL:
216 : 4 : used = rte_tel_json_add_obj_str(out_buf,
217 : : buf_len, used,
218 : 2 : v->name, v->value.sval);
219 : 2 : break;
220 : 14 : case RTE_TEL_INT_VAL:
221 : 28 : used = rte_tel_json_add_obj_int(out_buf,
222 : : buf_len, used,
223 : 14 : v->name, v->value.ival);
224 : 14 : break;
225 : 2 : case RTE_TEL_UINT_VAL:
226 : 4 : used = rte_tel_json_add_obj_uint(out_buf,
227 : : buf_len, used,
228 : 2 : v->name, v->value.uval);
229 : 2 : break;
230 : 2 : case RTE_TEL_CONTAINER:
231 : : {
232 : 2 : char *temp = malloc(buf_len);
233 [ + - ]: 2 : if (temp == NULL)
234 : : break;
235 : 2 : *temp = '\0'; /* ensure valid string */
236 : :
237 : : const struct container *cont =
238 : : &v->value.container;
239 [ + - ]: 2 : if (container_to_json(cont->data,
240 : : temp, buf_len) != 0)
241 : 2 : used = rte_tel_json_add_obj_json(
242 : : out_buf,
243 : : buf_len, used,
244 : 2 : v->name, temp);
245 [ + - ]: 2 : if (!cont->keep)
246 : 2 : rte_tel_data_free(cont->data);
247 : 2 : free(temp);
248 : 2 : break;
249 : : }
250 : : }
251 : : }
252 : :
253 : 48 : return used;
254 : : }
255 : :
256 : : static void
257 [ + + + + : 412 : output_json(const char *cmd, const struct rte_tel_data *d, int s)
- ]
258 : : {
259 : : char out_buf[MAX_OUTPUT_LEN];
260 : :
261 : : char *cb_data_buf;
262 : : size_t buf_len, prefix_used, used = 0;
263 : : unsigned int i;
264 : :
265 : : RTE_BUILD_BUG_ON(sizeof(out_buf) < MAX_CMD_LEN +
266 : : RTE_TEL_MAX_SINGLE_STRING_LEN + 10);
267 : :
268 : 412 : prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":",
269 : : MAX_CMD_LEN, cmd);
270 : 412 : cb_data_buf = &out_buf[prefix_used];
271 : 412 : buf_len = sizeof(out_buf) - prefix_used - 1; /* space for '}' */
272 : :
273 [ + + + + : 412 : switch (d->type) {
- ]
274 : : case TEL_NULL:
275 : : used = strlcpy(cb_data_buf, "null", buf_len);
276 : 2 : break;
277 : :
278 : 6 : case TEL_STRING:
279 : 6 : used = rte_tel_json_str(cb_data_buf, buf_len, 0, d->data.str);
280 : 6 : break;
281 : :
282 : 50 : case TEL_DICT:
283 : 50 : used = rte_tel_json_empty_obj(cb_data_buf, buf_len, 0);
284 [ + + ]: 241 : for (i = 0; i < d->data_len; i++) {
285 : : const struct tel_dict_entry *v = &d->data.dict[i];
286 [ + + + + : 191 : switch (v->type) {
- ]
287 : 47 : case RTE_TEL_STRING_VAL:
288 : 94 : used = rte_tel_json_add_obj_str(cb_data_buf,
289 : : buf_len, used,
290 : 47 : v->name, v->value.sval);
291 : 47 : break;
292 : 50 : case RTE_TEL_INT_VAL:
293 : 100 : used = rte_tel_json_add_obj_int(cb_data_buf,
294 : : buf_len, used,
295 : 50 : v->name, v->value.ival);
296 : 50 : break;
297 : 60 : case RTE_TEL_UINT_VAL:
298 : 120 : used = rte_tel_json_add_obj_uint(cb_data_buf,
299 : : buf_len, used,
300 : 60 : v->name, v->value.uval);
301 : 60 : break;
302 : 34 : case RTE_TEL_CONTAINER:
303 : : {
304 : 34 : char *temp = malloc(buf_len);
305 [ + - ]: 34 : if (temp == NULL)
306 : : break;
307 : 34 : *temp = '\0'; /* ensure valid string */
308 : :
309 : : const struct container *cont =
310 : : &v->value.container;
311 [ + - ]: 34 : if (container_to_json(cont->data,
312 : : temp, buf_len) != 0)
313 : 34 : used = rte_tel_json_add_obj_json(
314 : : cb_data_buf,
315 : : buf_len, used,
316 : 34 : v->name, temp);
317 [ + - ]: 34 : if (!cont->keep)
318 : 34 : rte_tel_data_free(cont->data);
319 : 34 : free(temp);
320 : : }
321 : : }
322 : : }
323 : : break;
324 : :
325 : 354 : case TEL_ARRAY_STRING:
326 : : case TEL_ARRAY_INT:
327 : : case TEL_ARRAY_UINT:
328 : : case TEL_ARRAY_CONTAINER:
329 : 354 : used = rte_tel_json_empty_array(cb_data_buf, buf_len, 0);
330 [ + + ]: 27963 : for (i = 0; i < d->data_len; i++)
331 [ + + ]: 27609 : if (d->type == TEL_ARRAY_STRING)
332 : 27027 : used = rte_tel_json_add_array_string(
333 : : cb_data_buf,
334 : : buf_len, used,
335 : 27027 : d->data.array[i].sval);
336 [ + + ]: 582 : else if (d->type == TEL_ARRAY_INT)
337 : 565 : used = rte_tel_json_add_array_int(cb_data_buf,
338 : : buf_len, used,
339 : 565 : d->data.array[i].ival);
340 [ + + ]: 17 : else if (d->type == TEL_ARRAY_UINT)
341 : 5 : used = rte_tel_json_add_array_uint(cb_data_buf,
342 : : buf_len, used,
343 : 5 : d->data.array[i].uval);
344 [ + - ]: 12 : else if (d->type == TEL_ARRAY_CONTAINER) {
345 : 12 : char *temp = malloc(buf_len);
346 [ + - ]: 12 : if (temp == NULL)
347 : : break;
348 : 12 : *temp = '\0'; /* ensure valid string */
349 : :
350 : : const struct container *rec_data =
351 : : &d->data.array[i].container;
352 [ + - ]: 12 : if (container_to_json(rec_data->data,
353 : : temp, buf_len) != 0)
354 : 12 : used = rte_tel_json_add_array_json(
355 : : cb_data_buf,
356 : : buf_len, used, temp);
357 [ + - ]: 12 : if (!rec_data->keep)
358 : 12 : rte_tel_data_free(rec_data->data);
359 : 12 : free(temp);
360 : : }
361 : : break;
362 : : }
363 : 412 : used += prefix_used;
364 : 412 : used += strlcat(out_buf + used, "}", sizeof(out_buf) - used);
365 [ - + ]: 412 : if (write(s, out_buf, used) < 0)
366 : 0 : TMTY_LOG_LINE(ERR, "Error writing to socket: %s", strerror(errno));
367 : 412 : }
368 : :
369 : : static void
370 : 593 : perform_command(const struct cmd_callback *cb, const char *cmd, const char *param, int s)
371 : : {
372 : 593 : struct rte_tel_data data = {0};
373 : : int ret;
374 : :
375 [ + + ]: 593 : if (cb->fn_arg != NULL)
376 : 54 : ret = cb->fn_arg(cmd, param, cb->arg, &data);
377 : : else
378 : 539 : ret = cb->fn(cmd, param, &data);
379 : :
380 [ + + ]: 593 : if (ret < 0) {
381 : : char out_buf[MAX_CMD_LEN + 10];
382 [ - + ]: 181 : int used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":null}",
383 : : MAX_CMD_LEN, cmd ? cmd : "none");
384 [ - + ]: 181 : if (write(s, out_buf, used) < 0)
385 : 0 : TMTY_LOG_LINE(ERR, "Error writing to socket: %s", strerror(errno));
386 : : return;
387 : : }
388 : 412 : output_json(cmd, &data, s);
389 : : }
390 : :
391 : : static int
392 : 0 : unknown_command(const char *cmd __rte_unused, const char *params __rte_unused,
393 : : struct rte_tel_data *d)
394 : : {
395 : 0 : return d->type = TEL_NULL;
396 : : }
397 : :
398 : : static void *
399 : 287 : client_handler(void *sock_id)
400 : : {
401 : 287 : int s = (int)(uintptr_t)sock_id;
402 : : char buffer[1024];
403 : : char info_str[1024];
404 : 287 : snprintf(info_str, sizeof(info_str),
405 : : "{\"version\":\"%s\",\"pid\":%d,\"max_output_len\":%d}",
406 : : telemetry_version, getpid(), MAX_OUTPUT_LEN);
407 [ - + ]: 287 : if (write(s, info_str, strlen(info_str)) < 0) {
408 : 0 : TMTY_LOG_LINE(DEBUG, "Socket write base info to client failed");
409 : 0 : goto exit;
410 : : }
411 : :
412 : : /* receive data is not null terminated */
413 : 287 : int bytes = read(s, buffer, sizeof(buffer) - 1);
414 [ + + ]: 880 : while (bytes > 0) {
415 : 593 : buffer[bytes] = 0;
416 : 593 : char *saveptr = NULL;
417 : 593 : const char *cmd = strtok_r(buffer, ",", &saveptr);
418 : 593 : const char *param = strtok_r(NULL, "\0", &saveptr);
419 : 593 : struct cmd_callback cb = {.fn = unknown_command};
420 : : int i;
421 : :
422 [ + - + - ]: 593 : if (cmd && strlen(cmd) < MAX_CMD_LEN) {
423 : : rte_spinlock_lock(&callback_sl);
424 [ + - ]: 16244 : for (i = 0; i < num_callbacks; i++)
425 [ + + ]: 16244 : if (strcmp(cmd, callbacks[i].cmd) == 0) {
426 : 593 : cb = callbacks[i];
427 : 593 : break;
428 : : }
429 : : rte_spinlock_unlock(&callback_sl);
430 : : }
431 : 593 : perform_command(&cb, cmd, param, s);
432 : :
433 : 593 : bytes = read(s, buffer, sizeof(buffer) - 1);
434 : : }
435 : 287 : exit:
436 : 287 : close(s);
437 : 287 : rte_atomic_fetch_sub_explicit(&v2_clients, 1, rte_memory_order_relaxed);
438 : 287 : return NULL;
439 : : }
440 : :
441 : : static void *
442 : 371 : socket_listener(void *socket)
443 : : {
444 : : while (1) {
445 : : pthread_t th;
446 : : int rc;
447 : : struct socket *s = (struct socket *)socket;
448 : 661 : int s_accepted = accept(s->sock, NULL, NULL);
449 [ - + ]: 290 : if (s_accepted < 0) {
450 : 0 : TMTY_LOG_LINE(ERR, "Error with accept, telemetry thread quitting");
451 : 0 : return NULL;
452 : : }
453 [ + + ]: 290 : if (s->num_clients != NULL) {
454 : 287 : uint16_t conns = rte_atomic_load_explicit(s->num_clients,
455 : : rte_memory_order_relaxed);
456 [ - + ]: 287 : if (conns >= MAX_CONNECTIONS) {
457 : 0 : close(s_accepted);
458 : 0 : continue;
459 : : }
460 : 287 : rte_atomic_fetch_add_explicit(s->num_clients, 1,
461 : : rte_memory_order_relaxed);
462 : : }
463 : 290 : rc = pthread_create(&th, NULL, s->fn,
464 : 290 : (void *)(uintptr_t)s_accepted);
465 [ - + ]: 290 : if (rc != 0) {
466 : 0 : TMTY_LOG_LINE(ERR, "Error with create client thread: %s",
467 : : strerror(rc));
468 : 0 : close(s_accepted);
469 [ # # ]: 0 : if (s->num_clients != NULL)
470 : 0 : rte_atomic_fetch_sub_explicit(s->num_clients, 1,
471 : : rte_memory_order_relaxed);
472 : 0 : continue;
473 : : }
474 : 290 : pthread_detach(th);
475 : : }
476 : : return NULL;
477 : : }
478 : :
479 : : static inline char *
480 : : get_socket_path(const char *runtime_dir, const int version)
481 : : {
482 : : static char path[PATH_MAX];
483 : 187 : snprintf(path, sizeof(path), "%s/dpdk_telemetry.v%d",
484 : 187 : strlen(runtime_dir) ? runtime_dir : "/tmp", version);
485 : : return path;
486 : : }
487 : :
488 : : static void
489 : 187 : unlink_sockets(void)
490 : : {
491 [ + - ]: 187 : if (v2_socket.path[0])
492 : 187 : unlink(v2_socket.path);
493 [ + + ]: 187 : if (v1_socket.path[0])
494 : 184 : unlink(v1_socket.path);
495 : 187 : }
496 : :
497 : : static int
498 : 377 : create_socket(char *path)
499 : : {
500 : 377 : int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
501 [ - + ]: 377 : if (sock < 0) {
502 : 0 : TMTY_LOG_LINE(ERR, "Error with socket creation, %s", strerror(errno));
503 : 0 : return -1;
504 : : }
505 : :
506 : 377 : struct sockaddr_un sun = {.sun_family = AF_UNIX};
507 : : strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
508 : 377 : TMTY_LOG_LINE(DEBUG, "Attempting socket bind to path '%s'", path);
509 : :
510 [ + + ]: 377 : if (bind(sock, (void *) &sun, sizeof(sun)) < 0) {
511 : : struct stat st;
512 : :
513 : 6 : TMTY_LOG_LINE(DEBUG, "Initial bind to socket '%s' failed.", path);
514 : :
515 : : /* first check if we have a runtime dir */
516 [ + - - + ]: 6 : if (stat(socket_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
517 : 0 : TMTY_LOG_LINE(ERR, "Cannot access DPDK runtime directory: %s", socket_dir);
518 : 0 : close(sock);
519 : 6 : return -ENOENT;
520 : : }
521 : :
522 : : /* check if current socket is active */
523 [ + - ]: 6 : if (connect(sock, (void *)&sun, sizeof(sun)) == 0) {
524 : 6 : close(sock);
525 : 6 : return -EADDRINUSE;
526 : : }
527 : :
528 : : /* socket is not active, delete and attempt rebind */
529 : 0 : TMTY_LOG_LINE(DEBUG, "Attempting unlink and retrying bind");
530 : 0 : unlink(sun.sun_path);
531 [ # # ]: 0 : if (bind(sock, (void *) &sun, sizeof(sun)) < 0) {
532 : 0 : TMTY_LOG_LINE(ERR, "Error binding socket: %s", strerror(errno));
533 : 0 : close(sock);
534 : 0 : return -errno; /* if unlink failed, this will be -EADDRINUSE as above */
535 : : }
536 : : }
537 : :
538 [ - + ]: 371 : if (listen(sock, 1) < 0) {
539 : 0 : TMTY_LOG_LINE(ERR, "Error calling listen for socket: %s", strerror(errno));
540 : 0 : unlink(sun.sun_path);
541 : 0 : close(sock);
542 : 0 : return -errno;
543 : : }
544 : 371 : TMTY_LOG_LINE(DEBUG, "Socket creation and binding ok");
545 : :
546 : 371 : return sock;
547 : : }
548 : :
549 : : static void
550 : : set_thread_name(pthread_t id __rte_unused, const char *name __rte_unused)
551 : : {
552 : : #if defined RTE_EXEC_ENV_LINUX && defined __GLIBC__ && defined __GLIBC_PREREQ
553 : : #if __GLIBC_PREREQ(2, 12)
554 : 371 : pthread_setname_np(id, name);
555 : : #endif
556 : : #elif defined RTE_EXEC_ENV_FREEBSD
557 : : pthread_set_name_np(id, name);
558 : : #endif
559 : : }
560 : :
561 : : static int
562 : 187 : telemetry_legacy_init(void)
563 : : {
564 : : pthread_t t_old;
565 : : int rc;
566 : :
567 [ - + ]: 187 : if (num_legacy_callbacks == 1) {
568 : 0 : TMTY_LOG_LINE(DEBUG, "No legacy callbacks, legacy socket not created");
569 : 0 : return -1;
570 : : }
571 : :
572 : 187 : v1_socket.fn = legacy_client_handler;
573 [ - + ]: 187 : if ((size_t) snprintf(v1_socket.path, sizeof(v1_socket.path),
574 : : "%s/telemetry", socket_dir) >= sizeof(v1_socket.path)) {
575 : 0 : TMTY_LOG_LINE(ERR, "Error with socket binding, path too long");
576 : 0 : return -1;
577 : : }
578 : 187 : v1_socket.sock = create_socket(v1_socket.path);
579 [ + + ]: 187 : if (v1_socket.sock < 0) {
580 : 3 : v1_socket.path[0] = '\0';
581 : 3 : return -1;
582 : : }
583 : 184 : rc = pthread_create(&t_old, NULL, socket_listener, &v1_socket);
584 [ - + ]: 184 : if (rc != 0) {
585 : 0 : TMTY_LOG_LINE(ERR, "Error with create legacy socket thread: %s",
586 : : strerror(rc));
587 : 0 : close(v1_socket.sock);
588 : 0 : v1_socket.sock = -1;
589 : 0 : unlink(v1_socket.path);
590 : 0 : v1_socket.path[0] = '\0';
591 : 0 : return -1;
592 : : }
593 : 184 : pthread_setaffinity_np(t_old, sizeof(*thread_cpuset), thread_cpuset);
594 : 184 : set_thread_name(t_old, "dpdk-telemet-v1");
595 : 184 : TMTY_LOG_LINE(DEBUG, "Legacy telemetry socket initialized ok");
596 : 184 : pthread_detach(t_old);
597 : 184 : return 0;
598 : : }
599 : :
600 : : static int
601 : 187 : telemetry_v2_init(void)
602 : : {
603 : : char spath[sizeof(v2_socket.path)];
604 : : pthread_t t_new;
605 : : short suffix = 0;
606 : : int rc;
607 : :
608 : 187 : v2_socket.num_clients = &v2_clients;
609 : 187 : rte_telemetry_register_cmd("/", list_commands,
610 : : "Returns list of available commands, Takes no parameters");
611 : 187 : rte_telemetry_register_cmd("/info", json_info,
612 : : "Returns DPDK Telemetry information. Takes no parameters");
613 : 187 : rte_telemetry_register_cmd("/help", command_help,
614 : : "Returns help text for a command. Parameters: string command");
615 : 187 : v2_socket.fn = client_handler;
616 [ - + - + ]: 187 : if (strlcpy(spath, get_socket_path(socket_dir, 2), sizeof(spath)) >= sizeof(spath)) {
617 : 0 : TMTY_LOG_LINE(ERR, "Error with socket binding, path too long");
618 : 0 : return -1;
619 : : }
620 : : memcpy(v2_socket.path, spath, sizeof(v2_socket.path));
621 : :
622 : 187 : v2_socket.sock = create_socket(v2_socket.path);
623 [ + + ]: 190 : while (v2_socket.sock < 0) {
624 : : /* bail out on unexpected error, or suffix wrap-around */
625 [ + - - + ]: 3 : if (v2_socket.sock != -EADDRINUSE || suffix < 0) {
626 : 0 : v2_socket.path[0] = '\0'; /* clear socket path */
627 : 0 : return -1;
628 : : }
629 : : /* add a suffix to the path if the basic version fails */
630 : 3 : if (snprintf(v2_socket.path, sizeof(v2_socket.path), "%s:%d",
631 [ - + ]: 3 : spath, ++suffix) >= (int)sizeof(v2_socket.path)) {
632 : 0 : TMTY_LOG_LINE(ERR, "Error with socket binding, path too long");
633 : 0 : return -1;
634 : : }
635 : 3 : v2_socket.sock = create_socket(v2_socket.path);
636 : : }
637 : 187 : rc = pthread_create(&t_new, NULL, socket_listener, &v2_socket);
638 [ - + ]: 187 : if (rc != 0) {
639 : 0 : TMTY_LOG_LINE(ERR, "Error with create socket thread: %s",
640 : : strerror(rc));
641 : 0 : close(v2_socket.sock);
642 : 0 : v2_socket.sock = -1;
643 : 0 : unlink(v2_socket.path);
644 : 0 : v2_socket.path[0] = '\0';
645 : 0 : return -1;
646 : : }
647 : 187 : pthread_setaffinity_np(t_new, sizeof(*thread_cpuset), thread_cpuset);
648 : 187 : set_thread_name(t_new, "dpdk-telemet-v2");
649 : 187 : pthread_detach(t_new);
650 : 187 : atexit(unlink_sockets);
651 : :
652 : 187 : return 0;
653 : : }
654 : :
655 : : #endif /* !RTE_EXEC_ENV_WINDOWS */
656 : :
657 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_telemetry_init)
658 : : int32_t
659 : 187 : rte_telemetry_init(const char *runtime_dir, const char *rte_version, rte_cpuset_t *cpuset)
660 : : {
661 : 187 : telemetry_version = rte_version;
662 : 187 : socket_dir = runtime_dir;
663 : 187 : thread_cpuset = cpuset;
664 : :
665 : : #ifndef RTE_EXEC_ENV_WINDOWS
666 [ + - ]: 187 : if (telemetry_v2_init() != 0)
667 : : return -1;
668 : 187 : TMTY_LOG_LINE(DEBUG, "Telemetry initialized ok");
669 : 187 : telemetry_legacy_init();
670 : : #endif /* RTE_EXEC_ENV_WINDOWS */
671 : :
672 : 187 : return 0;
673 : : }
|