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