1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2020 Intel Corporation 3 */ 4 5 #include <stdio.h> 6 #include <stdint.h> 7 #include <stdlib.h> 8 #include <string.h> 9 10 #include <rte_common.h> 11 #include <rte_ethdev.h> 12 #include <rte_swx_port_ethdev.h> 13 #include <rte_swx_port_ring.h> 14 #include <rte_swx_port_source_sink.h> 15 #include <rte_swx_pipeline.h> 16 #include <rte_swx_ctl.h> 17 18 #include "cli.h" 19 20 #include "obj.h" 21 #include "thread.h" 22 23 #ifndef CMD_MAX_TOKENS 24 #define CMD_MAX_TOKENS 256 25 #endif 26 27 #define MSG_OUT_OF_MEMORY "Not enough memory.\n" 28 #define MSG_CMD_UNKNOWN "Unknown command \"%s\".\n" 29 #define MSG_CMD_UNIMPLEM "Command \"%s\" not implemented.\n" 30 #define MSG_ARG_NOT_ENOUGH "Not enough arguments for command \"%s\".\n" 31 #define MSG_ARG_TOO_MANY "Too many arguments for command \"%s\".\n" 32 #define MSG_ARG_MISMATCH "Wrong number of arguments for command \"%s\".\n" 33 #define MSG_ARG_NOT_FOUND "Argument \"%s\" not found.\n" 34 #define MSG_ARG_INVALID "Invalid value for argument \"%s\".\n" 35 #define MSG_FILE_ERR "Error in file \"%s\" at line %u.\n" 36 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n" 37 #define MSG_CMD_FAIL "Command \"%s\" failed.\n" 38 39 #define skip_white_spaces(pos) \ 40 ({ \ 41 __typeof__(pos) _p = (pos); \ 42 for ( ; isspace(*_p); _p++) \ 43 ; \ 44 _p; \ 45 }) 46 47 static int 48 parser_read_uint64(uint64_t *value, const char *p) 49 { 50 char *next; 51 uint64_t val; 52 53 p = skip_white_spaces(p); 54 if (!isdigit(*p)) 55 return -EINVAL; 56 57 val = strtoul(p, &next, 10); 58 if (p == next) 59 return -EINVAL; 60 61 p = next; 62 switch (*p) { 63 case 'T': 64 val *= 1024ULL; 65 /* fall through */ 66 case 'G': 67 val *= 1024ULL; 68 /* fall through */ 69 case 'M': 70 val *= 1024ULL; 71 /* fall through */ 72 case 'k': 73 case 'K': 74 val *= 1024ULL; 75 p++; 76 break; 77 } 78 79 p = skip_white_spaces(p); 80 if (*p != '\0') 81 return -EINVAL; 82 83 *value = val; 84 return 0; 85 } 86 87 static int 88 parser_read_uint32(uint32_t *value, const char *p) 89 { 90 uint64_t val = 0; 91 int ret = parser_read_uint64(&val, p); 92 93 if (ret < 0) 94 return ret; 95 96 if (val > UINT32_MAX) 97 return -ERANGE; 98 99 *value = val; 100 return 0; 101 } 102 103 static int 104 parser_read_uint16(uint16_t *value, const char *p) 105 { 106 uint64_t val = 0; 107 int ret = parser_read_uint64(&val, p); 108 109 if (ret < 0) 110 return ret; 111 112 if (val > UINT16_MAX) 113 return -ERANGE; 114 115 *value = val; 116 return 0; 117 } 118 119 #define PARSE_DELIMITER " \f\n\r\t\v" 120 121 static int 122 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens) 123 { 124 uint32_t i; 125 126 if ((string == NULL) || 127 (tokens == NULL) || 128 (*n_tokens < 1)) 129 return -EINVAL; 130 131 for (i = 0; i < *n_tokens; i++) { 132 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string); 133 if (tokens[i] == NULL) 134 break; 135 } 136 137 if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string)) 138 return -E2BIG; 139 140 *n_tokens = i; 141 return 0; 142 } 143 144 static int 145 is_comment(char *in) 146 { 147 if ((strlen(in) && index("!#%;", in[0])) || 148 (strncmp(in, "//", 2) == 0) || 149 (strncmp(in, "--", 2) == 0)) 150 return 1; 151 152 return 0; 153 } 154 155 static const char cmd_mempool_help[] = 156 "mempool <mempool_name>\n" 157 " buffer <buffer_size>\n" 158 " pool <pool_size>\n" 159 " cache <cache_size>\n" 160 " cpu <cpu_id>\n"; 161 162 static void 163 cmd_mempool(char **tokens, 164 uint32_t n_tokens, 165 char *out, 166 size_t out_size, 167 void *obj) 168 { 169 struct mempool_params p; 170 char *name; 171 struct mempool *mempool; 172 173 if (n_tokens != 10) { 174 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 175 return; 176 } 177 178 name = tokens[1]; 179 180 if (strcmp(tokens[2], "buffer") != 0) { 181 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer"); 182 return; 183 } 184 185 if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) { 186 snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size"); 187 return; 188 } 189 190 if (strcmp(tokens[4], "pool") != 0) { 191 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool"); 192 return; 193 } 194 195 if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) { 196 snprintf(out, out_size, MSG_ARG_INVALID, "pool_size"); 197 return; 198 } 199 200 if (strcmp(tokens[6], "cache") != 0) { 201 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache"); 202 return; 203 } 204 205 if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) { 206 snprintf(out, out_size, MSG_ARG_INVALID, "cache_size"); 207 return; 208 } 209 210 if (strcmp(tokens[8], "cpu") != 0) { 211 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu"); 212 return; 213 } 214 215 if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) { 216 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id"); 217 return; 218 } 219 220 mempool = mempool_create(obj, name, &p); 221 if (mempool == NULL) { 222 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); 223 return; 224 } 225 } 226 227 static const char cmd_link_help[] = 228 "link <link_name>\n" 229 " dev <device_name> | port <port_id>\n" 230 " rxq <n_queues> <queue_size> <mempool_name>\n" 231 " txq <n_queues> <queue_size>\n" 232 " promiscuous on | off\n" 233 " [rss <qid_0> ... <qid_n>]\n"; 234 235 static void 236 cmd_link(char **tokens, 237 uint32_t n_tokens, 238 char *out, 239 size_t out_size, 240 void *obj) 241 { 242 struct link_params p; 243 struct link_params_rss rss; 244 struct link *link; 245 char *name; 246 247 memset(&p, 0, sizeof(p)); 248 249 if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) { 250 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 251 return; 252 } 253 name = tokens[1]; 254 255 if (strcmp(tokens[2], "dev") == 0) 256 p.dev_name = tokens[3]; 257 else if (strcmp(tokens[2], "port") == 0) { 258 p.dev_name = NULL; 259 260 if (parser_read_uint16(&p.port_id, tokens[3]) != 0) { 261 snprintf(out, out_size, MSG_ARG_INVALID, "port_id"); 262 return; 263 } 264 } else { 265 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port"); 266 return; 267 } 268 269 if (strcmp(tokens[4], "rxq") != 0) { 270 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq"); 271 return; 272 } 273 274 if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) { 275 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues"); 276 return; 277 } 278 if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) { 279 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size"); 280 return; 281 } 282 283 p.rx.mempool_name = tokens[7]; 284 285 if (strcmp(tokens[8], "txq") != 0) { 286 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq"); 287 return; 288 } 289 290 if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) { 291 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues"); 292 return; 293 } 294 295 if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) { 296 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size"); 297 return; 298 } 299 300 if (strcmp(tokens[11], "promiscuous") != 0) { 301 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous"); 302 return; 303 } 304 305 if (strcmp(tokens[12], "on") == 0) 306 p.promiscuous = 1; 307 else if (strcmp(tokens[12], "off") == 0) 308 p.promiscuous = 0; 309 else { 310 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off"); 311 return; 312 } 313 314 /* RSS */ 315 p.rx.rss = NULL; 316 if (n_tokens > 13) { 317 uint32_t queue_id, i; 318 319 if (strcmp(tokens[13], "rss") != 0) { 320 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss"); 321 return; 322 } 323 324 p.rx.rss = &rss; 325 326 rss.n_queues = 0; 327 for (i = 14; i < n_tokens; i++) { 328 if (parser_read_uint32(&queue_id, tokens[i]) != 0) { 329 snprintf(out, out_size, MSG_ARG_INVALID, 330 "queue_id"); 331 return; 332 } 333 334 rss.queue_id[rss.n_queues] = queue_id; 335 rss.n_queues++; 336 } 337 } 338 339 link = link_create(obj, name, &p); 340 if (link == NULL) { 341 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); 342 return; 343 } 344 } 345 346 /* Print the link stats and info */ 347 static void 348 print_link_info(struct link *link, char *out, size_t out_size) 349 { 350 struct rte_eth_stats stats; 351 struct rte_ether_addr mac_addr; 352 struct rte_eth_link eth_link; 353 uint16_t mtu; 354 int ret; 355 356 memset(&stats, 0, sizeof(stats)); 357 rte_eth_stats_get(link->port_id, &stats); 358 359 ret = rte_eth_macaddr_get(link->port_id, &mac_addr); 360 if (ret != 0) { 361 snprintf(out, out_size, "\n%s: MAC address get failed: %s", 362 link->name, rte_strerror(-ret)); 363 return; 364 } 365 366 ret = rte_eth_link_get(link->port_id, ð_link); 367 if (ret < 0) { 368 snprintf(out, out_size, "\n%s: link get failed: %s", 369 link->name, rte_strerror(-ret)); 370 return; 371 } 372 373 rte_eth_dev_get_mtu(link->port_id, &mtu); 374 375 snprintf(out, out_size, 376 "\n" 377 "%s: flags=<%s> mtu %u\n" 378 "\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n" 379 "\tport# %u speed %s\n" 380 "\tRX packets %" PRIu64" bytes %" PRIu64"\n" 381 "\tRX errors %" PRIu64" missed %" PRIu64" no-mbuf %" PRIu64"\n" 382 "\tTX packets %" PRIu64" bytes %" PRIu64"\n" 383 "\tTX errors %" PRIu64"\n", 384 link->name, 385 eth_link.link_status == 0 ? "DOWN" : "UP", 386 mtu, 387 mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], 388 mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], 389 mac_addr.addr_bytes[4], mac_addr.addr_bytes[5], 390 link->n_rxq, 391 link->n_txq, 392 link->port_id, 393 rte_eth_link_speed_to_str(eth_link.link_speed), 394 stats.ipackets, 395 stats.ibytes, 396 stats.ierrors, 397 stats.imissed, 398 stats.rx_nombuf, 399 stats.opackets, 400 stats.obytes, 401 stats.oerrors); 402 } 403 404 /* 405 * link show [<link_name>] 406 */ 407 static void 408 cmd_link_show(char **tokens, 409 uint32_t n_tokens, 410 char *out, 411 size_t out_size, 412 void *obj) 413 { 414 struct link *link; 415 char *link_name; 416 417 if (n_tokens != 2 && n_tokens != 3) { 418 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 419 return; 420 } 421 422 if (n_tokens == 2) { 423 link = link_next(obj, NULL); 424 425 while (link != NULL) { 426 out_size = out_size - strlen(out); 427 out = &out[strlen(out)]; 428 429 print_link_info(link, out, out_size); 430 link = link_next(obj, link); 431 } 432 } else { 433 out_size = out_size - strlen(out); 434 out = &out[strlen(out)]; 435 436 link_name = tokens[2]; 437 link = link_find(obj, link_name); 438 439 if (link == NULL) { 440 snprintf(out, out_size, MSG_ARG_INVALID, 441 "Link does not exist"); 442 return; 443 } 444 print_link_info(link, out, out_size); 445 } 446 } 447 448 static const char cmd_ring_help[] = 449 "ring <ring_name> size <size> numa <numa_node>\n"; 450 451 static void 452 cmd_ring(char **tokens, 453 uint32_t n_tokens, 454 char *out, 455 size_t out_size, 456 void *obj) 457 { 458 struct ring_params p; 459 char *name; 460 struct ring *ring; 461 462 if (n_tokens != 6) { 463 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 464 return; 465 } 466 467 name = tokens[1]; 468 469 if (strcmp(tokens[2], "size") != 0) { 470 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size"); 471 return; 472 } 473 474 if (parser_read_uint32(&p.size, tokens[3]) != 0) { 475 snprintf(out, out_size, MSG_ARG_INVALID, "size"); 476 return; 477 } 478 479 if (strcmp(tokens[4], "numa") != 0) { 480 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa"); 481 return; 482 } 483 484 if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) { 485 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node"); 486 return; 487 } 488 489 ring = ring_create(obj, name, &p); 490 if (!ring) { 491 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); 492 return; 493 } 494 } 495 496 static const char cmd_pipeline_create_help[] = 497 "pipeline <pipeline_name> create <numa_node>\n"; 498 499 static void 500 cmd_pipeline_create(char **tokens, 501 uint32_t n_tokens, 502 char *out, 503 size_t out_size, 504 void *obj) 505 { 506 struct pipeline *p; 507 char *name; 508 uint32_t numa_node; 509 510 if (n_tokens != 4) { 511 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 512 return; 513 } 514 515 name = tokens[1]; 516 517 if (parser_read_uint32(&numa_node, tokens[3]) != 0) { 518 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node"); 519 return; 520 } 521 522 p = pipeline_create(obj, name, (int)numa_node); 523 if (!p) { 524 snprintf(out, out_size, "pipeline create error."); 525 return; 526 } 527 } 528 529 static const char cmd_pipeline_port_in_help[] = 530 "pipeline <pipeline_name> port in <port_id>\n" 531 " link <link_name> rxq <queue_id> bsz <burst_size>\n" 532 " ring <ring_name> bsz <burst_size>\n" 533 " | source <mempool_name> <file_name>\n"; 534 535 static void 536 cmd_pipeline_port_in(char **tokens, 537 uint32_t n_tokens, 538 char *out, 539 size_t out_size, 540 void *obj) 541 { 542 struct pipeline *p; 543 int status; 544 uint32_t port_id = 0, t0; 545 546 if (n_tokens < 6) { 547 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 548 return; 549 } 550 551 p = pipeline_find(obj, tokens[1]); 552 if (!p || p->ctl) { 553 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); 554 return; 555 } 556 557 if (strcmp(tokens[2], "port") != 0) { 558 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port"); 559 return; 560 } 561 562 if (strcmp(tokens[3], "in") != 0) { 563 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in"); 564 return; 565 } 566 567 if (parser_read_uint32(&port_id, tokens[4]) != 0) { 568 snprintf(out, out_size, MSG_ARG_INVALID, "port_id"); 569 return; 570 } 571 572 t0 = 5; 573 574 if (strcmp(tokens[t0], "link") == 0) { 575 struct rte_swx_port_ethdev_reader_params params; 576 struct link *link; 577 578 if (n_tokens < t0 + 6) { 579 snprintf(out, out_size, MSG_ARG_MISMATCH, 580 "pipeline port in link"); 581 return; 582 } 583 584 link = link_find(obj, tokens[t0 + 1]); 585 if (!link) { 586 snprintf(out, out_size, MSG_ARG_INVALID, 587 "link_name"); 588 return; 589 } 590 params.dev_name = link->dev_name; 591 592 if (strcmp(tokens[t0 + 2], "rxq") != 0) { 593 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq"); 594 return; 595 } 596 597 if (parser_read_uint16(¶ms.queue_id, tokens[t0 + 3]) != 0) { 598 snprintf(out, out_size, MSG_ARG_INVALID, 599 "queue_id"); 600 return; 601 } 602 603 if (strcmp(tokens[t0 + 4], "bsz") != 0) { 604 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz"); 605 return; 606 } 607 608 if (parser_read_uint32(¶ms.burst_size, tokens[t0 + 5])) { 609 snprintf(out, out_size, MSG_ARG_INVALID, 610 "burst_size"); 611 return; 612 } 613 614 t0 += 6; 615 616 status = rte_swx_pipeline_port_in_config(p->p, 617 port_id, 618 "ethdev", 619 ¶ms); 620 } else if (strcmp(tokens[t0], "ring") == 0) { 621 struct rte_swx_port_ring_reader_params params; 622 struct ring *ring; 623 624 if (n_tokens < t0 + 4) { 625 snprintf(out, out_size, MSG_ARG_MISMATCH, 626 "pipeline port in ring"); 627 return; 628 } 629 630 ring = ring_find(obj, tokens[t0 + 1]); 631 if (!ring) { 632 snprintf(out, out_size, MSG_ARG_INVALID, 633 "ring_name"); 634 return; 635 } 636 params.name = ring->name; 637 638 if (strcmp(tokens[t0 + 2], "bsz") != 0) { 639 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz"); 640 return; 641 } 642 643 if (parser_read_uint32(¶ms.burst_size, tokens[t0 + 3])) { 644 snprintf(out, out_size, MSG_ARG_INVALID, 645 "burst_size"); 646 return; 647 } 648 649 t0 += 4; 650 651 status = rte_swx_pipeline_port_in_config(p->p, 652 port_id, 653 "ring", 654 ¶ms); 655 } else if (strcmp(tokens[t0], "source") == 0) { 656 struct rte_swx_port_source_params params; 657 struct mempool *mp; 658 659 if (n_tokens < t0 + 3) { 660 snprintf(out, out_size, MSG_ARG_MISMATCH, 661 "pipeline port in source"); 662 return; 663 } 664 665 mp = mempool_find(obj, tokens[t0 + 1]); 666 if (!mp) { 667 snprintf(out, out_size, MSG_ARG_INVALID, 668 "mempool_name"); 669 return; 670 } 671 params.pool = mp->m; 672 673 params.file_name = tokens[t0 + 2]; 674 675 t0 += 3; 676 677 status = rte_swx_pipeline_port_in_config(p->p, 678 port_id, 679 "source", 680 ¶ms); 681 } else { 682 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); 683 return; 684 } 685 686 if (status) { 687 snprintf(out, out_size, "port in error."); 688 return; 689 } 690 691 if (n_tokens != t0) { 692 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 693 return; 694 } 695 } 696 697 static const char cmd_pipeline_port_out_help[] = 698 "pipeline <pipeline_name> port out <port_id>\n" 699 " link <link_name> txq <txq_id> bsz <burst_size>\n" 700 " ring <ring_name> bsz <burst_size>\n" 701 " | sink <file_name> | none\n"; 702 703 static void 704 cmd_pipeline_port_out(char **tokens, 705 uint32_t n_tokens, 706 char *out, 707 size_t out_size, 708 void *obj) 709 { 710 struct pipeline *p; 711 int status; 712 uint32_t port_id = 0, t0; 713 714 if (n_tokens < 6) { 715 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 716 return; 717 } 718 719 p = pipeline_find(obj, tokens[1]); 720 if (!p || p->ctl) { 721 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); 722 return; 723 } 724 725 if (strcmp(tokens[2], "port") != 0) { 726 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port"); 727 return; 728 } 729 730 if (strcmp(tokens[3], "out") != 0) { 731 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out"); 732 return; 733 } 734 735 if (parser_read_uint32(&port_id, tokens[4]) != 0) { 736 snprintf(out, out_size, MSG_ARG_INVALID, "port_id"); 737 return; 738 } 739 740 t0 = 5; 741 742 if (strcmp(tokens[t0], "link") == 0) { 743 struct rte_swx_port_ethdev_writer_params params; 744 struct link *link; 745 746 if (n_tokens < t0 + 6) { 747 snprintf(out, out_size, MSG_ARG_MISMATCH, 748 "pipeline port out link"); 749 return; 750 } 751 752 link = link_find(obj, tokens[t0 + 1]); 753 if (!link) { 754 snprintf(out, out_size, MSG_ARG_INVALID, 755 "link_name"); 756 return; 757 } 758 params.dev_name = link->dev_name; 759 760 if (strcmp(tokens[t0 + 2], "txq") != 0) { 761 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq"); 762 return; 763 } 764 765 if (parser_read_uint16(¶ms.queue_id, tokens[t0 + 3]) != 0) { 766 snprintf(out, out_size, MSG_ARG_INVALID, 767 "queue_id"); 768 return; 769 } 770 771 if (strcmp(tokens[t0 + 4], "bsz") != 0) { 772 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz"); 773 return; 774 } 775 776 if (parser_read_uint32(¶ms.burst_size, tokens[t0 + 5])) { 777 snprintf(out, out_size, MSG_ARG_INVALID, 778 "burst_size"); 779 return; 780 } 781 782 t0 += 6; 783 784 status = rte_swx_pipeline_port_out_config(p->p, 785 port_id, 786 "ethdev", 787 ¶ms); 788 } else if (strcmp(tokens[t0], "ring") == 0) { 789 struct rte_swx_port_ring_writer_params params; 790 struct ring *ring; 791 792 if (n_tokens < t0 + 4) { 793 snprintf(out, out_size, MSG_ARG_MISMATCH, 794 "pipeline port out link"); 795 return; 796 } 797 798 ring = ring_find(obj, tokens[t0 + 1]); 799 if (!ring) { 800 snprintf(out, out_size, MSG_ARG_INVALID, 801 "ring_name"); 802 return; 803 } 804 params.name = ring->name; 805 806 if (strcmp(tokens[t0 + 2], "bsz") != 0) { 807 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz"); 808 return; 809 } 810 811 if (parser_read_uint32(¶ms.burst_size, tokens[t0 + 3])) { 812 snprintf(out, out_size, MSG_ARG_INVALID, 813 "burst_size"); 814 return; 815 } 816 817 t0 += 4; 818 819 status = rte_swx_pipeline_port_out_config(p->p, 820 port_id, 821 "ring", 822 ¶ms); 823 } else if (strcmp(tokens[t0], "sink") == 0) { 824 struct rte_swx_port_sink_params params; 825 826 params.file_name = strcmp(tokens[t0 + 1], "none") ? 827 tokens[t0 + 1] : NULL; 828 829 t0 += 2; 830 831 status = rte_swx_pipeline_port_out_config(p->p, 832 port_id, 833 "sink", 834 ¶ms); 835 } else { 836 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); 837 return; 838 } 839 840 if (status) { 841 snprintf(out, out_size, "port out error."); 842 return; 843 } 844 845 if (n_tokens != t0) { 846 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 847 return; 848 } 849 } 850 851 static const char cmd_pipeline_build_help[] = 852 "pipeline <pipeline_name> build <spec_file>\n"; 853 854 static void 855 cmd_pipeline_build(char **tokens, 856 uint32_t n_tokens, 857 char *out, 858 size_t out_size, 859 void *obj) 860 { 861 struct pipeline *p = NULL; 862 FILE *spec = NULL; 863 uint32_t err_line; 864 const char *err_msg; 865 int status; 866 867 if (n_tokens != 4) { 868 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 869 return; 870 } 871 872 p = pipeline_find(obj, tokens[1]); 873 if (!p || p->ctl) { 874 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); 875 return; 876 } 877 878 spec = fopen(tokens[3], "r"); 879 if (!spec) { 880 snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]); 881 return; 882 } 883 884 status = rte_swx_pipeline_build_from_spec(p->p, 885 spec, 886 &err_line, 887 &err_msg); 888 fclose(spec); 889 if (status) { 890 snprintf(out, out_size, "Error %d at line %u: %s\n.", 891 status, err_line, err_msg); 892 return; 893 } 894 895 p->ctl = rte_swx_ctl_pipeline_create(p->p); 896 if (!p->ctl) { 897 snprintf(out, out_size, "Pipeline control create failed."); 898 rte_swx_pipeline_free(p->p); 899 return; 900 } 901 } 902 903 static void 904 table_entry_free(struct rte_swx_table_entry *entry) 905 { 906 if (!entry) 907 return; 908 909 free(entry->key); 910 free(entry->key_mask); 911 free(entry->action_data); 912 free(entry); 913 } 914 915 static const char cmd_pipeline_table_update_help[] = 916 "pipeline <pipeline_name> table <table_name> update <file_name_add> " 917 "<file_name_delete> <file_name_default>"; 918 919 static void 920 cmd_pipeline_table_update(char **tokens, 921 uint32_t n_tokens, 922 char *out, 923 size_t out_size, 924 void *obj) 925 { 926 struct pipeline *p; 927 char *pipeline_name, *table_name, *line = NULL; 928 char *file_name_add, *file_name_delete, *file_name_default; 929 FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL; 930 uint32_t line_id; 931 int status; 932 933 if (n_tokens != 8) { 934 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 935 return; 936 } 937 938 pipeline_name = tokens[1]; 939 p = pipeline_find(obj, pipeline_name); 940 if (!p || !p->ctl) { 941 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 942 return; 943 } 944 945 if (strcmp(tokens[2], "table") != 0) { 946 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); 947 return; 948 } 949 950 table_name = tokens[3]; 951 952 if (strcmp(tokens[4], "update") != 0) { 953 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update"); 954 return; 955 } 956 957 file_name_add = tokens[5]; 958 file_name_delete = tokens[6]; 959 file_name_default = tokens[7]; 960 961 /* File open. */ 962 if (strcmp(file_name_add, "none")) { 963 file_add = fopen(file_name_add, "r"); 964 if (!file_add) { 965 snprintf(out, out_size, "Cannot open file %s", 966 file_name_add); 967 goto error; 968 } 969 } 970 971 if (strcmp(file_name_delete, "none")) { 972 file_delete = fopen(file_name_delete, "r"); 973 if (!file_delete) { 974 snprintf(out, out_size, "Cannot open file %s", 975 file_name_delete); 976 goto error; 977 } 978 } 979 980 if (strcmp(file_name_default, "none")) { 981 file_default = fopen(file_name_default, "r"); 982 if (!file_default) { 983 snprintf(out, out_size, "Cannot open file %s", 984 file_name_default); 985 goto error; 986 } 987 } 988 989 if (!file_add && !file_delete && !file_default) { 990 snprintf(out, out_size, "Nothing to be done."); 991 return; 992 } 993 994 /* Buffer allocation. */ 995 line = malloc(2048); 996 if (!line) { 997 snprintf(out, out_size, MSG_OUT_OF_MEMORY); 998 goto error; 999 } 1000 1001 /* Add. */ 1002 if (file_add) 1003 for (line_id = 1; ; line_id++) { 1004 struct rte_swx_table_entry *entry; 1005 int is_blank_or_comment; 1006 1007 if (fgets(line, 2048, file_add) == NULL) 1008 break; 1009 1010 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, 1011 table_name, 1012 line, 1013 &is_blank_or_comment); 1014 if (!entry) { 1015 if (is_blank_or_comment) 1016 continue; 1017 1018 snprintf(out, out_size, MSG_FILE_ERR, 1019 file_name_add, line_id); 1020 goto error; 1021 } 1022 1023 status = rte_swx_ctl_pipeline_table_entry_add(p->ctl, 1024 table_name, 1025 entry); 1026 table_entry_free(entry); 1027 if (status) { 1028 snprintf(out, out_size, 1029 "Invalid entry in file %s at line %u", 1030 file_name_add, line_id); 1031 goto error; 1032 } 1033 } 1034 1035 1036 /* Delete. */ 1037 if (file_delete) 1038 for (line_id = 1; ; line_id++) { 1039 struct rte_swx_table_entry *entry; 1040 int is_blank_or_comment; 1041 1042 if (fgets(line, 2048, file_delete) == NULL) 1043 break; 1044 1045 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, 1046 table_name, 1047 line, 1048 &is_blank_or_comment); 1049 if (!entry) { 1050 if (is_blank_or_comment) 1051 continue; 1052 1053 snprintf(out, out_size, MSG_FILE_ERR, 1054 file_name_delete, line_id); 1055 goto error; 1056 } 1057 1058 status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl, 1059 table_name, 1060 entry); 1061 table_entry_free(entry); 1062 if (status) { 1063 snprintf(out, out_size, 1064 "Invalid entry in file %s at line %u", 1065 file_name_delete, line_id); 1066 goto error; 1067 } 1068 } 1069 1070 /* Default. */ 1071 if (file_default) 1072 for (line_id = 1; ; line_id++) { 1073 struct rte_swx_table_entry *entry; 1074 int is_blank_or_comment; 1075 1076 if (fgets(line, 2048, file_default) == NULL) 1077 break; 1078 1079 entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl, 1080 table_name, 1081 line, 1082 &is_blank_or_comment); 1083 if (!entry) { 1084 if (is_blank_or_comment) 1085 continue; 1086 1087 snprintf(out, out_size, MSG_FILE_ERR, 1088 file_name_default, line_id); 1089 goto error; 1090 } 1091 1092 status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl, 1093 table_name, 1094 entry); 1095 table_entry_free(entry); 1096 if (status) { 1097 snprintf(out, out_size, 1098 "Invalid entry in file %s at line %u", 1099 file_name_default, line_id); 1100 goto error; 1101 } 1102 } 1103 1104 status = rte_swx_ctl_pipeline_commit(p->ctl, 1); 1105 if (status) { 1106 snprintf(out, out_size, "Commit failed."); 1107 goto error; 1108 } 1109 1110 1111 rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name); 1112 1113 free(line); 1114 if (file_add) 1115 fclose(file_add); 1116 if (file_delete) 1117 fclose(file_delete); 1118 if (file_default) 1119 fclose(file_default); 1120 return; 1121 1122 error: 1123 rte_swx_ctl_pipeline_abort(p->ctl); 1124 free(line); 1125 if (file_add) 1126 fclose(file_add); 1127 if (file_delete) 1128 fclose(file_delete); 1129 if (file_default) 1130 fclose(file_default); 1131 } 1132 1133 static const char cmd_pipeline_stats_help[] = 1134 "pipeline <pipeline_name> stats\n"; 1135 1136 static void 1137 cmd_pipeline_stats(char **tokens, 1138 uint32_t n_tokens, 1139 char *out, 1140 size_t out_size, 1141 void *obj) 1142 { 1143 struct rte_swx_ctl_pipeline_info info; 1144 struct pipeline *p; 1145 uint32_t i; 1146 int status; 1147 1148 if (n_tokens != 3) { 1149 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 1150 return; 1151 } 1152 1153 p = pipeline_find(obj, tokens[1]); 1154 if (!p || !p->ctl) { 1155 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 1156 return; 1157 } 1158 1159 if (strcmp(tokens[2], "stats")) { 1160 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats"); 1161 return; 1162 } 1163 1164 status = rte_swx_ctl_pipeline_info_get(p->p, &info); 1165 if (status) { 1166 snprintf(out, out_size, "Pipeline info get error."); 1167 return; 1168 } 1169 1170 snprintf(out, out_size, "Input ports:\n"); 1171 out_size -= strlen(out); 1172 out += strlen(out); 1173 1174 for (i = 0; i < info.n_ports_in; i++) { 1175 struct rte_swx_port_in_stats stats; 1176 1177 rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats); 1178 1179 snprintf(out, out_size, "\tPort %u:" 1180 " packets %" PRIu64 1181 " bytes %" PRIu64 1182 " empty %" PRIu64 "\n", 1183 i, stats.n_pkts, stats.n_bytes, stats.n_empty); 1184 out_size -= strlen(out); 1185 out += strlen(out); 1186 } 1187 1188 snprintf(out, out_size, "Output ports:\n"); 1189 out_size -= strlen(out); 1190 out += strlen(out); 1191 1192 for (i = 0; i < info.n_ports_out; i++) { 1193 struct rte_swx_port_out_stats stats; 1194 1195 rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats); 1196 1197 snprintf(out, out_size, "\tPort %u:" 1198 " packets %" PRIu64 1199 " bytes %" PRIu64 "\n", 1200 i, stats.n_pkts, stats.n_bytes); 1201 out_size -= strlen(out); 1202 out += strlen(out); 1203 } 1204 } 1205 1206 static const char cmd_thread_pipeline_enable_help[] = 1207 "thread <thread_id> pipeline <pipeline_name> enable\n"; 1208 1209 static void 1210 cmd_thread_pipeline_enable(char **tokens, 1211 uint32_t n_tokens, 1212 char *out, 1213 size_t out_size, 1214 void *obj) 1215 { 1216 char *pipeline_name; 1217 struct pipeline *p; 1218 uint32_t thread_id; 1219 int status; 1220 1221 if (n_tokens != 5) { 1222 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 1223 return; 1224 } 1225 1226 if (parser_read_uint32(&thread_id, tokens[1]) != 0) { 1227 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); 1228 return; 1229 } 1230 1231 if (strcmp(tokens[2], "pipeline") != 0) { 1232 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); 1233 return; 1234 } 1235 1236 pipeline_name = tokens[3]; 1237 p = pipeline_find(obj, pipeline_name); 1238 if (!p || !p->ctl) { 1239 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 1240 return; 1241 } 1242 1243 if (strcmp(tokens[4], "enable") != 0) { 1244 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable"); 1245 return; 1246 } 1247 1248 status = thread_pipeline_enable(thread_id, obj, pipeline_name); 1249 if (status) { 1250 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable"); 1251 return; 1252 } 1253 } 1254 1255 static const char cmd_thread_pipeline_disable_help[] = 1256 "thread <thread_id> pipeline <pipeline_name> disable\n"; 1257 1258 static void 1259 cmd_thread_pipeline_disable(char **tokens, 1260 uint32_t n_tokens, 1261 char *out, 1262 size_t out_size, 1263 void *obj) 1264 { 1265 struct pipeline *p; 1266 char *pipeline_name; 1267 uint32_t thread_id; 1268 int status; 1269 1270 if (n_tokens != 5) { 1271 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); 1272 return; 1273 } 1274 1275 if (parser_read_uint32(&thread_id, tokens[1]) != 0) { 1276 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); 1277 return; 1278 } 1279 1280 if (strcmp(tokens[2], "pipeline") != 0) { 1281 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); 1282 return; 1283 } 1284 1285 pipeline_name = tokens[3]; 1286 p = pipeline_find(obj, pipeline_name); 1287 if (!p || !p->ctl) { 1288 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name"); 1289 return; 1290 } 1291 1292 if (strcmp(tokens[4], "disable") != 0) { 1293 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable"); 1294 return; 1295 } 1296 1297 status = thread_pipeline_disable(thread_id, obj, pipeline_name); 1298 if (status) { 1299 snprintf(out, out_size, MSG_CMD_FAIL, 1300 "thread pipeline disable"); 1301 return; 1302 } 1303 } 1304 1305 static void 1306 cmd_help(char **tokens, 1307 uint32_t n_tokens, 1308 char *out, 1309 size_t out_size, 1310 void *arg __rte_unused) 1311 { 1312 tokens++; 1313 n_tokens--; 1314 1315 if (n_tokens == 0) { 1316 snprintf(out, out_size, 1317 "Type 'help <command>' for command details.\n\n" 1318 "List of commands:\n" 1319 "\tmempool\n" 1320 "\tlink\n" 1321 "\tpipeline create\n" 1322 "\tpipeline port in\n" 1323 "\tpipeline port out\n" 1324 "\tpipeline build\n" 1325 "\tpipeline table update\n" 1326 "\tpipeline stats\n" 1327 "\tthread pipeline enable\n" 1328 "\tthread pipeline disable\n\n"); 1329 return; 1330 } 1331 1332 if (strcmp(tokens[0], "mempool") == 0) { 1333 snprintf(out, out_size, "\n%s\n", cmd_mempool_help); 1334 return; 1335 } 1336 1337 if (strcmp(tokens[0], "link") == 0) { 1338 snprintf(out, out_size, "\n%s\n", cmd_link_help); 1339 return; 1340 } 1341 1342 if (strcmp(tokens[0], "ring") == 0) { 1343 snprintf(out, out_size, "\n%s\n", cmd_ring_help); 1344 return; 1345 } 1346 1347 if ((strcmp(tokens[0], "pipeline") == 0) && 1348 (n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) { 1349 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help); 1350 return; 1351 } 1352 1353 if ((strcmp(tokens[0], "pipeline") == 0) && 1354 (n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) { 1355 if (strcmp(tokens[2], "in") == 0) { 1356 snprintf(out, out_size, "\n%s\n", 1357 cmd_pipeline_port_in_help); 1358 return; 1359 } 1360 1361 if (strcmp(tokens[2], "out") == 0) { 1362 snprintf(out, out_size, "\n%s\n", 1363 cmd_pipeline_port_out_help); 1364 return; 1365 } 1366 } 1367 1368 if ((strcmp(tokens[0], "pipeline") == 0) && 1369 (n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) { 1370 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help); 1371 return; 1372 } 1373 1374 if ((strcmp(tokens[0], "pipeline") == 0) && 1375 (n_tokens == 3) && 1376 (strcmp(tokens[1], "table") == 0) && 1377 (strcmp(tokens[2], "update") == 0)) { 1378 snprintf(out, out_size, "\n%s\n", 1379 cmd_pipeline_table_update_help); 1380 return; 1381 } 1382 1383 if ((strcmp(tokens[0], "pipeline") == 0) && 1384 (n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) { 1385 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help); 1386 return; 1387 } 1388 1389 if ((n_tokens == 3) && 1390 (strcmp(tokens[0], "thread") == 0) && 1391 (strcmp(tokens[1], "pipeline") == 0)) { 1392 if (strcmp(tokens[2], "enable") == 0) { 1393 snprintf(out, out_size, "\n%s\n", 1394 cmd_thread_pipeline_enable_help); 1395 return; 1396 } 1397 1398 if (strcmp(tokens[2], "disable") == 0) { 1399 snprintf(out, out_size, "\n%s\n", 1400 cmd_thread_pipeline_disable_help); 1401 return; 1402 } 1403 } 1404 1405 snprintf(out, out_size, "Invalid command\n"); 1406 } 1407 1408 void 1409 cli_process(char *in, char *out, size_t out_size, void *obj) 1410 { 1411 char *tokens[CMD_MAX_TOKENS]; 1412 uint32_t n_tokens = RTE_DIM(tokens); 1413 int status; 1414 1415 if (is_comment(in)) 1416 return; 1417 1418 status = parse_tokenize_string(in, tokens, &n_tokens); 1419 if (status) { 1420 snprintf(out, out_size, MSG_ARG_TOO_MANY, ""); 1421 return; 1422 } 1423 1424 if (n_tokens == 0) 1425 return; 1426 1427 if (strcmp(tokens[0], "help") == 0) { 1428 cmd_help(tokens, n_tokens, out, out_size, obj); 1429 return; 1430 } 1431 1432 if (strcmp(tokens[0], "mempool") == 0) { 1433 cmd_mempool(tokens, n_tokens, out, out_size, obj); 1434 return; 1435 } 1436 1437 if (strcmp(tokens[0], "link") == 0) { 1438 if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) { 1439 cmd_link_show(tokens, n_tokens, out, out_size, obj); 1440 return; 1441 } 1442 1443 cmd_link(tokens, n_tokens, out, out_size, obj); 1444 return; 1445 } 1446 1447 if (strcmp(tokens[0], "ring") == 0) { 1448 cmd_ring(tokens, n_tokens, out, out_size, obj); 1449 return; 1450 } 1451 1452 if (strcmp(tokens[0], "pipeline") == 0) { 1453 if ((n_tokens >= 3) && 1454 (strcmp(tokens[2], "create") == 0)) { 1455 cmd_pipeline_create(tokens, n_tokens, out, out_size, 1456 obj); 1457 return; 1458 } 1459 1460 if ((n_tokens >= 4) && 1461 (strcmp(tokens[2], "port") == 0) && 1462 (strcmp(tokens[3], "in") == 0)) { 1463 cmd_pipeline_port_in(tokens, n_tokens, out, out_size, 1464 obj); 1465 return; 1466 } 1467 1468 if ((n_tokens >= 4) && 1469 (strcmp(tokens[2], "port") == 0) && 1470 (strcmp(tokens[3], "out") == 0)) { 1471 cmd_pipeline_port_out(tokens, n_tokens, out, out_size, 1472 obj); 1473 return; 1474 } 1475 1476 if ((n_tokens >= 3) && 1477 (strcmp(tokens[2], "build") == 0)) { 1478 cmd_pipeline_build(tokens, n_tokens, out, out_size, 1479 obj); 1480 return; 1481 } 1482 1483 if ((n_tokens >= 3) && 1484 (strcmp(tokens[2], "table") == 0)) { 1485 cmd_pipeline_table_update(tokens, n_tokens, out, 1486 out_size, obj); 1487 return; 1488 } 1489 1490 if ((n_tokens >= 3) && 1491 (strcmp(tokens[2], "stats") == 0)) { 1492 cmd_pipeline_stats(tokens, n_tokens, out, out_size, 1493 obj); 1494 return; 1495 } 1496 } 1497 1498 if (strcmp(tokens[0], "thread") == 0) { 1499 if ((n_tokens >= 5) && 1500 (strcmp(tokens[4], "enable") == 0)) { 1501 cmd_thread_pipeline_enable(tokens, n_tokens, 1502 out, out_size, obj); 1503 return; 1504 } 1505 1506 if ((n_tokens >= 5) && 1507 (strcmp(tokens[4], "disable") == 0)) { 1508 cmd_thread_pipeline_disable(tokens, n_tokens, 1509 out, out_size, obj); 1510 return; 1511 } 1512 } 1513 1514 snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); 1515 } 1516 1517 int 1518 cli_script_process(const char *file_name, 1519 size_t msg_in_len_max, 1520 size_t msg_out_len_max, 1521 void *obj) 1522 { 1523 char *msg_in = NULL, *msg_out = NULL; 1524 FILE *f = NULL; 1525 1526 /* Check input arguments */ 1527 if ((file_name == NULL) || 1528 (strlen(file_name) == 0) || 1529 (msg_in_len_max == 0) || 1530 (msg_out_len_max == 0)) 1531 return -EINVAL; 1532 1533 msg_in = malloc(msg_in_len_max + 1); 1534 msg_out = malloc(msg_out_len_max + 1); 1535 if ((msg_in == NULL) || 1536 (msg_out == NULL)) { 1537 free(msg_out); 1538 free(msg_in); 1539 return -ENOMEM; 1540 } 1541 1542 /* Open input file */ 1543 f = fopen(file_name, "r"); 1544 if (f == NULL) { 1545 free(msg_out); 1546 free(msg_in); 1547 return -EIO; 1548 } 1549 1550 /* Read file */ 1551 for ( ; ; ) { 1552 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL) 1553 break; 1554 1555 printf("%s", msg_in); 1556 msg_out[0] = 0; 1557 1558 cli_process(msg_in, 1559 msg_out, 1560 msg_out_len_max, 1561 obj); 1562 1563 if (strlen(msg_out)) 1564 printf("%s", msg_out); 1565 } 1566 1567 /* Close file */ 1568 fclose(f); 1569 free(msg_out); 1570 free(msg_in); 1571 return 0; 1572 } 1573