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