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