xref: /dpdk/examples/pipeline/cli.c (revision 455a771fd6f1a9cb6edc8711ff278ad31709cf7c)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #include <ctype.h>
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <rte_common.h>
13 #include <rte_ethdev.h>
14 #include <rte_ring.h>
15 #include <rte_swx_port_ethdev.h>
16 #include <rte_swx_port_ring.h>
17 #include <rte_swx_port_source_sink.h>
18 #include <rte_swx_port_fd.h>
19 #include <rte_swx_pipeline.h>
20 #include <rte_swx_ctl.h>
21 #include <rte_swx_ipsec.h>
22 #include <rte_string_fns.h>
23 
24 #include "cli.h"
25 
26 #include "obj.h"
27 #include "thread.h"
28 
29 #ifndef CMD_MAX_TOKENS
30 #define CMD_MAX_TOKENS     256
31 #endif
32 
33 #ifndef MAX_LINE_SIZE
34 #define MAX_LINE_SIZE 2048
35 #endif
36 
37 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
38 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
39 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
40 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
41 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
42 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
43 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
44 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
45 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
46 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
47 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
48 
49 static int
50 parser_read_uint64(uint64_t *value, const char *p)
51 {
52 	char *next;
53 	uint64_t val;
54 
55 	p = rte_str_skip_leading_spaces(p);
56 	if (!isdigit(*p))
57 		return -EINVAL;
58 
59 	val = strtoul(p, &next, 0);
60 	if (p == next)
61 		return -EINVAL;
62 
63 	p = next;
64 	switch (*p) {
65 	case 'T':
66 		val *= 1024ULL;
67 		/* fall through */
68 	case 'G':
69 		val *= 1024ULL;
70 		/* fall through */
71 	case 'M':
72 		val *= 1024ULL;
73 		/* fall through */
74 	case 'k':
75 	case 'K':
76 		val *= 1024ULL;
77 		p++;
78 		break;
79 	}
80 
81 	p = rte_str_skip_leading_spaces(p);
82 	if (*p != '\0')
83 		return -EINVAL;
84 
85 	*value = val;
86 	return 0;
87 }
88 
89 static int
90 parser_read_uint32(uint32_t *value, const char *p)
91 {
92 	uint64_t val = 0;
93 	int ret = parser_read_uint64(&val, p);
94 
95 	if (ret < 0)
96 		return ret;
97 
98 	if (val > UINT32_MAX)
99 		return -ERANGE;
100 
101 	*value = val;
102 	return 0;
103 }
104 
105 #define PARSE_DELIMITER " \f\n\r\t\v"
106 
107 static int
108 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
109 {
110 	uint32_t i;
111 
112 	if ((string == NULL) ||
113 		(tokens == NULL) ||
114 		(*n_tokens < 1))
115 		return -EINVAL;
116 
117 	for (i = 0; i < *n_tokens; i++) {
118 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
119 		if (tokens[i] == NULL)
120 			break;
121 	}
122 
123 	if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
124 		return -E2BIG;
125 
126 	*n_tokens = i;
127 	return 0;
128 }
129 
130 static int
131 is_comment(char *in)
132 {
133 	if ((strlen(in) && index("!#%;", in[0])) ||
134 		(strncmp(in, "//", 2) == 0) ||
135 		(strncmp(in, "--", 2) == 0))
136 		return 1;
137 
138 	return 0;
139 }
140 
141 static void
142 table_entry_free(struct rte_swx_table_entry *entry)
143 {
144 	if (!entry)
145 		return;
146 
147 	free(entry->key);
148 	free(entry->key_mask);
149 	free(entry->action_data);
150 	free(entry);
151 }
152 
153 static struct rte_swx_table_entry *
154 parse_table_entry(struct rte_swx_ctl_pipeline *p,
155 		  char *table_name,
156 		  char **tokens,
157 		  uint32_t n_tokens)
158 {
159 	struct rte_swx_table_entry *entry;
160 	char *line;
161 	uint32_t i;
162 
163 	/* Buffer allocation. */
164 	line = malloc(MAX_LINE_SIZE);
165 	if (!line)
166 		return NULL;
167 
168 	/* Copy tokens to buffer. Since the tokens were initially part of a buffer of size
169 	 * MAX_LINE_LENGTH, it is guaranteed that putting back some of them into a buffer of the
170 	 * same size separated by a single space will not result in buffer overrun.
171 	 */
172 	line[0] = 0;
173 	for (i = 0; i < n_tokens; i++) {
174 		if (i)
175 			strcat(line, " ");
176 
177 		strcat(line, tokens[i]);
178 	}
179 
180 	/* Read the table entry from the input buffer. */
181 	entry = rte_swx_ctl_pipeline_table_entry_read(p, table_name, line, NULL);
182 
183 	/* Buffer free. */
184 	free(line);
185 
186 	return entry;
187 }
188 
189 static const char cmd_mempool_help[] =
190 "mempool <mempool_name> "
191 "meta <mbuf_private_size> "
192 "pkt <pkt_buffer_size> "
193 "pool <pool_size> "
194 "cache <cache_size> "
195 "numa <numa_node>\n";
196 
197 static void
198 cmd_mempool(char **tokens,
199 	    uint32_t n_tokens,
200 	    char *out,
201 	    size_t out_size,
202 	    void *obj __rte_unused)
203 {
204 	struct rte_mempool *mp;
205 	char *mempool_name;
206 	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
207 
208 	if (n_tokens != 12) {
209 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
210 		return;
211 	}
212 
213 	mempool_name = tokens[1];
214 
215 	if (strcmp(tokens[2], "meta")) {
216 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
217 		return;
218 	}
219 
220 	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
221 		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
222 		return;
223 	}
224 
225 	if (strcmp(tokens[4], "pkt")) {
226 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
227 		return;
228 	}
229 
230 	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
231 		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
232 		return;
233 	}
234 
235 	if (strcmp(tokens[6], "pool")) {
236 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
237 		return;
238 	}
239 
240 	if (parser_read_uint32(&pool_size, tokens[7])) {
241 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
242 		return;
243 	}
244 
245 	if (strcmp(tokens[8], "cache")) {
246 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
247 		return;
248 	}
249 
250 	if (parser_read_uint32(&cache_size, tokens[9])) {
251 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
252 		return;
253 	}
254 
255 	if (strcmp(tokens[10], "numa")) {
256 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
257 		return;
258 	}
259 
260 	if (parser_read_uint32(&numa_node, tokens[11])) {
261 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
262 		return;
263 	}
264 
265 	mp = rte_pktmbuf_pool_create(mempool_name,
266 				     pool_size,
267 				     cache_size,
268 				     mbuf_private_size,
269 				     pkt_buffer_size,
270 				     numa_node);
271 	if (!mp) {
272 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
273 		return;
274 	}
275 }
276 
277 static const char cmd_ethdev_help[] =
278 "ethdev <ethdev_name>\n"
279 "   rxq <n_queues> <queue_size> <mempool_name>\n"
280 "   txq <n_queues> <queue_size>\n"
281 "   promiscuous on | off\n"
282 "   [rss <qid_0> ... <qid_n>]\n";
283 
284 static void
285 cmd_ethdev(char **tokens,
286 	uint32_t n_tokens,
287 	char *out,
288 	size_t out_size,
289 	void *obj __rte_unused)
290 {
291 	struct ethdev_params p;
292 	struct ethdev_params_rss rss;
293 	char *name;
294 	int status;
295 
296 	memset(&p, 0, sizeof(p));
297 	memset(&rss, 0, sizeof(rss));
298 
299 	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
300 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
301 		return;
302 	}
303 	name = tokens[1];
304 
305 	if (strcmp(tokens[2], "rxq") != 0) {
306 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
307 		return;
308 	}
309 
310 	if (parser_read_uint32(&p.rx.n_queues, tokens[3]) != 0) {
311 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
312 		return;
313 	}
314 	if (parser_read_uint32(&p.rx.queue_size, tokens[4]) != 0) {
315 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
316 		return;
317 	}
318 
319 	p.rx.mempool_name = tokens[5];
320 
321 	if (strcmp(tokens[6], "txq") != 0) {
322 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
323 		return;
324 	}
325 
326 	if (parser_read_uint32(&p.tx.n_queues, tokens[7]) != 0) {
327 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
328 		return;
329 	}
330 
331 	if (parser_read_uint32(&p.tx.queue_size, tokens[8]) != 0) {
332 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
333 		return;
334 	}
335 
336 	if (strcmp(tokens[9], "promiscuous") != 0) {
337 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
338 		return;
339 	}
340 
341 	if (strcmp(tokens[10], "on") == 0)
342 		p.promiscuous = 1;
343 	else if (strcmp(tokens[10], "off") == 0)
344 		p.promiscuous = 0;
345 	else {
346 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
347 		return;
348 	}
349 
350 	/* RSS */
351 	p.rx.rss = NULL;
352 	if (n_tokens > 11) {
353 		uint32_t queue_id, i;
354 
355 		if (strcmp(tokens[11], "rss") != 0) {
356 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
357 			return;
358 		}
359 
360 		p.rx.rss = &rss;
361 
362 		rss.n_queues = 0;
363 		for (i = 12; i < n_tokens; i++) {
364 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
365 				snprintf(out, out_size, MSG_ARG_INVALID,
366 					"queue_id");
367 				return;
368 			}
369 
370 			rss.queue_id[rss.n_queues] = queue_id;
371 			rss.n_queues++;
372 		}
373 	}
374 
375 	status = ethdev_config(name, &p);
376 	if (status) {
377 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
378 		return;
379 	}
380 }
381 
382 static void
383 ethdev_show(uint16_t port_id, char **out, size_t *out_size)
384 {
385 	char name[RTE_ETH_NAME_MAX_LEN];
386 	struct rte_eth_dev_info info;
387 	struct rte_eth_stats stats;
388 	struct rte_ether_addr addr;
389 	struct rte_eth_link link;
390 	uint32_t length;
391 	uint16_t mtu = 0;
392 
393 	if (!rte_eth_dev_is_valid_port(port_id))
394 		return;
395 
396 	rte_eth_dev_get_name_by_port(port_id, name);
397 	rte_eth_dev_info_get(port_id, &info);
398 	rte_eth_stats_get(port_id, &stats);
399 	rte_eth_macaddr_get(port_id, &addr);
400 	rte_eth_link_get(port_id, &link);
401 	rte_eth_dev_get_mtu(port_id, &mtu);
402 
403 	snprintf(*out, *out_size,
404 		 "%s: flags=<%s> mtu %u\n"
405 		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
406 		 "\tport# %u  speed %s\n"
407 		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
408 		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
409 		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
410 		 "\tTX errors %" PRIu64"\n\n",
411 		 name,
412 		 link.link_status ? "UP" : "DOWN",
413 		 mtu,
414 		 RTE_ETHER_ADDR_BYTES(&addr),
415 		 info.nb_rx_queues,
416 		 info.nb_tx_queues,
417 		 port_id,
418 		 rte_eth_link_speed_to_str(link.link_speed),
419 		 stats.ipackets,
420 		 stats.ibytes,
421 		 stats.ierrors,
422 		 stats.imissed,
423 		 stats.rx_nombuf,
424 		 stats.opackets,
425 		 stats.obytes,
426 		 stats.oerrors);
427 
428 	length = strlen(*out);
429 	*out_size -= length;
430 	*out += length;
431 }
432 
433 
434 static char cmd_ethdev_show_help[] =
435 "ethdev show [ <ethdev_name> ]\n";
436 
437 static void
438 cmd_ethdev_show(char **tokens,
439 	      uint32_t n_tokens,
440 	      char *out,
441 	      size_t out_size,
442 	      void *obj __rte_unused)
443 {
444 	uint16_t port_id;
445 
446 	if (n_tokens != 2 && n_tokens != 3) {
447 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
448 		return;
449 	}
450 
451 	/* Single device. */
452 	if (n_tokens == 3) {
453 		int status;
454 
455 		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
456 		if (status)
457 			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
458 
459 		ethdev_show(port_id, &out, &out_size);
460 		return;
461 	}
462 
463 	/*  All devices. */
464 	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
465 		if (rte_eth_dev_is_valid_port(port_id))
466 			ethdev_show(port_id, &out, &out_size);
467 }
468 
469 static const char cmd_ring_help[] =
470 "ring <ring_name> size <size> numa <numa_node>\n";
471 
472 static void
473 cmd_ring(char **tokens,
474 	uint32_t n_tokens,
475 	char *out,
476 	size_t out_size,
477 	void *obj __rte_unused)
478 {
479 	struct rte_ring *r;
480 	char *name;
481 	uint32_t size, numa_node;
482 
483 	if (n_tokens != 6) {
484 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
485 		return;
486 	}
487 
488 	name = tokens[1];
489 
490 	if (strcmp(tokens[2], "size")) {
491 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
492 		return;
493 	}
494 
495 	if (parser_read_uint32(&size, tokens[3])) {
496 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
497 		return;
498 	}
499 
500 	if (strcmp(tokens[4], "numa")) {
501 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
502 		return;
503 	}
504 
505 	if (parser_read_uint32(&numa_node, tokens[5])) {
506 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
507 		return;
508 	}
509 
510 	r = rte_ring_create(
511 		name,
512 		size,
513 		(int)numa_node,
514 		RING_F_SP_ENQ | RING_F_SC_DEQ);
515 	if (!r) {
516 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
517 		return;
518 	}
519 }
520 
521 static const char cmd_cryptodev_help[] =
522 "cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
523 
524 static void
525 cmd_cryptodev(char **tokens,
526 	      uint32_t n_tokens,
527 	      char *out,
528 	      size_t out_size,
529 	      void *obj __rte_unused)
530 {
531 	struct cryptodev_params params;
532 	char *cryptodev_name;
533 	int status;
534 
535 	if (n_tokens != 6) {
536 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
537 		return;
538 	}
539 
540 	if (strcmp(tokens[0], "cryptodev")) {
541 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
542 		return;
543 	}
544 
545 	cryptodev_name = tokens[1];
546 
547 	if (strcmp(tokens[2], "queues")) {
548 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
549 		return;
550 	}
551 
552 	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
553 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
554 		return;
555 	}
556 
557 	if (strcmp(tokens[4], "qsize")) {
558 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
559 		return;
560 	}
561 
562 
563 	if (parser_read_uint32(&params.queue_size, tokens[5])) {
564 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
565 		return;
566 	}
567 
568 	status = cryptodev_config(cryptodev_name, &params);
569 	if (status)
570 		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
571 }
572 
573 static const char cmd_pipeline_codegen_help[] =
574 "pipeline codegen <spec_file> <code_file>\n";
575 
576 static void
577 cmd_pipeline_codegen(char **tokens,
578 	uint32_t n_tokens,
579 	char *out,
580 	size_t out_size,
581 	void *obj __rte_unused)
582 {
583 	FILE *spec_file = NULL;
584 	FILE *code_file = NULL;
585 	uint32_t err_line;
586 	const char *err_msg;
587 	int status;
588 
589 	if (n_tokens != 4) {
590 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
591 		return;
592 	}
593 
594 	spec_file = fopen(tokens[2], "r");
595 	if (!spec_file) {
596 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
597 		return;
598 	}
599 
600 	code_file = fopen(tokens[3], "w");
601 	if (!code_file) {
602 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
603 		fclose(spec_file);
604 		return;
605 	}
606 
607 	status = rte_swx_pipeline_codegen(spec_file,
608 					  code_file,
609 					  &err_line,
610 					  &err_msg);
611 
612 	fclose(spec_file);
613 	fclose(code_file);
614 
615 	if (status) {
616 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
617 			status, err_line, err_msg);
618 		return;
619 	}
620 }
621 
622 static const char cmd_pipeline_libbuild_help[] =
623 "pipeline libbuild <code_file> <lib_file>\n";
624 
625 static void
626 cmd_pipeline_libbuild(char **tokens,
627 	uint32_t n_tokens,
628 	char *out,
629 	size_t out_size,
630 	void *obj __rte_unused)
631 {
632 	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
633 	char *install_dir, *cwd = NULL, *buffer = NULL;
634 	size_t length;
635 	int status = 0;
636 
637 	if (n_tokens != 4) {
638 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
639 		goto free;
640 	}
641 
642 	install_dir = getenv("RTE_INSTALL_DIR");
643 	if (!install_dir) {
644 		cwd = malloc(MAX_LINE_SIZE);
645 		if (!cwd) {
646 			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
647 			goto free;
648 		}
649 
650 		install_dir = getcwd(cwd, MAX_LINE_SIZE);
651 		if (!install_dir) {
652 			snprintf(out, out_size, "Error: Path too long.\n");
653 			goto free;
654 		}
655 	}
656 
657 	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
658 	out_size -= strlen(out);
659 	out += strlen(out);
660 
661 	code_file = tokens[2];
662 	length = strnlen(code_file, MAX_LINE_SIZE);
663 	if ((length < 3) ||
664 	    (code_file[length - 2] != '.') ||
665 	    (code_file[length - 1] != 'c')) {
666 		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
667 		goto free;
668 	}
669 
670 	lib_file = tokens[3];
671 	length = strnlen(lib_file, MAX_LINE_SIZE);
672 	if ((length < 4) ||
673 	    (lib_file[length - 3] != '.') ||
674 	    (lib_file[length - 2] != 's') ||
675 	    (lib_file[length - 1] != 'o')) {
676 		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
677 		goto free;
678 	}
679 
680 	obj_file = malloc(length);
681 	log_file = malloc(length + 2);
682 	if (!obj_file || !log_file) {
683 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
684 		goto free;
685 	}
686 
687 	memcpy(obj_file, lib_file, length - 2);
688 	obj_file[length - 2] = 'o';
689 	obj_file[length - 1] = 0;
690 
691 	memcpy(log_file, lib_file, length - 2);
692 	log_file[length - 2] = 'l';
693 	log_file[length - 1] = 'o';
694 	log_file[length] = 'g';
695 	log_file[length + 1] = 0;
696 
697 	buffer = malloc(MAX_LINE_SIZE);
698 	if (!buffer) {
699 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
700 		goto free;
701 	}
702 
703 	snprintf(buffer,
704 		 MAX_LINE_SIZE,
705 		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
706 		 "-I %s/lib/pipeline "
707 		 "-I %s/lib/eal/include "
708 		 "-I %s/lib/eal/x86/include "
709 		 "-I %s/lib/eal/include/generic "
710 		 "-I %s/lib/log "
711 		 "-I %s/lib/meter "
712 		 "-I %s/lib/port "
713 		 "-I %s/lib/table "
714 		 "-I %s/lib/pipeline "
715 		 "-I %s/config "
716 		 "-I %s/build "
717 		 "-I %s/lib/eal/linux/include "
718 		 ">%s 2>&1 "
719 		 "&& "
720 		 "gcc -shared %s -o %s "
721 		 ">>%s 2>&1",
722 		 obj_file,
723 		 code_file,
724 		 install_dir,
725 		 install_dir,
726 		 install_dir,
727 		 install_dir,
728 		 install_dir,
729 		 install_dir,
730 		 install_dir,
731 		 install_dir,
732 		 install_dir,
733 		 install_dir,
734 		 install_dir,
735 		 install_dir,
736 		 log_file,
737 		 obj_file,
738 		 lib_file,
739 		 log_file);
740 
741 	status = system(buffer);
742 	if (status) {
743 		snprintf(out,
744 			 out_size,
745 			 "Library build failed, see file \"%s\" for details.\n",
746 			 log_file);
747 		goto free;
748 	}
749 
750 free:
751 	free(cwd);
752 	free(obj_file);
753 	free(log_file);
754 	free(buffer);
755 }
756 
757 static const char cmd_pipeline_build_help[] =
758 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
759 
760 static void
761 cmd_pipeline_build(char **tokens,
762 	uint32_t n_tokens,
763 	char *out,
764 	size_t out_size,
765 	void *obj __rte_unused)
766 {
767 	struct rte_swx_pipeline *p = NULL;
768 	struct rte_swx_ctl_pipeline *ctl = NULL;
769 	char *pipeline_name, *lib_file_name, *iospec_file_name;
770 	FILE *iospec_file = NULL;
771 	uint32_t numa_node = 0;
772 	int status = 0;
773 
774 	/* Parsing. */
775 	if (n_tokens != 9) {
776 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
777 		return;
778 	}
779 
780 	pipeline_name = tokens[1];
781 
782 	if (strcmp(tokens[2], "build")) {
783 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
784 		return;
785 	}
786 
787 	if (strcmp(tokens[3], "lib")) {
788 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
789 		return;
790 	}
791 
792 	lib_file_name = tokens[4];
793 
794 	if (strcmp(tokens[5], "io")) {
795 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
796 		return;
797 	}
798 
799 	iospec_file_name = tokens[6];
800 
801 	if (strcmp(tokens[7], "numa")) {
802 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
803 		return;
804 	}
805 
806 	if (parser_read_uint32(&numa_node, tokens[8])) {
807 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
808 		return;
809 	}
810 
811 	/* I/O spec file open. */
812 	iospec_file = fopen(iospec_file_name, "r");
813 	if (!iospec_file) {
814 		snprintf(out, out_size, "Cannot open file \"%s\".\n", iospec_file_name);
815 		return;
816 	}
817 
818 	status = rte_swx_pipeline_build_from_lib(&p,
819 						 pipeline_name,
820 						 lib_file_name,
821 						 iospec_file,
822 						 (int)numa_node);
823 	if (status) {
824 		snprintf(out, out_size, "Pipeline build failed (%d).", status);
825 		goto free;
826 	}
827 
828 	ctl = rte_swx_ctl_pipeline_create(p);
829 	if (!ctl) {
830 		snprintf(out, out_size, "Pipeline control create failed.");
831 		goto free;
832 	}
833 
834 free:
835 	if (status)
836 		rte_swx_pipeline_free(p);
837 
838 	if (iospec_file)
839 		fclose(iospec_file);
840 }
841 
842 static int
843 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
844 			   const char *table_name,
845 			   FILE *file,
846 			   uint32_t *file_line_number)
847 {
848 	char *line = NULL;
849 	uint32_t line_id = 0;
850 	int status = 0;
851 
852 	/* Buffer allocation. */
853 	line = malloc(MAX_LINE_SIZE);
854 	if (!line)
855 		return -ENOMEM;
856 
857 	/* File read. */
858 	for (line_id = 1; ; line_id++) {
859 		struct rte_swx_table_entry *entry;
860 		int is_blank_or_comment;
861 
862 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
863 			break;
864 
865 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
866 							      table_name,
867 							      line,
868 							      &is_blank_or_comment);
869 		if (!entry) {
870 			if (is_blank_or_comment)
871 				continue;
872 
873 			status = -EINVAL;
874 			goto error;
875 		}
876 
877 		status = rte_swx_ctl_pipeline_table_entry_add(p,
878 							      table_name,
879 							      entry);
880 		table_entry_free(entry);
881 		if (status)
882 			goto error;
883 	}
884 
885 error:
886 	free(line);
887 	*file_line_number = line_id;
888 	return status;
889 }
890 
891 static const char cmd_pipeline_table_add_help[] =
892 "pipeline <pipeline_name> table <table_name> add <file_name>\n";
893 
894 static void
895 cmd_pipeline_table_add(char **tokens,
896 		       uint32_t n_tokens,
897 		       char *out,
898 		       size_t out_size,
899 		       void *obj __rte_unused)
900 {
901 	struct rte_swx_ctl_pipeline *ctl;
902 	char *pipeline_name, *table_name, *file_name;
903 	FILE *file = NULL;
904 	uint32_t file_line_number = 0;
905 	int status;
906 
907 	if (n_tokens != 6) {
908 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
909 		return;
910 	}
911 
912 	pipeline_name = tokens[1];
913 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
914 	if (!ctl) {
915 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
916 		return;
917 	}
918 
919 	table_name = tokens[3];
920 
921 	file_name = tokens[5];
922 	file = fopen(file_name, "r");
923 	if (!file) {
924 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
925 		return;
926 	}
927 
928 	status = pipeline_table_entries_add(ctl,
929 					    table_name,
930 					    file,
931 					    &file_line_number);
932 	if (status)
933 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
934 			 file_name,
935 			 file_line_number);
936 
937 	fclose(file);
938 }
939 
940 static int
941 pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
942 			      const char *table_name,
943 			      FILE *file,
944 			      uint32_t *file_line_number)
945 {
946 	char *line = NULL;
947 	uint32_t line_id = 0;
948 	int status = 0;
949 
950 	/* Buffer allocation. */
951 	line = malloc(MAX_LINE_SIZE);
952 	if (!line)
953 		return -ENOMEM;
954 
955 	/* File read. */
956 	for (line_id = 1; ; line_id++) {
957 		struct rte_swx_table_entry *entry;
958 		int is_blank_or_comment;
959 
960 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
961 			break;
962 
963 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
964 							      table_name,
965 							      line,
966 							      &is_blank_or_comment);
967 		if (!entry) {
968 			if (is_blank_or_comment)
969 				continue;
970 
971 			status = -EINVAL;
972 			goto error;
973 		}
974 
975 		status = rte_swx_ctl_pipeline_table_entry_delete(p,
976 								 table_name,
977 								 entry);
978 		table_entry_free(entry);
979 		if (status)
980 			goto error;
981 	}
982 
983 error:
984 	*file_line_number = line_id;
985 	free(line);
986 	return status;
987 }
988 
989 static const char cmd_pipeline_table_delete_help[] =
990 "pipeline <pipeline_name> table <table_name> delete <file_name>\n";
991 
992 static void
993 cmd_pipeline_table_delete(char **tokens,
994 			  uint32_t n_tokens,
995 			  char *out,
996 			  size_t out_size,
997 			  void *obj __rte_unused)
998 {
999 	struct rte_swx_ctl_pipeline *ctl;
1000 	char *pipeline_name, *table_name, *file_name;
1001 	FILE *file = NULL;
1002 	uint32_t file_line_number = 0;
1003 	int status;
1004 
1005 	if (n_tokens != 6) {
1006 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1007 		return;
1008 	}
1009 
1010 	pipeline_name = tokens[1];
1011 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1012 	if (!ctl) {
1013 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1014 		return;
1015 	}
1016 
1017 	table_name = tokens[3];
1018 
1019 	file_name = tokens[5];
1020 	file = fopen(file_name, "r");
1021 	if (!file) {
1022 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1023 		return;
1024 	}
1025 
1026 	status = pipeline_table_entries_delete(ctl,
1027 					       table_name,
1028 					       file,
1029 					       &file_line_number);
1030 	if (status)
1031 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1032 			 file_name,
1033 			 file_line_number);
1034 
1035 	fclose(file);
1036 }
1037 
1038 static int
1039 pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
1040 				 const char *table_name,
1041 				 FILE *file,
1042 				 uint32_t *file_line_number)
1043 {
1044 	char *line = NULL;
1045 	uint32_t line_id = 0;
1046 	int status = 0;
1047 
1048 	/* Buffer allocation. */
1049 	line = malloc(MAX_LINE_SIZE);
1050 	if (!line)
1051 		return -ENOMEM;
1052 
1053 	/* File read. */
1054 	for (line_id = 1; ; line_id++) {
1055 		struct rte_swx_table_entry *entry;
1056 		int is_blank_or_comment;
1057 
1058 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1059 			break;
1060 
1061 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
1062 							      table_name,
1063 							      line,
1064 							      &is_blank_or_comment);
1065 		if (!entry) {
1066 			if (is_blank_or_comment)
1067 				continue;
1068 
1069 			status = -EINVAL;
1070 			goto error;
1071 		}
1072 
1073 		status = rte_swx_ctl_pipeline_table_default_entry_add(p,
1074 								      table_name,
1075 								      entry);
1076 		table_entry_free(entry);
1077 		if (status)
1078 			goto error;
1079 	}
1080 
1081 error:
1082 	*file_line_number = line_id;
1083 	free(line);
1084 	return status;
1085 }
1086 
1087 static const char cmd_pipeline_table_default_help[] =
1088 "pipeline <pipeline_name> table <table_name> default <file_name>\n";
1089 
1090 static void
1091 cmd_pipeline_table_default(char **tokens,
1092 			   uint32_t n_tokens,
1093 			   char *out,
1094 			   size_t out_size,
1095 			   void *obj __rte_unused)
1096 {
1097 	struct rte_swx_ctl_pipeline *ctl;
1098 	char *pipeline_name, *table_name, *file_name;
1099 	FILE *file = NULL;
1100 	uint32_t file_line_number = 0;
1101 	int status;
1102 
1103 	if (n_tokens != 6) {
1104 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1105 		return;
1106 	}
1107 
1108 	pipeline_name = tokens[1];
1109 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1110 	if (!ctl) {
1111 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1112 		return;
1113 	}
1114 
1115 	table_name = tokens[3];
1116 
1117 	file_name = tokens[5];
1118 	file = fopen(file_name, "r");
1119 	if (!file) {
1120 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1121 		return;
1122 	}
1123 
1124 	status = pipeline_table_default_entry_add(ctl,
1125 						  table_name,
1126 						  file,
1127 						  &file_line_number);
1128 	if (status)
1129 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1130 			 file_name,
1131 			 file_line_number);
1132 
1133 	fclose(file);
1134 }
1135 
1136 static const char cmd_pipeline_table_show_help[] =
1137 "pipeline <pipeline_name> table <table_name> show [filename]\n";
1138 
1139 static void
1140 cmd_pipeline_table_show(char **tokens,
1141 	uint32_t n_tokens,
1142 	char *out,
1143 	size_t out_size,
1144 	void *obj __rte_unused)
1145 {
1146 	struct rte_swx_ctl_pipeline *ctl;
1147 	char *pipeline_name, *table_name;
1148 	FILE *file = NULL;
1149 	int status;
1150 
1151 	if (n_tokens != 5 && n_tokens != 6) {
1152 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1153 		return;
1154 	}
1155 
1156 	pipeline_name = tokens[1];
1157 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1158 	if (!ctl) {
1159 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1160 		return;
1161 	}
1162 
1163 	table_name = tokens[3];
1164 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1165 	if (!file) {
1166 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1167 		return;
1168 	}
1169 
1170 	status = rte_swx_ctl_pipeline_table_fprintf(file, ctl, table_name);
1171 	if (status)
1172 		snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
1173 
1174 	if (file)
1175 		fclose(file);
1176 }
1177 
1178 static const char cmd_pipeline_selector_group_add_help[] =
1179 "pipeline <pipeline_name> selector <selector_name> group add\n";
1180 
1181 static void
1182 cmd_pipeline_selector_group_add(char **tokens,
1183 	uint32_t n_tokens,
1184 	char *out,
1185 	size_t out_size,
1186 	void *obj __rte_unused)
1187 {
1188 	struct rte_swx_ctl_pipeline *ctl;
1189 	char *pipeline_name, *selector_name;
1190 	uint32_t group_id;
1191 	int status;
1192 
1193 	if (n_tokens != 6) {
1194 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1195 		return;
1196 	}
1197 
1198 	pipeline_name = tokens[1];
1199 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1200 	if (!ctl) {
1201 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1202 		return;
1203 	}
1204 
1205 	if (strcmp(tokens[2], "selector") != 0) {
1206 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1207 		return;
1208 	}
1209 
1210 	selector_name = tokens[3];
1211 
1212 	if (strcmp(tokens[4], "group") ||
1213 		strcmp(tokens[5], "add")) {
1214 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
1215 		return;
1216 	}
1217 
1218 	status = rte_swx_ctl_pipeline_selector_group_add(ctl,
1219 		selector_name,
1220 		&group_id);
1221 	if (status)
1222 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1223 	else
1224 		snprintf(out, out_size, "Group ID: %u\n", group_id);
1225 }
1226 
1227 static const char cmd_pipeline_selector_group_delete_help[] =
1228 "pipeline <pipeline_name> selector <selector_name> group delete <group_id>\n";
1229 
1230 static void
1231 cmd_pipeline_selector_group_delete(char **tokens,
1232 	uint32_t n_tokens,
1233 	char *out,
1234 	size_t out_size,
1235 	void *obj __rte_unused)
1236 {
1237 	struct rte_swx_ctl_pipeline *ctl;
1238 	char *pipeline_name, *selector_name;
1239 	uint32_t group_id;
1240 	int status;
1241 
1242 	if (n_tokens != 7) {
1243 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1244 		return;
1245 	}
1246 
1247 	pipeline_name = tokens[1];
1248 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1249 	if (!ctl) {
1250 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1251 		return;
1252 	}
1253 
1254 	if (strcmp(tokens[2], "selector") != 0) {
1255 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1256 		return;
1257 	}
1258 
1259 	selector_name = tokens[3];
1260 
1261 	if (strcmp(tokens[4], "group") ||
1262 		strcmp(tokens[5], "delete")) {
1263 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
1264 		return;
1265 	}
1266 
1267 	if (parser_read_uint32(&group_id, tokens[6]) != 0) {
1268 		snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
1269 		return;
1270 	}
1271 
1272 	status = rte_swx_ctl_pipeline_selector_group_delete(ctl,
1273 		selector_name,
1274 		group_id);
1275 	if (status)
1276 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1277 }
1278 
1279 #define GROUP_MEMBER_INFO_TOKENS_MAX 6
1280 
1281 static int
1282 token_is_comment(const char *token)
1283 {
1284 	if ((token[0] == '#') ||
1285 	    (token[0] == ';') ||
1286 	    ((token[0] == '/') && (token[1] == '/')))
1287 		return 1; /* TRUE. */
1288 
1289 	return 0; /* FALSE. */
1290 }
1291 
1292 static int
1293 pipeline_selector_group_member_read(const char *string,
1294 				      uint32_t *group_id,
1295 				      uint32_t *member_id,
1296 				      uint32_t *weight,
1297 				      int *is_blank_or_comment)
1298 {
1299 	char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
1300 	char *s0 = NULL, *s;
1301 	uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
1302 	int blank_or_comment = 0;
1303 
1304 	/* Check input arguments. */
1305 	if (!string || !string[0])
1306 		goto error;
1307 
1308 	/* Memory allocation. */
1309 	s0 = strdup(string);
1310 	if (!s0)
1311 		goto error;
1312 
1313 	/* Parse the string into tokens. */
1314 	for (s = s0; ; ) {
1315 		char *token;
1316 
1317 		token = strtok_r(s, " \f\n\r\t\v", &s);
1318 		if (!token || token_is_comment(token))
1319 			break;
1320 
1321 		if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
1322 			goto error;
1323 
1324 		token_array[n_tokens] = token;
1325 		n_tokens++;
1326 	}
1327 
1328 	if (!n_tokens) {
1329 		blank_or_comment = 1;
1330 		goto error;
1331 	}
1332 
1333 	tokens = token_array;
1334 
1335 	if (n_tokens < 4 ||
1336 		strcmp(tokens[0], "group") ||
1337 		strcmp(tokens[2], "member"))
1338 		goto error;
1339 
1340 	/*
1341 	 * Group ID.
1342 	 */
1343 	if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
1344 		goto error;
1345 	*group_id = group_id_val;
1346 
1347 	/*
1348 	 * Member ID.
1349 	 */
1350 	if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
1351 		goto error;
1352 	*member_id = member_id_val;
1353 
1354 	tokens += 4;
1355 	n_tokens -= 4;
1356 
1357 	/*
1358 	 * Weight.
1359 	 */
1360 	if (n_tokens && !strcmp(tokens[0], "weight")) {
1361 		if (n_tokens < 2)
1362 			goto error;
1363 
1364 		if (parser_read_uint32(&weight_val, tokens[1]) != 0)
1365 			goto error;
1366 		*weight = weight_val;
1367 
1368 		tokens += 2;
1369 		n_tokens -= 2;
1370 	}
1371 
1372 	if (n_tokens)
1373 		goto error;
1374 
1375 	free(s0);
1376 	return 0;
1377 
1378 error:
1379 	free(s0);
1380 	if (is_blank_or_comment)
1381 		*is_blank_or_comment = blank_or_comment;
1382 	return -EINVAL;
1383 }
1384 
1385 static int
1386 pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
1387 			   const char *selector_name,
1388 			   FILE *file,
1389 			   uint32_t *file_line_number)
1390 {
1391 	char *line = NULL;
1392 	uint32_t line_id = 0;
1393 	int status = 0;
1394 
1395 	/* Buffer allocation. */
1396 	line = malloc(MAX_LINE_SIZE);
1397 	if (!line)
1398 		return -ENOMEM;
1399 
1400 	/* File read. */
1401 	for (line_id = 1; ; line_id++) {
1402 		uint32_t group_id, member_id, weight;
1403 		int is_blank_or_comment;
1404 
1405 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1406 			break;
1407 
1408 		status = pipeline_selector_group_member_read(line,
1409 							      &group_id,
1410 							      &member_id,
1411 							      &weight,
1412 							      &is_blank_or_comment);
1413 		if (status) {
1414 			if (is_blank_or_comment)
1415 				continue;
1416 
1417 			goto error;
1418 		}
1419 
1420 		status = rte_swx_ctl_pipeline_selector_group_member_add(p,
1421 			selector_name,
1422 			group_id,
1423 			member_id,
1424 			weight);
1425 		if (status)
1426 			goto error;
1427 	}
1428 
1429 error:
1430 	free(line);
1431 	*file_line_number = line_id;
1432 	return status;
1433 }
1434 
1435 static const char cmd_pipeline_selector_group_member_add_help[] =
1436 "pipeline <pipeline_name> selector <selector_name> group member add <file_name>";
1437 
1438 static void
1439 cmd_pipeline_selector_group_member_add(char **tokens,
1440 	uint32_t n_tokens,
1441 	char *out,
1442 	size_t out_size,
1443 	void *obj __rte_unused)
1444 {
1445 	struct rte_swx_ctl_pipeline *ctl;
1446 	char *pipeline_name, *selector_name, *file_name;
1447 	FILE *file = NULL;
1448 	uint32_t file_line_number = 0;
1449 	int status;
1450 
1451 	if (n_tokens != 8) {
1452 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1453 		return;
1454 	}
1455 
1456 	pipeline_name = tokens[1];
1457 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1458 	if (!ctl) {
1459 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1460 		return;
1461 	}
1462 
1463 	if (strcmp(tokens[2], "selector") != 0) {
1464 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1465 		return;
1466 	}
1467 
1468 	selector_name = tokens[3];
1469 
1470 	if (strcmp(tokens[4], "group") ||
1471 		strcmp(tokens[5], "member") ||
1472 		strcmp(tokens[6], "add")) {
1473 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
1474 		return;
1475 	}
1476 
1477 	file_name = tokens[7];
1478 	file = fopen(file_name, "r");
1479 	if (!file) {
1480 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1481 		return;
1482 	}
1483 
1484 	status = pipeline_selector_group_members_add(ctl,
1485 					    selector_name,
1486 					    file,
1487 					    &file_line_number);
1488 	if (status)
1489 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1490 			 file_name,
1491 			 file_line_number);
1492 
1493 	fclose(file);
1494 }
1495 
1496 static int
1497 pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
1498 			   const char *selector_name,
1499 			   FILE *file,
1500 			   uint32_t *file_line_number)
1501 {
1502 	char *line = NULL;
1503 	uint32_t line_id = 0;
1504 	int status = 0;
1505 
1506 	/* Buffer allocation. */
1507 	line = malloc(MAX_LINE_SIZE);
1508 	if (!line)
1509 		return -ENOMEM;
1510 
1511 	/* File read. */
1512 	for (line_id = 1; ; line_id++) {
1513 		uint32_t group_id, member_id, weight;
1514 		int is_blank_or_comment;
1515 
1516 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1517 			break;
1518 
1519 		status = pipeline_selector_group_member_read(line,
1520 							      &group_id,
1521 							      &member_id,
1522 							      &weight,
1523 							      &is_blank_or_comment);
1524 		if (status) {
1525 			if (is_blank_or_comment)
1526 				continue;
1527 
1528 			goto error;
1529 		}
1530 
1531 		status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
1532 			selector_name,
1533 			group_id,
1534 			member_id);
1535 		if (status)
1536 			goto error;
1537 	}
1538 
1539 error:
1540 	free(line);
1541 	*file_line_number = line_id;
1542 	return status;
1543 }
1544 
1545 static const char cmd_pipeline_selector_group_member_delete_help[] =
1546 "pipeline <pipeline_name> selector <selector_name> group member delete <file_name>";
1547 
1548 static void
1549 cmd_pipeline_selector_group_member_delete(char **tokens,
1550 	uint32_t n_tokens,
1551 	char *out,
1552 	size_t out_size,
1553 	void *obj __rte_unused)
1554 {
1555 	struct rte_swx_ctl_pipeline *ctl;
1556 	char *pipeline_name, *selector_name, *file_name;
1557 	FILE *file = NULL;
1558 	uint32_t file_line_number = 0;
1559 	int status;
1560 
1561 	if (n_tokens != 8) {
1562 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1563 		return;
1564 	}
1565 
1566 	pipeline_name = tokens[1];
1567 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1568 	if (!ctl) {
1569 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1570 		return;
1571 	}
1572 
1573 	if (strcmp(tokens[2], "selector") != 0) {
1574 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1575 		return;
1576 	}
1577 
1578 	selector_name = tokens[3];
1579 
1580 	if (strcmp(tokens[4], "group") ||
1581 		strcmp(tokens[5], "member") ||
1582 		strcmp(tokens[6], "delete")) {
1583 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
1584 		return;
1585 	}
1586 
1587 	file_name = tokens[7];
1588 	file = fopen(file_name, "r");
1589 	if (!file) {
1590 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1591 		return;
1592 	}
1593 
1594 	status = pipeline_selector_group_members_delete(ctl,
1595 					    selector_name,
1596 					    file,
1597 					    &file_line_number);
1598 	if (status)
1599 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1600 			 file_name,
1601 			 file_line_number);
1602 
1603 	fclose(file);
1604 }
1605 
1606 static const char cmd_pipeline_selector_show_help[] =
1607 "pipeline <pipeline_name> selector <selector_name> show [filename]\n";
1608 
1609 static void
1610 cmd_pipeline_selector_show(char **tokens,
1611 	uint32_t n_tokens,
1612 	char *out,
1613 	size_t out_size,
1614 	void *obj __rte_unused)
1615 {
1616 	struct rte_swx_ctl_pipeline *ctl;
1617 	char *pipeline_name, *selector_name;
1618 	FILE *file = NULL;
1619 	int status;
1620 
1621 	if (n_tokens != 5 && n_tokens != 6) {
1622 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1623 		return;
1624 	}
1625 
1626 	pipeline_name = tokens[1];
1627 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1628 	if (!ctl) {
1629 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1630 		return;
1631 	}
1632 
1633 	selector_name = tokens[3];
1634 
1635 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1636 	if (!file) {
1637 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1638 		return;
1639 	}
1640 
1641 	status = rte_swx_ctl_pipeline_selector_fprintf(file, ctl, selector_name);
1642 	if (status)
1643 		snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
1644 
1645 	if (file)
1646 		fclose(file);
1647 }
1648 
1649 static int
1650 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1651 				   const char *learner_name,
1652 				   FILE *file,
1653 				   uint32_t *file_line_number)
1654 {
1655 	char *line = NULL;
1656 	uint32_t line_id = 0;
1657 	int status = 0;
1658 
1659 	/* Buffer allocation. */
1660 	line = malloc(MAX_LINE_SIZE);
1661 	if (!line)
1662 		return -ENOMEM;
1663 
1664 	/* File read. */
1665 	for (line_id = 1; ; line_id++) {
1666 		struct rte_swx_table_entry *entry;
1667 		int is_blank_or_comment;
1668 
1669 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1670 			break;
1671 
1672 		entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1673 									learner_name,
1674 									line,
1675 									&is_blank_or_comment);
1676 		if (!entry) {
1677 			if (is_blank_or_comment)
1678 				continue;
1679 
1680 			status = -EINVAL;
1681 			goto error;
1682 		}
1683 
1684 		status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1685 									learner_name,
1686 									entry);
1687 		table_entry_free(entry);
1688 		if (status)
1689 			goto error;
1690 	}
1691 
1692 error:
1693 	*file_line_number = line_id;
1694 	free(line);
1695 	return status;
1696 }
1697 
1698 static const char cmd_pipeline_learner_default_help[] =
1699 "pipeline <pipeline_name> learner <learner_name> default <file_name>\n";
1700 
1701 static void
1702 cmd_pipeline_learner_default(char **tokens,
1703 			     uint32_t n_tokens,
1704 			     char *out,
1705 			     size_t out_size,
1706 			     void *obj __rte_unused)
1707 {
1708 	struct rte_swx_ctl_pipeline *ctl;
1709 	char *pipeline_name, *learner_name, *file_name;
1710 	FILE *file = NULL;
1711 	uint32_t file_line_number = 0;
1712 	int status;
1713 
1714 	if (n_tokens != 6) {
1715 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1716 		return;
1717 	}
1718 
1719 	pipeline_name = tokens[1];
1720 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1721 	if (!ctl) {
1722 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1723 		return;
1724 	}
1725 
1726 	learner_name = tokens[3];
1727 
1728 	file_name = tokens[5];
1729 	file = fopen(file_name, "r");
1730 	if (!file) {
1731 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1732 		return;
1733 	}
1734 
1735 	status = pipeline_learner_default_entry_add(ctl,
1736 						    learner_name,
1737 						    file,
1738 						    &file_line_number);
1739 	if (status)
1740 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1741 			 file_name,
1742 			 file_line_number);
1743 
1744 	fclose(file);
1745 }
1746 
1747 static const char cmd_pipeline_commit_help[] =
1748 "pipeline <pipeline_name> commit\n";
1749 
1750 static void
1751 cmd_pipeline_commit(char **tokens,
1752 	uint32_t n_tokens,
1753 	char *out,
1754 	size_t out_size,
1755 	void *obj __rte_unused)
1756 {
1757 	struct rte_swx_ctl_pipeline *ctl;
1758 	char *pipeline_name;
1759 	int status;
1760 
1761 	if (n_tokens != 3) {
1762 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1763 		return;
1764 	}
1765 
1766 	pipeline_name = tokens[1];
1767 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1768 	if (!ctl) {
1769 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1770 		return;
1771 	}
1772 
1773 	status = rte_swx_ctl_pipeline_commit(ctl, 1);
1774 	if (status)
1775 		snprintf(out, out_size, "Commit failed. "
1776 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1777 }
1778 
1779 static const char cmd_pipeline_abort_help[] =
1780 "pipeline <pipeline_name> abort\n";
1781 
1782 static void
1783 cmd_pipeline_abort(char **tokens,
1784 	uint32_t n_tokens,
1785 	char *out,
1786 	size_t out_size,
1787 	void *obj __rte_unused)
1788 {
1789 	struct rte_swx_ctl_pipeline *ctl;
1790 	char *pipeline_name;
1791 
1792 	if (n_tokens != 3) {
1793 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1794 		return;
1795 	}
1796 
1797 	pipeline_name = tokens[1];
1798 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1799 	if (!ctl) {
1800 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1801 		return;
1802 	}
1803 
1804 	rte_swx_ctl_pipeline_abort(ctl);
1805 }
1806 
1807 static const char cmd_pipeline_regrd_help[] =
1808 "pipeline <pipeline_name> regrd <register_array_name>\n"
1809 	"index <index>\n"
1810 	" | table <table_name> match <field0> ...\n";
1811 
1812 static void
1813 cmd_pipeline_regrd(char **tokens,
1814 	uint32_t n_tokens,
1815 	char *out,
1816 	size_t out_size,
1817 	void *obj __rte_unused)
1818 {
1819 	struct rte_swx_pipeline *p;
1820 	struct rte_swx_ctl_pipeline *ctl;
1821 	const char *pipeline_name, *name;
1822 	uint64_t value;
1823 	int status;
1824 
1825 	if (n_tokens < 5) {
1826 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1827 		return;
1828 	}
1829 
1830 	pipeline_name = tokens[1];
1831 	p = rte_swx_pipeline_find(pipeline_name);
1832 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1833 	if (!p || !ctl) {
1834 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1835 		return;
1836 	}
1837 
1838 	if (strcmp(tokens[2], "regrd")) {
1839 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
1840 		return;
1841 	}
1842 
1843 	name = tokens[3];
1844 
1845 	/* index. */
1846 	if (!strcmp(tokens[4], "index")) {
1847 		uint32_t idx = 0;
1848 
1849 		if (n_tokens != 6) {
1850 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1851 			return;
1852 		}
1853 
1854 		if (parser_read_uint32(&idx, tokens[5])) {
1855 			snprintf(out, out_size, MSG_ARG_INVALID, "index");
1856 			return;
1857 		}
1858 
1859 		status = rte_swx_ctl_pipeline_regarray_read(p, name, idx, &value);
1860 		if (status) {
1861 			snprintf(out, out_size, "Command failed.\n");
1862 			return;
1863 		}
1864 
1865 		snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1866 		return;
1867 	}
1868 
1869 	/* table. */
1870 	if (!strcmp(tokens[4], "table")) {
1871 		struct rte_swx_table_entry *entry;
1872 		char *table_name;
1873 
1874 		if (n_tokens < 8) {
1875 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1876 			return;
1877 		}
1878 
1879 		table_name = tokens[5];
1880 
1881 		if (strcmp(tokens[6], "match")) {
1882 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
1883 			return;
1884 		}
1885 
1886 		entry = parse_table_entry(ctl, table_name, &tokens[6], n_tokens - 6);
1887 		if (!entry) {
1888 			snprintf(out, out_size, "Invalid match tokens.\n");
1889 			return;
1890 		}
1891 
1892 		status = rte_swx_ctl_pipeline_regarray_read_with_key(p,
1893 								     name,
1894 								     table_name,
1895 								     entry->key,
1896 								     &value);
1897 		table_entry_free(entry);
1898 		if (status) {
1899 			snprintf(out, out_size, "Command failed.\n");
1900 			return;
1901 		}
1902 
1903 		snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1904 		return;
1905 	}
1906 
1907 	/* anything else. */
1908 	snprintf(out, out_size, "Invalid token %s\n.", tokens[4]);
1909 	return;
1910 }
1911 
1912 static const char cmd_pipeline_regwr_help[] =
1913 "pipeline <pipeline_name> regwr <register_array_name> value <value>\n"
1914 	"index <index>\n"
1915 	" | table <table_name> match <field0> ...\n";
1916 
1917 static void
1918 cmd_pipeline_regwr(char **tokens,
1919 	uint32_t n_tokens,
1920 	char *out,
1921 	size_t out_size,
1922 	void *obj __rte_unused)
1923 {
1924 	struct rte_swx_pipeline *p;
1925 	struct rte_swx_ctl_pipeline *ctl;
1926 	const char *pipeline_name, *name;
1927 	uint64_t value = 0;
1928 	int status;
1929 
1930 	if (n_tokens < 7) {
1931 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1932 		return;
1933 	}
1934 
1935 	pipeline_name = tokens[1];
1936 	p = rte_swx_pipeline_find(pipeline_name);
1937 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1938 	if (!p || !ctl) {
1939 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1940 		return;
1941 	}
1942 
1943 	if (strcmp(tokens[2], "regwr")) {
1944 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
1945 		return;
1946 	}
1947 
1948 	name = tokens[3];
1949 
1950 	if (strcmp(tokens[4], "value")) {
1951 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "value");
1952 		return;
1953 	}
1954 
1955 	if (parser_read_uint64(&value, tokens[5])) {
1956 		snprintf(out, out_size, MSG_ARG_INVALID, "value");
1957 		return;
1958 	}
1959 
1960 	/* index. */
1961 	if (!strcmp(tokens[6], "index")) {
1962 		uint32_t idx = 0;
1963 
1964 		if (n_tokens != 8) {
1965 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1966 			return;
1967 		}
1968 
1969 		if (parser_read_uint32(&idx, tokens[7])) {
1970 			snprintf(out, out_size, MSG_ARG_INVALID, "index");
1971 			return;
1972 		}
1973 
1974 		status = rte_swx_ctl_pipeline_regarray_write(p, name, idx, value);
1975 		if (status) {
1976 			snprintf(out, out_size, "Command failed.\n");
1977 			return;
1978 		}
1979 
1980 		snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1981 		return;
1982 	}
1983 
1984 	/* table. */
1985 	if (!strcmp(tokens[6], "table")) {
1986 		struct rte_swx_table_entry *entry;
1987 		char *table_name;
1988 
1989 		if (n_tokens < 10) {
1990 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1991 			return;
1992 		}
1993 
1994 		table_name = tokens[7];
1995 
1996 		if (strcmp(tokens[8], "match")) {
1997 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
1998 			return;
1999 		}
2000 
2001 		entry = parse_table_entry(ctl, table_name, &tokens[8], n_tokens - 8);
2002 		if (!entry) {
2003 			snprintf(out, out_size, "Invalid match tokens.\n");
2004 			return;
2005 		}
2006 
2007 		status = rte_swx_ctl_pipeline_regarray_write_with_key(p,
2008 								      name,
2009 								      table_name,
2010 								      entry->key,
2011 								      value);
2012 		table_entry_free(entry);
2013 		if (status) {
2014 			snprintf(out, out_size, "Command failed.\n");
2015 			return;
2016 		}
2017 
2018 		return;
2019 	}
2020 
2021 	/* anything else. */
2022 	snprintf(out, out_size, "Invalid token %s\n.", tokens[6]);
2023 	return;
2024 }
2025 
2026 static const char cmd_pipeline_meter_profile_add_help[] =
2027 "pipeline <pipeline_name> meter profile <profile_name> add "
2028 	"cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
2029 
2030 static void
2031 cmd_pipeline_meter_profile_add(char **tokens,
2032 	uint32_t n_tokens,
2033 	char *out,
2034 	size_t out_size,
2035 	void *obj __rte_unused)
2036 {
2037 	struct rte_meter_trtcm_params params;
2038 	struct rte_swx_pipeline *p;
2039 	const char *profile_name;
2040 	int status;
2041 
2042 	if (n_tokens != 14) {
2043 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2044 		return;
2045 	}
2046 
2047 	p = rte_swx_pipeline_find(tokens[1]);
2048 	if (!p) {
2049 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2050 		return;
2051 	}
2052 
2053 	if (strcmp(tokens[2], "meter")) {
2054 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2055 		return;
2056 	}
2057 
2058 	if (strcmp(tokens[3], "profile")) {
2059 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2060 		return;
2061 	}
2062 
2063 	profile_name = tokens[4];
2064 
2065 	if (strcmp(tokens[5], "add")) {
2066 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
2067 		return;
2068 	}
2069 
2070 	if (strcmp(tokens[6], "cir")) {
2071 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
2072 		return;
2073 	}
2074 
2075 	if (parser_read_uint64(&params.cir, tokens[7])) {
2076 		snprintf(out, out_size, MSG_ARG_INVALID, "cir");
2077 		return;
2078 	}
2079 
2080 	if (strcmp(tokens[8], "pir")) {
2081 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
2082 		return;
2083 	}
2084 
2085 	if (parser_read_uint64(&params.pir, tokens[9])) {
2086 		snprintf(out, out_size, MSG_ARG_INVALID, "pir");
2087 		return;
2088 	}
2089 
2090 	if (strcmp(tokens[10], "cbs")) {
2091 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
2092 		return;
2093 	}
2094 
2095 	if (parser_read_uint64(&params.cbs, tokens[11])) {
2096 		snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
2097 		return;
2098 	}
2099 
2100 	if (strcmp(tokens[12], "pbs")) {
2101 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
2102 		return;
2103 	}
2104 
2105 	if (parser_read_uint64(&params.pbs, tokens[13])) {
2106 		snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
2107 		return;
2108 	}
2109 
2110 	status = rte_swx_ctl_meter_profile_add(p, profile_name, &params);
2111 	if (status) {
2112 		snprintf(out, out_size, "Command failed.\n");
2113 		return;
2114 	}
2115 }
2116 
2117 static const char cmd_pipeline_meter_profile_delete_help[] =
2118 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
2119 
2120 static void
2121 cmd_pipeline_meter_profile_delete(char **tokens,
2122 	uint32_t n_tokens,
2123 	char *out,
2124 	size_t out_size,
2125 	void *obj __rte_unused)
2126 {
2127 	struct rte_swx_pipeline *p;
2128 	const char *profile_name;
2129 	int status;
2130 
2131 	if (n_tokens != 6) {
2132 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2133 		return;
2134 	}
2135 
2136 	p = rte_swx_pipeline_find(tokens[1]);
2137 	if (!p) {
2138 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2139 		return;
2140 	}
2141 
2142 	if (strcmp(tokens[2], "meter")) {
2143 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2144 		return;
2145 	}
2146 
2147 	if (strcmp(tokens[3], "profile")) {
2148 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2149 		return;
2150 	}
2151 
2152 	profile_name = tokens[4];
2153 
2154 	if (strcmp(tokens[5], "delete")) {
2155 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
2156 		return;
2157 	}
2158 
2159 	status = rte_swx_ctl_meter_profile_delete(p, profile_name);
2160 	if (status) {
2161 		snprintf(out, out_size, "Command failed.\n");
2162 		return;
2163 	}
2164 }
2165 
2166 static const char cmd_pipeline_meter_reset_help[] =
2167 "pipeline <pipeline_name> meter <meter_array_name> reset\n"
2168 	"index from <index0> to <index1>\n"
2169 	" | table <table_name> match <field0> ...\n";
2170 
2171 static void
2172 cmd_pipeline_meter_reset(char **tokens,
2173 	uint32_t n_tokens,
2174 	char *out,
2175 	size_t out_size,
2176 	void *obj __rte_unused)
2177 {
2178 	struct rte_swx_pipeline *p;
2179 	struct rte_swx_ctl_pipeline *ctl;
2180 	const char *pipeline_name, *name;
2181 
2182 	if (n_tokens < 6) {
2183 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2184 		return;
2185 	}
2186 
2187 	pipeline_name = tokens[1];
2188 	p = rte_swx_pipeline_find(pipeline_name);
2189 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
2190 	if (!p || !ctl) {
2191 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2192 		return;
2193 	}
2194 
2195 	if (strcmp(tokens[2], "meter")) {
2196 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2197 		return;
2198 	}
2199 
2200 	name = tokens[3];
2201 
2202 	if (strcmp(tokens[4], "reset")) {
2203 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
2204 		return;
2205 	}
2206 
2207 	/* index. */
2208 	if (!strcmp(tokens[5], "index")) {
2209 		uint32_t idx0 = 0, idx1 = 0;
2210 
2211 		if (n_tokens != 10) {
2212 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2213 			return;
2214 		}
2215 
2216 		if (strcmp(tokens[6], "from")) {
2217 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2218 			return;
2219 		}
2220 
2221 		if (parser_read_uint32(&idx0, tokens[7])) {
2222 			snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2223 			return;
2224 		}
2225 
2226 		if (strcmp(tokens[8], "to")) {
2227 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2228 			return;
2229 		}
2230 
2231 		if (parser_read_uint32(&idx1, tokens[9]) || (idx1 < idx0)) {
2232 			snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2233 			return;
2234 		}
2235 
2236 		for ( ; idx0 <= idx1; idx0++) {
2237 			int status;
2238 
2239 			status = rte_swx_ctl_meter_reset(p, name, idx0);
2240 			if (status) {
2241 				snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2242 				return;
2243 			}
2244 		}
2245 
2246 		return;
2247 	}
2248 
2249 	/* table. */
2250 	if (!strcmp(tokens[5], "table")) {
2251 		struct rte_swx_table_entry *entry;
2252 		char *table_name;
2253 		int status;
2254 
2255 		if (n_tokens < 9) {
2256 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2257 			return;
2258 		}
2259 
2260 		table_name = tokens[6];
2261 
2262 		if (strcmp(tokens[7], "match")) {
2263 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2264 			return;
2265 		}
2266 
2267 		entry = parse_table_entry(ctl, table_name, &tokens[7], n_tokens - 7);
2268 		if (!entry) {
2269 			snprintf(out, out_size, "Invalid match tokens.\n");
2270 			return;
2271 		}
2272 
2273 		status = rte_swx_ctl_meter_reset_with_key(p, name, table_name, entry->key);
2274 		table_entry_free(entry);
2275 		if (status) {
2276 			snprintf(out, out_size, "Command failed.\n");
2277 			return;
2278 		}
2279 
2280 		return;
2281 	}
2282 
2283 	/* anything else. */
2284 	snprintf(out, out_size, "Invalid token %s\n.", tokens[5]);
2285 	return;
2286 }
2287 
2288 static const char cmd_pipeline_meter_set_help[] =
2289 "pipeline <pipeline_name> meter <meter_array_name> set profile <profile_name>\n"
2290 	"index from <index0> to <index1>\n"
2291 	" | table <table_name> match <field0> ...\n";
2292 
2293 static void
2294 cmd_pipeline_meter_set(char **tokens,
2295 	uint32_t n_tokens,
2296 	char *out,
2297 	size_t out_size,
2298 	void *obj __rte_unused)
2299 {
2300 	struct rte_swx_pipeline *p;
2301 	struct rte_swx_ctl_pipeline *ctl;
2302 	const char *pipeline_name, *name, *profile_name;
2303 
2304 	if (n_tokens < 8) {
2305 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2306 		return;
2307 	}
2308 
2309 	pipeline_name = tokens[1];
2310 	p = rte_swx_pipeline_find(pipeline_name);
2311 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
2312 	if (!p || !ctl) {
2313 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2314 		return;
2315 	}
2316 
2317 	if (strcmp(tokens[2], "meter")) {
2318 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2319 		return;
2320 	}
2321 
2322 	name = tokens[3];
2323 
2324 	if (strcmp(tokens[4], "set")) {
2325 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
2326 		return;
2327 	}
2328 
2329 	if (strcmp(tokens[5], "profile")) {
2330 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2331 		return;
2332 	}
2333 
2334 	profile_name = tokens[6];
2335 
2336 	/* index. */
2337 	if (!strcmp(tokens[7], "index")) {
2338 		uint32_t idx0 = 0, idx1 = 0;
2339 
2340 		if (n_tokens != 12) {
2341 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2342 			return;
2343 		}
2344 
2345 		if (strcmp(tokens[8], "from")) {
2346 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2347 			return;
2348 		}
2349 
2350 		if (parser_read_uint32(&idx0, tokens[9])) {
2351 			snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2352 			return;
2353 		}
2354 
2355 		if (strcmp(tokens[10], "to")) {
2356 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2357 			return;
2358 		}
2359 
2360 		if (parser_read_uint32(&idx1, tokens[11]) || (idx1 < idx0)) {
2361 			snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2362 			return;
2363 		}
2364 
2365 		for ( ; idx0 <= idx1; idx0++) {
2366 			int status;
2367 
2368 			status = rte_swx_ctl_meter_set(p, name, idx0, profile_name);
2369 			if (status) {
2370 				snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2371 				return;
2372 			}
2373 		}
2374 
2375 		return;
2376 	}
2377 
2378 	/* table. */
2379 	if (!strcmp(tokens[7], "table")) {
2380 		struct rte_swx_table_entry *entry;
2381 		char *table_name;
2382 		int status;
2383 
2384 		if (n_tokens < 11) {
2385 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2386 			return;
2387 		}
2388 
2389 		table_name = tokens[8];
2390 
2391 		if (strcmp(tokens[9], "match")) {
2392 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2393 			return;
2394 		}
2395 
2396 		entry = parse_table_entry(ctl, table_name, &tokens[9], n_tokens - 9);
2397 		if (!entry) {
2398 			snprintf(out, out_size, "Invalid match tokens.\n");
2399 			return;
2400 		}
2401 
2402 		status = rte_swx_ctl_meter_set_with_key(p,
2403 							name,
2404 							table_name,
2405 							entry->key,
2406 							profile_name);
2407 		table_entry_free(entry);
2408 		if (status) {
2409 			snprintf(out, out_size, "Command failed.\n");
2410 			return;
2411 		}
2412 
2413 		return;
2414 	}
2415 
2416 	/* anything else. */
2417 	snprintf(out, out_size, "Invalid token %s\n.", tokens[7]);
2418 	return;
2419 }
2420 
2421 static const char cmd_pipeline_meter_stats_help[] =
2422 "pipeline <pipeline_name> meter <meter_array_name> stats\n"
2423 	"index from <index0> to <index1>\n"
2424 	" | table <table_name> match <field0> ...\n";
2425 
2426 static void
2427 cmd_pipeline_meter_stats(char **tokens,
2428 	uint32_t n_tokens,
2429 	char *out,
2430 	size_t out_size,
2431 	void *obj __rte_unused)
2432 {
2433 	struct rte_swx_ctl_meter_stats stats;
2434 	struct rte_swx_pipeline *p;
2435 	struct rte_swx_ctl_pipeline *ctl;
2436 	const char *pipeline_name, *name;
2437 
2438 	if (n_tokens < 6) {
2439 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2440 		return;
2441 	}
2442 
2443 	pipeline_name = tokens[1];
2444 	p = rte_swx_pipeline_find(pipeline_name);
2445 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
2446 	if (!p || !ctl) {
2447 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2448 		return;
2449 	}
2450 
2451 	if (strcmp(tokens[2], "meter")) {
2452 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2453 		return;
2454 	}
2455 
2456 	name = tokens[3];
2457 
2458 	if (strcmp(tokens[4], "stats")) {
2459 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2460 		return;
2461 	}
2462 
2463 	/* index. */
2464 	if (!strcmp(tokens[5], "index")) {
2465 		uint32_t idx0 = 0, idx1 = 0;
2466 
2467 		if (n_tokens != 10) {
2468 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2469 			return;
2470 		}
2471 
2472 		if (strcmp(tokens[6], "from")) {
2473 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2474 			return;
2475 		}
2476 
2477 		if (parser_read_uint32(&idx0, tokens[7])) {
2478 			snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2479 			return;
2480 		}
2481 
2482 		if (strcmp(tokens[8], "to")) {
2483 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2484 			return;
2485 		}
2486 
2487 		if (parser_read_uint32(&idx1, tokens[9]) || (idx1 < idx0)) {
2488 			snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2489 			return;
2490 		}
2491 
2492 		/* Table header. */
2493 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2494 			 "-------",
2495 			 "----------------", "----------------", "----------------",
2496 			 "----------------", "----------------", "----------------");
2497 		out_size -= strlen(out);
2498 		out += strlen(out);
2499 
2500 		snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2501 			 "METER #",
2502 			 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2503 			 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2504 		out_size -= strlen(out);
2505 		out += strlen(out);
2506 
2507 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2508 			 "-------",
2509 			 "----------------", "----------------", "----------------",
2510 			 "----------------", "----------------", "----------------");
2511 		out_size -= strlen(out);
2512 		out += strlen(out);
2513 
2514 		/* Table rows. */
2515 		for ( ; idx0 <= idx1; idx0++) {
2516 			int status;
2517 
2518 			status = rte_swx_ctl_meter_stats_read(p, name, idx0, &stats);
2519 			if (status) {
2520 				snprintf(out, out_size, "Meter stats error at index %u.\n", idx0);
2521 				out_size -= strlen(out);
2522 				out += strlen(out);
2523 				return;
2524 			}
2525 
2526 			snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2527 				 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2528 				 idx0,
2529 				 stats.n_pkts[RTE_COLOR_GREEN],
2530 				 stats.n_pkts[RTE_COLOR_YELLOW],
2531 				 stats.n_pkts[RTE_COLOR_RED],
2532 				 stats.n_bytes[RTE_COLOR_GREEN],
2533 				 stats.n_bytes[RTE_COLOR_YELLOW],
2534 				 stats.n_bytes[RTE_COLOR_RED]);
2535 			out_size -= strlen(out);
2536 			out += strlen(out);
2537 		}
2538 
2539 		return;
2540 	}
2541 
2542 	/* table. */
2543 	if (!strcmp(tokens[5], "table")) {
2544 		struct rte_swx_table_entry *entry;
2545 		char *table_name;
2546 		int status;
2547 
2548 		if (n_tokens < 9) {
2549 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2550 			return;
2551 		}
2552 
2553 		table_name = tokens[6];
2554 
2555 		if (strcmp(tokens[7], "match")) {
2556 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2557 			return;
2558 		}
2559 
2560 		entry = parse_table_entry(ctl, table_name, &tokens[7], n_tokens - 7);
2561 		if (!entry) {
2562 			snprintf(out, out_size, "Invalid match tokens.\n");
2563 			return;
2564 		}
2565 
2566 		status = rte_swx_ctl_meter_stats_read_with_key(p,
2567 							name,
2568 							table_name,
2569 							entry->key,
2570 							&stats);
2571 		table_entry_free(entry);
2572 		if (status) {
2573 			snprintf(out, out_size, "Command failed.\n");
2574 			return;
2575 		}
2576 
2577 		/* Table header. */
2578 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2579 			 "-------",
2580 			 "----------------", "----------------", "----------------",
2581 			 "----------------", "----------------", "----------------");
2582 		out_size -= strlen(out);
2583 		out += strlen(out);
2584 
2585 		snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2586 			 "METER #",
2587 			 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2588 			 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2589 		out_size -= strlen(out);
2590 		out += strlen(out);
2591 
2592 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2593 			 "-------",
2594 			 "----------------", "----------------", "----------------",
2595 			 "----------------", "----------------", "----------------");
2596 		out_size -= strlen(out);
2597 		out += strlen(out);
2598 
2599 		/* Table row. */
2600 		snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2601 			 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2602 			 0,
2603 			 stats.n_pkts[RTE_COLOR_GREEN],
2604 			 stats.n_pkts[RTE_COLOR_YELLOW],
2605 			 stats.n_pkts[RTE_COLOR_RED],
2606 			 stats.n_bytes[RTE_COLOR_GREEN],
2607 			 stats.n_bytes[RTE_COLOR_YELLOW],
2608 			 stats.n_bytes[RTE_COLOR_RED]);
2609 		out_size -= strlen(out);
2610 		out += strlen(out);
2611 
2612 		return;
2613 	}
2614 
2615 	/* anything else. */
2616 	snprintf(out, out_size, "Invalid token %s\n.", tokens[5]);
2617 	return;
2618 }
2619 
2620 static const char cmd_pipeline_rss_help[] =
2621 "pipeline <pipeline_name> rss <rss_obj_name> key <key_byte0> ...\n";
2622 
2623 static void
2624 cmd_pipeline_rss(char **tokens,
2625 	uint32_t n_tokens,
2626 	char *out,
2627 	size_t out_size,
2628 	void *obj __rte_unused)
2629 {
2630 	uint8_t rss_key[CMD_MAX_TOKENS];
2631 	struct rte_swx_pipeline *p;
2632 	const char *rss_obj_name;
2633 	uint32_t rss_key_size, i;
2634 	int status;
2635 
2636 	if (n_tokens < 6) {
2637 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2638 		return;
2639 	}
2640 
2641 	p = rte_swx_pipeline_find(tokens[1]);
2642 	if (!p) {
2643 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2644 		return;
2645 	}
2646 
2647 	if (strcmp(tokens[2], "rss")) {
2648 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
2649 		return;
2650 	}
2651 
2652 	rss_obj_name = tokens[3];
2653 
2654 	if (strcmp(tokens[4], "key")) {
2655 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
2656 		return;
2657 	}
2658 
2659 	tokens += 5;
2660 	n_tokens -= 5;
2661 	rss_key_size = n_tokens;
2662 
2663 	for (i = 0; i < rss_key_size; i++) {
2664 		uint32_t key_byte;
2665 
2666 		if (parser_read_uint32(&key_byte, tokens[i]) || (key_byte >= UINT8_MAX)) {
2667 			snprintf(out, out_size, MSG_ARG_INVALID, "key byte");
2668 			return;
2669 		}
2670 
2671 		rss_key[i] = (uint8_t)key_byte;
2672 	}
2673 
2674 	status = rte_swx_ctl_pipeline_rss_key_write(p, rss_obj_name, rss_key_size, rss_key);
2675 	if (status) {
2676 		snprintf(out, out_size, "Command failed.\n");
2677 		return;
2678 	}
2679 }
2680 
2681 static const char cmd_pipeline_stats_help[] =
2682 "pipeline <pipeline_name> stats\n";
2683 
2684 static void
2685 cmd_pipeline_stats(char **tokens,
2686 	uint32_t n_tokens,
2687 	char *out,
2688 	size_t out_size,
2689 	void *obj __rte_unused)
2690 {
2691 	struct rte_swx_ctl_pipeline_info info;
2692 	struct rte_swx_pipeline *p;
2693 	uint32_t i;
2694 	int status;
2695 
2696 	if (n_tokens != 3) {
2697 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2698 		return;
2699 	}
2700 
2701 	p = rte_swx_pipeline_find(tokens[1]);
2702 	if (!p) {
2703 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2704 		return;
2705 	}
2706 
2707 	if (strcmp(tokens[2], "stats")) {
2708 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2709 		return;
2710 	}
2711 
2712 	status = rte_swx_ctl_pipeline_info_get(p, &info);
2713 	if (status) {
2714 		snprintf(out, out_size, "Pipeline info get error.");
2715 		return;
2716 	}
2717 
2718 	snprintf(out, out_size, "Input ports:\n");
2719 	out_size -= strlen(out);
2720 	out += strlen(out);
2721 
2722 	for (i = 0; i < info.n_ports_in; i++) {
2723 		struct rte_swx_port_in_stats stats;
2724 
2725 		rte_swx_ctl_pipeline_port_in_stats_read(p, i, &stats);
2726 
2727 		snprintf(out, out_size, "\tPort %u:"
2728 			" packets %" PRIu64
2729 			" bytes %" PRIu64
2730 			" empty %" PRIu64 "\n",
2731 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
2732 		out_size -= strlen(out);
2733 		out += strlen(out);
2734 	}
2735 
2736 	snprintf(out, out_size, "\nOutput ports:\n");
2737 	out_size -= strlen(out);
2738 	out += strlen(out);
2739 
2740 	for (i = 0; i < info.n_ports_out; i++) {
2741 		struct rte_swx_port_out_stats stats;
2742 
2743 		rte_swx_ctl_pipeline_port_out_stats_read(p, i, &stats);
2744 
2745 		if (i != info.n_ports_out - 1)
2746 			snprintf(out, out_size, "\tPort %u:", i);
2747 		else
2748 			snprintf(out, out_size, "\tDROP:");
2749 
2750 		out_size -= strlen(out);
2751 		out += strlen(out);
2752 
2753 		snprintf(out,
2754 			out_size,
2755 			" packets %" PRIu64
2756 			" bytes %" PRIu64
2757 			" packets dropped %" PRIu64
2758 			" bytes dropped %" PRIu64
2759 			" clone %" PRIu64
2760 			" clonerr %" PRIu64 "\n",
2761 			stats.n_pkts,
2762 			stats.n_bytes,
2763 			stats.n_pkts_drop,
2764 			stats.n_bytes_drop,
2765 			stats.n_pkts_clone,
2766 			stats.n_pkts_clone_err);
2767 
2768 		out_size -= strlen(out);
2769 		out += strlen(out);
2770 	}
2771 
2772 	snprintf(out, out_size, "\nTables:\n");
2773 	out_size -= strlen(out);
2774 	out += strlen(out);
2775 
2776 	for (i = 0; i < info.n_tables; i++) {
2777 		struct rte_swx_ctl_table_info table_info;
2778 		uint64_t n_pkts_action[info.n_actions];
2779 		struct rte_swx_table_stats stats = {
2780 			.n_pkts_hit = 0,
2781 			.n_pkts_miss = 0,
2782 			.n_pkts_action = n_pkts_action,
2783 		};
2784 		uint32_t j;
2785 
2786 		status = rte_swx_ctl_table_info_get(p, i, &table_info);
2787 		if (status) {
2788 			snprintf(out, out_size, "Table info get error.");
2789 			return;
2790 		}
2791 
2792 		status = rte_swx_ctl_pipeline_table_stats_read(p, table_info.name, &stats);
2793 		if (status) {
2794 			snprintf(out, out_size, "Table stats read error.");
2795 			return;
2796 		}
2797 
2798 		snprintf(out, out_size, "\tTable %s:\n"
2799 			"\t\tHit (packets): %" PRIu64 "\n"
2800 			"\t\tMiss (packets): %" PRIu64 "\n",
2801 			table_info.name,
2802 			stats.n_pkts_hit,
2803 			stats.n_pkts_miss);
2804 		out_size -= strlen(out);
2805 		out += strlen(out);
2806 
2807 		for (j = 0; j < info.n_actions; j++) {
2808 			struct rte_swx_ctl_action_info action_info;
2809 
2810 			status = rte_swx_ctl_action_info_get(p, j, &action_info);
2811 			if (status) {
2812 				snprintf(out, out_size, "Action info get error.");
2813 				return;
2814 			}
2815 
2816 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2817 				action_info.name,
2818 				stats.n_pkts_action[j]);
2819 			out_size -= strlen(out);
2820 			out += strlen(out);
2821 		}
2822 	}
2823 
2824 	snprintf(out, out_size, "\nLearner tables:\n");
2825 	out_size -= strlen(out);
2826 	out += strlen(out);
2827 
2828 	for (i = 0; i < info.n_learners; i++) {
2829 		struct rte_swx_ctl_learner_info learner_info;
2830 		uint64_t n_pkts_action[info.n_actions];
2831 		struct rte_swx_learner_stats stats = {
2832 			.n_pkts_hit = 0,
2833 			.n_pkts_miss = 0,
2834 			.n_pkts_action = n_pkts_action,
2835 		};
2836 		uint32_t j;
2837 
2838 		status = rte_swx_ctl_learner_info_get(p, i, &learner_info);
2839 		if (status) {
2840 			snprintf(out, out_size, "Learner table info get error.");
2841 			return;
2842 		}
2843 
2844 		status = rte_swx_ctl_pipeline_learner_stats_read(p, learner_info.name, &stats);
2845 		if (status) {
2846 			snprintf(out, out_size, "Learner table stats read error.");
2847 			return;
2848 		}
2849 
2850 		snprintf(out, out_size, "\tLearner table %s:\n"
2851 			"\t\tHit (packets): %" PRIu64 "\n"
2852 			"\t\tMiss (packets): %" PRIu64 "\n"
2853 			"\t\tLearn OK (packets): %" PRIu64 "\n"
2854 			"\t\tLearn error (packets): %" PRIu64 "\n"
2855 			"\t\tRearm (packets): %" PRIu64 "\n"
2856 			"\t\tForget (packets): %" PRIu64 "\n",
2857 			learner_info.name,
2858 			stats.n_pkts_hit,
2859 			stats.n_pkts_miss,
2860 			stats.n_pkts_learn_ok,
2861 			stats.n_pkts_learn_err,
2862 			stats.n_pkts_rearm,
2863 			stats.n_pkts_forget);
2864 		out_size -= strlen(out);
2865 		out += strlen(out);
2866 
2867 		for (j = 0; j < info.n_actions; j++) {
2868 			struct rte_swx_ctl_action_info action_info;
2869 
2870 			status = rte_swx_ctl_action_info_get(p, j, &action_info);
2871 			if (status) {
2872 				snprintf(out, out_size, "Action info get error.");
2873 				return;
2874 			}
2875 
2876 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2877 				action_info.name,
2878 				stats.n_pkts_action[j]);
2879 			out_size -= strlen(out);
2880 			out += strlen(out);
2881 		}
2882 	}
2883 }
2884 
2885 static const char cmd_pipeline_mirror_session_help[] =
2886 "pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow "
2887 "truncate <truncation_length>\n";
2888 
2889 static void
2890 cmd_pipeline_mirror_session(char **tokens,
2891 	uint32_t n_tokens,
2892 	char *out,
2893 	size_t out_size,
2894 	void *obj __rte_unused)
2895 {
2896 	struct rte_swx_pipeline_mirroring_session_params params;
2897 	struct rte_swx_pipeline *p;
2898 	uint32_t session_id = 0;
2899 	int status;
2900 
2901 	if (n_tokens != 11) {
2902 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2903 		return;
2904 	}
2905 
2906 	if (strcmp(tokens[0], "pipeline")) {
2907 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2908 		return;
2909 	}
2910 
2911 	p = rte_swx_pipeline_find(tokens[1]);
2912 	if (!p) {
2913 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2914 		return;
2915 	}
2916 
2917 	if (strcmp(tokens[2], "mirror")) {
2918 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2919 		return;
2920 	}
2921 
2922 	if (strcmp(tokens[3], "session")) {
2923 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "session");
2924 		return;
2925 	}
2926 
2927 	if (parser_read_uint32(&session_id, tokens[4])) {
2928 		snprintf(out, out_size, MSG_ARG_INVALID, "session_id");
2929 		return;
2930 	}
2931 
2932 	if (strcmp(tokens[5], "port")) {
2933 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2934 		return;
2935 	}
2936 
2937 	if (parser_read_uint32(&params.port_id, tokens[6])) {
2938 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2939 		return;
2940 	}
2941 
2942 	if (strcmp(tokens[7], "clone")) {
2943 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "clone");
2944 		return;
2945 	}
2946 
2947 	if (!strcmp(tokens[8], "fast"))
2948 		params.fast_clone = 1;
2949 	else if (!strcmp(tokens[8], "slow"))
2950 		params.fast_clone = 0;
2951 	else {
2952 		snprintf(out, out_size, MSG_ARG_INVALID, "clone");
2953 		return;
2954 	}
2955 
2956 	if (strcmp(tokens[9], "truncate")) {
2957 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "truncate");
2958 		return;
2959 	}
2960 
2961 	if (parser_read_uint32(&params.truncation_length, tokens[10])) {
2962 		snprintf(out, out_size, MSG_ARG_INVALID, "truncation_length");
2963 		return;
2964 	}
2965 
2966 	status = rte_swx_ctl_pipeline_mirroring_session_set(p, session_id, &params);
2967 	if (status) {
2968 		snprintf(out, out_size, "Command failed!\n");
2969 		return;
2970 	}
2971 }
2972 
2973 static const char cmd_ipsec_create_help[] =
2974 "ipsec <ipsec_instance_name> create "
2975 "in <ring_in_name> out <ring_out_name> "
2976 "cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
2977 "bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
2978 "samax <n_sa_max> "
2979 "numa <numa_node>\n";
2980 
2981 static void
2982 cmd_ipsec_create(char **tokens,
2983 		 uint32_t n_tokens,
2984 		 char *out,
2985 		 size_t out_size,
2986 		 void *obj __rte_unused)
2987 {
2988 	struct rte_swx_ipsec_params p;
2989 	struct rte_swx_ipsec *ipsec;
2990 	char *ipsec_instance_name;
2991 	uint32_t numa_node;
2992 	int status;
2993 
2994 	if (n_tokens != 20) {
2995 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2996 		return;
2997 	}
2998 
2999 	ipsec_instance_name = tokens[1];
3000 
3001 	if (strcmp(tokens[2], "create")) {
3002 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
3003 		return;
3004 	}
3005 
3006 	if (strcmp(tokens[3], "in")) {
3007 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
3008 		return;
3009 	}
3010 
3011 	p.ring_in_name = tokens[4];
3012 
3013 	if (strcmp(tokens[5], "out")) {
3014 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
3015 		return;
3016 	}
3017 
3018 	p.ring_out_name = tokens[6];
3019 
3020 	if (strcmp(tokens[7], "cryptodev")) {
3021 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
3022 		return;
3023 	}
3024 
3025 	p.crypto_dev_name = tokens[8];
3026 
3027 	if (strcmp(tokens[9], "cryptoq")) {
3028 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
3029 		return;
3030 	}
3031 
3032 	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
3033 		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
3034 		return;
3035 	}
3036 
3037 	if (strcmp(tokens[11], "bsz")) {
3038 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
3039 		return;
3040 	}
3041 
3042 	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
3043 		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
3044 		return;
3045 	}
3046 
3047 	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
3048 		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
3049 		return;
3050 	}
3051 
3052 	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
3053 		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
3054 		return;
3055 	}
3056 
3057 	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
3058 		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
3059 		return;
3060 	}
3061 
3062 	if (strcmp(tokens[16], "samax")) {
3063 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
3064 		return;
3065 	}
3066 
3067 	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
3068 		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
3069 		return;
3070 	}
3071 
3072 	if (strcmp(tokens[18], "numa")) {
3073 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
3074 		return;
3075 	}
3076 
3077 	if (parser_read_uint32(&numa_node, tokens[19])) {
3078 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
3079 		return;
3080 	}
3081 
3082 	status = rte_swx_ipsec_create(&ipsec,
3083 				      ipsec_instance_name,
3084 				      &p,
3085 				      (int)numa_node);
3086 	if (status)
3087 		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
3088 }
3089 
3090 static const char cmd_ipsec_sa_add_help[] =
3091 "ipsec <ipsec_instance_name> sa add <file_name>\n";
3092 
3093 static void
3094 cmd_ipsec_sa_add(char **tokens,
3095 		 uint32_t n_tokens,
3096 		 char *out,
3097 		 size_t out_size,
3098 		 void *obj __rte_unused)
3099 {
3100 	struct rte_swx_ipsec *ipsec;
3101 	char *ipsec_instance_name, *file_name, *line = NULL;
3102 	FILE *file = NULL;
3103 	uint32_t line_id = 0;
3104 
3105 	if (n_tokens != 5) {
3106 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3107 		return;
3108 	}
3109 
3110 	ipsec_instance_name = tokens[1];
3111 	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
3112 	if (!ipsec) {
3113 		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
3114 		goto free;
3115 	}
3116 
3117 	if (strcmp(tokens[2], "sa")) {
3118 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
3119 		goto free;
3120 	}
3121 
3122 	if (strcmp(tokens[3], "add")) {
3123 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3124 		goto free;
3125 	}
3126 
3127 	file_name = tokens[4];
3128 	file = fopen(file_name, "r");
3129 	if (!file) {
3130 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
3131 		goto free;
3132 	}
3133 
3134 	/* Buffer allocation. */
3135 	line = malloc(MAX_LINE_SIZE);
3136 	if (!line) {
3137 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
3138 		goto free;
3139 	}
3140 
3141 	/* File read. */
3142 	for (line_id = 1; ; line_id++) {
3143 		struct rte_swx_ipsec_sa_params *sa;
3144 		const char *err_msg;
3145 		uint32_t sa_id = 0;
3146 		int is_blank_or_comment, status = 0;
3147 
3148 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
3149 			break;
3150 
3151 		/* Read SA from file. */
3152 		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
3153 		if (!sa) {
3154 			if (is_blank_or_comment)
3155 				continue;
3156 
3157 			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
3158 				file_name, line_id, err_msg);
3159 			goto free;
3160 		}
3161 
3162 		snprintf(out, out_size, "%s", line);
3163 		out_size -= strlen(out);
3164 		out += strlen(out);
3165 
3166 		/* Add the SA to the IPsec instance. Free the SA. */
3167 		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
3168 		if (status)
3169 			snprintf(out, out_size, "\t: Error (%d)\n", status);
3170 		else
3171 			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
3172 		out_size -= strlen(out);
3173 		out += strlen(out);
3174 
3175 		free(sa);
3176 		if (status)
3177 			goto free;
3178 	}
3179 
3180 free:
3181 	if (file)
3182 		fclose(file);
3183 	free(line);
3184 }
3185 
3186 static const char cmd_ipsec_sa_delete_help[] =
3187 "ipsec <ipsec_instance_name> sa delete <sa_id>\n";
3188 
3189 static void
3190 cmd_ipsec_sa_delete(char **tokens,
3191 		    uint32_t n_tokens,
3192 		    char *out,
3193 		    size_t out_size,
3194 		    void *obj __rte_unused)
3195 {
3196 	struct rte_swx_ipsec *ipsec;
3197 	char *ipsec_instance_name;
3198 	uint32_t sa_id;
3199 
3200 	if (n_tokens != 5) {
3201 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3202 		return;
3203 	}
3204 
3205 	ipsec_instance_name = tokens[1];
3206 	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
3207 	if (!ipsec) {
3208 		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
3209 		return;
3210 	}
3211 
3212 	if (strcmp(tokens[2], "sa")) {
3213 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
3214 		return;
3215 	}
3216 
3217 	if (strcmp(tokens[3], "delete")) {
3218 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
3219 		return;
3220 	}
3221 
3222 	if (parser_read_uint32(&sa_id, tokens[4])) {
3223 		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
3224 		return;
3225 	}
3226 
3227 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
3228 }
3229 
3230 static const char cmd_pipeline_enable_help[] =
3231 "pipeline <pipeline_name> enable thread <thread_id>\n";
3232 
3233 static void
3234 cmd_pipeline_enable(char **tokens,
3235 		    uint32_t n_tokens,
3236 		    char *out,
3237 		    size_t out_size,
3238 		    void *obj __rte_unused)
3239 {
3240 	char *pipeline_name;
3241 	struct rte_swx_pipeline *p;
3242 	uint32_t thread_id;
3243 	int status;
3244 
3245 	if (n_tokens != 5) {
3246 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3247 		return;
3248 	}
3249 
3250 	pipeline_name = tokens[1];
3251 	p = rte_swx_pipeline_find(pipeline_name);
3252 	if (!p) {
3253 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
3254 		return;
3255 	}
3256 
3257 	if (strcmp(tokens[2], "enable") != 0) {
3258 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
3259 		return;
3260 	}
3261 
3262 	if (strcmp(tokens[3], "thread") != 0) {
3263 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
3264 		return;
3265 	}
3266 
3267 	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
3268 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
3269 		return;
3270 	}
3271 
3272 	status = pipeline_enable(p, thread_id);
3273 	if (status) {
3274 		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
3275 		return;
3276 	}
3277 }
3278 
3279 static const char cmd_pipeline_disable_help[] =
3280 "pipeline <pipeline_name> disable\n";
3281 
3282 static void
3283 cmd_pipeline_disable(char **tokens,
3284 		     uint32_t n_tokens,
3285 		     char *out,
3286 		     size_t out_size,
3287 		     void *obj __rte_unused)
3288 {
3289 	struct rte_swx_pipeline *p;
3290 	char *pipeline_name;
3291 
3292 	if (n_tokens != 3) {
3293 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3294 		return;
3295 	}
3296 
3297 	pipeline_name = tokens[1];
3298 	p = rte_swx_pipeline_find(pipeline_name);
3299 	if (!p) {
3300 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
3301 		return;
3302 	}
3303 
3304 	if (strcmp(tokens[2], "disable") != 0) {
3305 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
3306 		return;
3307 	}
3308 
3309 	pipeline_disable(p);
3310 }
3311 
3312 static const char cmd_block_enable_help[] =
3313 "block type <block_type> instance <block_name> enable thread <thread_id>\n";
3314 
3315 static void
3316 cmd_block_enable(char **tokens,
3317 		 uint32_t n_tokens,
3318 		 char *out,
3319 		 size_t out_size,
3320 		 void *obj __rte_unused)
3321 {
3322 	char *block_type, *block_name;
3323 	block_run_f block_func = NULL;
3324 	void *block = NULL;
3325 	uint32_t thread_id;
3326 	int status;
3327 
3328 	if (n_tokens != 8) {
3329 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3330 		return;
3331 	}
3332 
3333 	if (strcmp(tokens[1], "type") != 0) {
3334 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
3335 		return;
3336 	}
3337 
3338 	block_type = tokens[2];
3339 
3340 	if (strcmp(tokens[3], "instance") != 0) {
3341 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
3342 		return;
3343 	}
3344 
3345 	block_name = tokens[4];
3346 
3347 	if (strcmp(tokens[5], "enable") != 0) {
3348 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
3349 		return;
3350 	}
3351 
3352 	if (strcmp(tokens[6], "thread") != 0) {
3353 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
3354 		return;
3355 	}
3356 
3357 	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
3358 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
3359 		return;
3360 	}
3361 
3362 	if (!strcmp(block_type, "ipsec")) {
3363 		struct rte_swx_ipsec *ipsec;
3364 
3365 		ipsec = rte_swx_ipsec_find(block_name);
3366 		if (!ipsec) {
3367 			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
3368 			return;
3369 		}
3370 
3371 		block_func = (block_run_f)rte_swx_ipsec_run;
3372 		block = (void *)ipsec;
3373 	} else {
3374 		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
3375 		return;
3376 	}
3377 
3378 	status = block_enable(block_func, block, thread_id);
3379 	if (status) {
3380 		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
3381 		return;
3382 	}
3383 }
3384 
3385 static const char cmd_block_disable_help[] =
3386 "block type <block_type> instance <block_name> disable\n";
3387 
3388 static void
3389 cmd_block_disable(char **tokens,
3390 		  uint32_t n_tokens,
3391 		  char *out,
3392 		  size_t out_size,
3393 		  void *obj __rte_unused)
3394 {
3395 	char *block_type, *block_name;
3396 	void *block = NULL;
3397 
3398 	if (n_tokens != 6) {
3399 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3400 		return;
3401 	}
3402 
3403 	if (strcmp(tokens[1], "type") != 0) {
3404 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
3405 		return;
3406 	}
3407 
3408 	block_type = tokens[2];
3409 
3410 	if (strcmp(tokens[3], "instance") != 0) {
3411 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
3412 		return;
3413 	}
3414 
3415 	block_name = tokens[4];
3416 
3417 	if (strcmp(tokens[5], "disable") != 0) {
3418 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
3419 		return;
3420 	}
3421 
3422 	if (!strcmp(block_type, "ipsec")) {
3423 		struct rte_swx_ipsec *ipsec;
3424 
3425 		ipsec = rte_swx_ipsec_find(block_name);
3426 		if (!ipsec) {
3427 			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
3428 			return;
3429 		}
3430 
3431 		block = (void *)ipsec;
3432 	} else {
3433 		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
3434 		return;
3435 	}
3436 
3437 	block_disable(block);
3438 }
3439 
3440 static void
3441 cmd_help(char **tokens,
3442 	 uint32_t n_tokens,
3443 	 char *out,
3444 	 size_t out_size,
3445 	 void *arg __rte_unused)
3446 {
3447 	tokens++;
3448 	n_tokens--;
3449 
3450 	if (n_tokens == 0) {
3451 		snprintf(out, out_size,
3452 			"Type 'help <command>' for command details.\n\n"
3453 			"List of commands:\n"
3454 			"\tmempool\n"
3455 			"\tethdev\n"
3456 			"\tethdev show\n"
3457 			"\tring\n"
3458 			"\tcryptodev\n"
3459 			"\tpipeline codegen\n"
3460 			"\tpipeline libbuild\n"
3461 			"\tpipeline build\n"
3462 			"\tpipeline table add\n"
3463 			"\tpipeline table delete\n"
3464 			"\tpipeline table default\n"
3465 			"\tpipeline table show\n"
3466 			"\tpipeline selector group add\n"
3467 			"\tpipeline selector group delete\n"
3468 			"\tpipeline selector group member add\n"
3469 			"\tpipeline selector group member delete\n"
3470 			"\tpipeline selector show\n"
3471 			"\tpipeline learner default\n"
3472 			"\tpipeline commit\n"
3473 			"\tpipeline abort\n"
3474 			"\tpipeline regrd\n"
3475 			"\tpipeline regwr\n"
3476 			"\tpipeline meter profile add\n"
3477 			"\tpipeline meter profile delete\n"
3478 			"\tpipeline meter reset\n"
3479 			"\tpipeline meter set\n"
3480 			"\tpipeline meter stats\n"
3481 			"\tpipeline rss\n"
3482 			"\tpipeline stats\n"
3483 			"\tpipeline mirror session\n"
3484 			"\tpipeline enable\n"
3485 			"\tpipeline disable\n\n"
3486 			"\tipsec create\n"
3487 			"\tipsec sa add\n"
3488 			"\tipsec sa delete\n"
3489 			"\tblock enable\n"
3490 			"\tblock disable\n"
3491 			);
3492 		return;
3493 	}
3494 
3495 	if (strcmp(tokens[0], "mempool") == 0) {
3496 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
3497 		return;
3498 	}
3499 
3500 	if (!strcmp(tokens[0], "ethdev")) {
3501 		if (n_tokens == 1) {
3502 			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
3503 			return;
3504 		}
3505 
3506 		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
3507 			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
3508 			return;
3509 		}
3510 	}
3511 
3512 	if (strcmp(tokens[0], "ring") == 0) {
3513 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
3514 		return;
3515 	}
3516 
3517 	if (!strcmp(tokens[0], "cryptodev")) {
3518 		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
3519 		return;
3520 	}
3521 
3522 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3523 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
3524 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
3525 		return;
3526 	}
3527 
3528 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3529 		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
3530 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
3531 		return;
3532 	}
3533 
3534 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3535 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
3536 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
3537 		return;
3538 	}
3539 
3540 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3541 		(n_tokens == 3) &&
3542 		(strcmp(tokens[1], "table") == 0) &&
3543 		(strcmp(tokens[2], "add") == 0)) {
3544 		snprintf(out, out_size, "\n%s\n",
3545 			cmd_pipeline_table_add_help);
3546 		return;
3547 	}
3548 
3549 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3550 		(n_tokens == 3) &&
3551 		(strcmp(tokens[1], "table") == 0) &&
3552 		(strcmp(tokens[2], "delete") == 0)) {
3553 		snprintf(out, out_size, "\n%s\n",
3554 			cmd_pipeline_table_delete_help);
3555 		return;
3556 	}
3557 
3558 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3559 		(n_tokens == 3) &&
3560 		(strcmp(tokens[1], "table") == 0) &&
3561 		(strcmp(tokens[2], "default") == 0)) {
3562 		snprintf(out, out_size, "\n%s\n",
3563 			cmd_pipeline_table_default_help);
3564 		return;
3565 	}
3566 
3567 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3568 		(n_tokens == 3) &&
3569 		(strcmp(tokens[1], "table") == 0) &&
3570 		(strcmp(tokens[2], "show") == 0)) {
3571 		snprintf(out, out_size, "\n%s\n",
3572 			cmd_pipeline_table_show_help);
3573 		return;
3574 	}
3575 
3576 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3577 		(n_tokens == 4) &&
3578 		(strcmp(tokens[1], "selector") == 0) &&
3579 		(strcmp(tokens[2], "group") == 0) &&
3580 		(strcmp(tokens[3], "add") == 0)) {
3581 		snprintf(out, out_size, "\n%s\n",
3582 			cmd_pipeline_selector_group_add_help);
3583 		return;
3584 	}
3585 
3586 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3587 		(n_tokens == 4) &&
3588 		(strcmp(tokens[1], "selector") == 0) &&
3589 		(strcmp(tokens[2], "group") == 0) &&
3590 		(strcmp(tokens[3], "delete") == 0)) {
3591 		snprintf(out, out_size, "\n%s\n",
3592 			cmd_pipeline_selector_group_delete_help);
3593 		return;
3594 	}
3595 
3596 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3597 		(n_tokens == 5) &&
3598 		(strcmp(tokens[1], "selector") == 0) &&
3599 		(strcmp(tokens[2], "group") == 0) &&
3600 		(strcmp(tokens[3], "member") == 0) &&
3601 		(strcmp(tokens[4], "add") == 0)) {
3602 		snprintf(out, out_size, "\n%s\n",
3603 			cmd_pipeline_selector_group_member_add_help);
3604 		return;
3605 	}
3606 
3607 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3608 		(n_tokens == 5) &&
3609 		(strcmp(tokens[1], "selector") == 0) &&
3610 		(strcmp(tokens[2], "group") == 0) &&
3611 		(strcmp(tokens[3], "member") == 0) &&
3612 		(strcmp(tokens[4], "delete") == 0)) {
3613 		snprintf(out, out_size, "\n%s\n",
3614 			cmd_pipeline_selector_group_member_delete_help);
3615 		return;
3616 	}
3617 
3618 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3619 		(n_tokens == 3) &&
3620 		(strcmp(tokens[1], "selector") == 0) &&
3621 		(strcmp(tokens[2], "show") == 0)) {
3622 		snprintf(out, out_size, "\n%s\n",
3623 			cmd_pipeline_selector_show_help);
3624 		return;
3625 	}
3626 
3627 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3628 		(n_tokens == 3) &&
3629 		(strcmp(tokens[1], "learner") == 0) &&
3630 		(strcmp(tokens[2], "default") == 0)) {
3631 		snprintf(out, out_size, "\n%s\n",
3632 			cmd_pipeline_learner_default_help);
3633 		return;
3634 	}
3635 
3636 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3637 		(n_tokens == 2) &&
3638 		(strcmp(tokens[1], "commit") == 0)) {
3639 		snprintf(out, out_size, "\n%s\n",
3640 			cmd_pipeline_commit_help);
3641 		return;
3642 	}
3643 
3644 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3645 		(n_tokens == 2) &&
3646 		(strcmp(tokens[1], "abort") == 0)) {
3647 		snprintf(out, out_size, "\n%s\n",
3648 			cmd_pipeline_abort_help);
3649 		return;
3650 	}
3651 
3652 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3653 		(n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
3654 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
3655 		return;
3656 	}
3657 
3658 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3659 		(n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
3660 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
3661 		return;
3662 	}
3663 
3664 	if (!strcmp(tokens[0], "pipeline") &&
3665 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
3666 		&& !strcmp(tokens[2], "profile")
3667 		&& !strcmp(tokens[3], "add")) {
3668 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
3669 		return;
3670 	}
3671 
3672 	if (!strcmp(tokens[0], "pipeline") &&
3673 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
3674 		&& !strcmp(tokens[2], "profile")
3675 		&& !strcmp(tokens[3], "delete")) {
3676 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
3677 		return;
3678 	}
3679 
3680 	if (!strcmp(tokens[0], "pipeline") &&
3681 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3682 		&& !strcmp(tokens[2], "reset")) {
3683 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
3684 		return;
3685 	}
3686 
3687 	if (!strcmp(tokens[0], "pipeline") &&
3688 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3689 		&& !strcmp(tokens[2], "set")) {
3690 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
3691 		return;
3692 	}
3693 
3694 	if (!strcmp(tokens[0], "pipeline") &&
3695 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3696 		&& !strcmp(tokens[2], "stats")) {
3697 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
3698 		return;
3699 	}
3700 
3701 	if (!strcmp(tokens[0], "pipeline") &&
3702 		(n_tokens == 2) && !strcmp(tokens[1], "rss")) {
3703 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_rss_help);
3704 		return;
3705 	}
3706 
3707 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3708 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
3709 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
3710 		return;
3711 	}
3712 
3713 	if (!strcmp(tokens[0], "pipeline") &&
3714 		(n_tokens == 3) && !strcmp(tokens[1], "mirror")
3715 		&& !strcmp(tokens[2], "session")) {
3716 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_session_help);
3717 		return;
3718 	}
3719 
3720 	if (!strcmp(tokens[0], "pipeline") &&
3721 		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
3722 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
3723 		return;
3724 	}
3725 
3726 	if (!strcmp(tokens[0], "pipeline") &&
3727 		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
3728 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
3729 		return;
3730 	}
3731 
3732 	if (!strcmp(tokens[0], "ipsec") &&
3733 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
3734 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
3735 		return;
3736 	}
3737 
3738 	if (!strcmp(tokens[0], "ipsec") &&
3739 		(n_tokens == 3) && !strcmp(tokens[1], "sa")
3740 		&& !strcmp(tokens[2], "add")) {
3741 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
3742 		return;
3743 	}
3744 
3745 	if (!strcmp(tokens[0], "ipsec") &&
3746 		(n_tokens == 3) && !strcmp(tokens[1], "sa")
3747 		&& !strcmp(tokens[2], "delete")) {
3748 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
3749 		return;
3750 	}
3751 
3752 	if (!strcmp(tokens[0], "block") &&
3753 		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
3754 		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
3755 		return;
3756 	}
3757 
3758 	if (!strcmp(tokens[0], "block") &&
3759 		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
3760 		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
3761 		return;
3762 	}
3763 
3764 	snprintf(out, out_size, "Invalid command\n");
3765 }
3766 
3767 void
3768 cli_process(char *in, char *out, size_t out_size, void *obj)
3769 {
3770 	char *tokens[CMD_MAX_TOKENS];
3771 	uint32_t n_tokens = RTE_DIM(tokens);
3772 	int status;
3773 
3774 	if (is_comment(in))
3775 		return;
3776 
3777 	status = parse_tokenize_string(in, tokens, &n_tokens);
3778 	if (status) {
3779 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3780 		return;
3781 	}
3782 
3783 	if (n_tokens == 0)
3784 		return;
3785 
3786 	if (strcmp(tokens[0], "help") == 0) {
3787 		cmd_help(tokens, n_tokens, out, out_size, obj);
3788 		return;
3789 	}
3790 
3791 	if (strcmp(tokens[0], "mempool") == 0) {
3792 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
3793 		return;
3794 	}
3795 
3796 	if (strcmp(tokens[0], "ethdev") == 0) {
3797 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3798 			cmd_ethdev_show(tokens, n_tokens, out, out_size, obj);
3799 			return;
3800 		}
3801 
3802 		cmd_ethdev(tokens, n_tokens, out, out_size, obj);
3803 		return;
3804 	}
3805 
3806 	if (strcmp(tokens[0], "ring") == 0) {
3807 		cmd_ring(tokens, n_tokens, out, out_size, obj);
3808 		return;
3809 	}
3810 
3811 	if (!strcmp(tokens[0], "cryptodev")) {
3812 		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
3813 		return;
3814 	}
3815 
3816 	if (strcmp(tokens[0], "pipeline") == 0) {
3817 		if ((n_tokens >= 3) &&
3818 			(strcmp(tokens[1], "codegen") == 0)) {
3819 			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
3820 				obj);
3821 			return;
3822 		}
3823 
3824 		if ((n_tokens >= 3) &&
3825 			(strcmp(tokens[1], "libbuild") == 0)) {
3826 			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
3827 				obj);
3828 			return;
3829 		}
3830 
3831 		if ((n_tokens >= 3) &&
3832 			(strcmp(tokens[2], "build") == 0)) {
3833 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
3834 				obj);
3835 			return;
3836 		}
3837 
3838 		if ((n_tokens >= 5) &&
3839 			(strcmp(tokens[2], "table") == 0) &&
3840 			(strcmp(tokens[4], "add") == 0)) {
3841 			cmd_pipeline_table_add(tokens, n_tokens, out,
3842 				out_size, obj);
3843 			return;
3844 		}
3845 
3846 		if ((n_tokens >= 5) &&
3847 			(strcmp(tokens[2], "table") == 0) &&
3848 			(strcmp(tokens[4], "delete") == 0)) {
3849 			cmd_pipeline_table_delete(tokens, n_tokens, out,
3850 				out_size, obj);
3851 			return;
3852 		}
3853 
3854 		if ((n_tokens >= 5) &&
3855 			(strcmp(tokens[2], "table") == 0) &&
3856 			(strcmp(tokens[4], "default") == 0)) {
3857 			cmd_pipeline_table_default(tokens, n_tokens, out,
3858 				out_size, obj);
3859 			return;
3860 		}
3861 
3862 		if ((n_tokens >= 5) &&
3863 			(strcmp(tokens[2], "table") == 0) &&
3864 			(strcmp(tokens[4], "show") == 0)) {
3865 			cmd_pipeline_table_show(tokens, n_tokens, out,
3866 				out_size, obj);
3867 			return;
3868 		}
3869 
3870 		if ((n_tokens >= 6) &&
3871 			(strcmp(tokens[2], "selector") == 0) &&
3872 			(strcmp(tokens[4], "group") == 0) &&
3873 			(strcmp(tokens[5], "add") == 0)) {
3874 			cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3875 				out_size, obj);
3876 			return;
3877 		}
3878 
3879 		if ((n_tokens >= 6) &&
3880 			(strcmp(tokens[2], "selector") == 0) &&
3881 			(strcmp(tokens[4], "group") == 0) &&
3882 			(strcmp(tokens[5], "delete") == 0)) {
3883 			cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3884 				out_size, obj);
3885 			return;
3886 		}
3887 
3888 		if ((n_tokens >= 7) &&
3889 			(strcmp(tokens[2], "selector") == 0) &&
3890 			(strcmp(tokens[4], "group") == 0) &&
3891 			(strcmp(tokens[5], "member") == 0) &&
3892 			(strcmp(tokens[6], "add") == 0)) {
3893 			cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3894 				out_size, obj);
3895 			return;
3896 		}
3897 
3898 		if ((n_tokens >= 7) &&
3899 			(strcmp(tokens[2], "selector") == 0) &&
3900 			(strcmp(tokens[4], "group") == 0) &&
3901 			(strcmp(tokens[5], "member") == 0) &&
3902 			(strcmp(tokens[6], "delete") == 0)) {
3903 			cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3904 				out_size, obj);
3905 			return;
3906 		}
3907 
3908 		if ((n_tokens >= 5) &&
3909 			(strcmp(tokens[2], "selector") == 0) &&
3910 			(strcmp(tokens[4], "show") == 0)) {
3911 			cmd_pipeline_selector_show(tokens, n_tokens, out,
3912 				out_size, obj);
3913 			return;
3914 		}
3915 
3916 		if ((n_tokens >= 5) &&
3917 			(strcmp(tokens[2], "learner") == 0) &&
3918 			(strcmp(tokens[4], "default") == 0)) {
3919 			cmd_pipeline_learner_default(tokens, n_tokens, out,
3920 				out_size, obj);
3921 			return;
3922 		}
3923 
3924 		if ((n_tokens >= 3) &&
3925 			(strcmp(tokens[2], "commit") == 0)) {
3926 			cmd_pipeline_commit(tokens, n_tokens, out,
3927 				out_size, obj);
3928 			return;
3929 		}
3930 
3931 		if ((n_tokens >= 3) &&
3932 			(strcmp(tokens[2], "abort") == 0)) {
3933 			cmd_pipeline_abort(tokens, n_tokens, out,
3934 				out_size, obj);
3935 			return;
3936 		}
3937 
3938 		if ((n_tokens >= 3) &&
3939 			(strcmp(tokens[2], "regrd") == 0)) {
3940 			cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3941 			return;
3942 		}
3943 
3944 		if ((n_tokens >= 3) &&
3945 			(strcmp(tokens[2], "regwr") == 0)) {
3946 			cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3947 			return;
3948 		}
3949 
3950 		if ((n_tokens >= 6) &&
3951 			(strcmp(tokens[2], "meter") == 0) &&
3952 			(strcmp(tokens[3], "profile") == 0) &&
3953 			(strcmp(tokens[5], "add") == 0)) {
3954 			cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3955 			return;
3956 		}
3957 
3958 		if ((n_tokens >= 6) &&
3959 			(strcmp(tokens[2], "meter") == 0) &&
3960 			(strcmp(tokens[3], "profile") == 0) &&
3961 			(strcmp(tokens[5], "delete") == 0)) {
3962 			cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3963 			return;
3964 		}
3965 
3966 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "reset")) {
3967 			cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3968 			return;
3969 		}
3970 
3971 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "set")) {
3972 			cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3973 			return;
3974 		}
3975 
3976 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "stats")) {
3977 			cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3978 			return;
3979 		}
3980 
3981 		if (n_tokens >= 3 && !strcmp(tokens[2], "rss")) {
3982 			cmd_pipeline_rss(tokens, n_tokens, out, out_size, obj);
3983 			return;
3984 		}
3985 
3986 		if ((n_tokens >= 3) &&
3987 			(strcmp(tokens[2], "stats") == 0)) {
3988 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3989 				obj);
3990 			return;
3991 		}
3992 
3993 		if ((n_tokens >= 4) &&
3994 			(strcmp(tokens[2], "mirror") == 0) &&
3995 			(strcmp(tokens[3], "session") == 0)) {
3996 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
3997 			return;
3998 		}
3999 
4000 		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
4001 			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
4002 			return;
4003 		}
4004 
4005 		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
4006 			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
4007 			return;
4008 		}
4009 	}
4010 
4011 	if (!strcmp(tokens[0], "ipsec")) {
4012 		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
4013 			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
4014 			return;
4015 		}
4016 
4017 		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
4018 			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
4019 			return;
4020 		}
4021 
4022 		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
4023 			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
4024 			return;
4025 		}
4026 	}
4027 
4028 	if (!strcmp(tokens[0], "block")) {
4029 		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
4030 			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
4031 			return;
4032 		}
4033 
4034 		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
4035 			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
4036 			return;
4037 		}
4038 	}
4039 
4040 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
4041 }
4042 
4043 int
4044 cli_script_process(const char *file_name,
4045 	size_t msg_in_len_max,
4046 	size_t msg_out_len_max,
4047 	void *obj)
4048 {
4049 	char *msg_in = NULL, *msg_out = NULL;
4050 	FILE *f = NULL;
4051 
4052 	/* Check input arguments */
4053 	if ((file_name == NULL) ||
4054 		(strlen(file_name) == 0) ||
4055 		(msg_in_len_max == 0) ||
4056 		(msg_out_len_max == 0))
4057 		return -EINVAL;
4058 
4059 	msg_in = malloc(msg_in_len_max + 1);
4060 	msg_out = malloc(msg_out_len_max + 1);
4061 	if ((msg_in == NULL) ||
4062 		(msg_out == NULL)) {
4063 		free(msg_out);
4064 		free(msg_in);
4065 		return -ENOMEM;
4066 	}
4067 
4068 	/* Open input file */
4069 	f = fopen(file_name, "r");
4070 	if (f == NULL) {
4071 		free(msg_out);
4072 		free(msg_in);
4073 		return -EIO;
4074 	}
4075 
4076 	/* Read file */
4077 	for ( ; ; ) {
4078 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
4079 			break;
4080 
4081 		printf("%s", msg_in);
4082 		msg_out[0] = 0;
4083 
4084 		cli_process(msg_in,
4085 			msg_out,
4086 			msg_out_len_max,
4087 			obj);
4088 
4089 		if (strlen(msg_out))
4090 			printf("%s", msg_out);
4091 	}
4092 
4093 	/* Close file */
4094 	fclose(f);
4095 	free(msg_out);
4096 	free(msg_in);
4097 	return 0;
4098 }
4099