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