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