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