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