xref: /dpdk/examples/pipeline/cli.c (revision 2099295abf607f5ce35bd42b9d94e65204b69c97)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <rte_common.h>
12 #include <rte_ethdev.h>
13 #include <rte_swx_port_ethdev.h>
14 #include <rte_swx_port_ring.h>
15 #include <rte_swx_port_source_sink.h>
16 #include <rte_swx_port_fd.h>
17 #include <rte_swx_pipeline.h>
18 #include <rte_swx_ctl.h>
19 
20 #include "cli.h"
21 
22 #include "obj.h"
23 #include "thread.h"
24 
25 #ifndef CMD_MAX_TOKENS
26 #define CMD_MAX_TOKENS     256
27 #endif
28 
29 #ifndef MAX_LINE_SIZE
30 #define MAX_LINE_SIZE 2048
31 #endif
32 
33 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
34 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
35 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
36 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
37 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
38 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
39 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
40 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
41 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
42 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
43 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
44 
45 #define skip_white_spaces(pos)			\
46 ({						\
47 	__typeof__(pos) _p = (pos);		\
48 	for ( ; isspace(*_p); _p++)		\
49 		;				\
50 	_p;					\
51 })
52 
53 static int
54 parser_read_uint64(uint64_t *value, const char *p)
55 {
56 	char *next;
57 	uint64_t val;
58 
59 	p = skip_white_spaces(p);
60 	if (!isdigit(*p))
61 		return -EINVAL;
62 
63 	val = strtoul(p, &next, 0);
64 	if (p == next)
65 		return -EINVAL;
66 
67 	p = next;
68 	switch (*p) {
69 	case 'T':
70 		val *= 1024ULL;
71 		/* fall through */
72 	case 'G':
73 		val *= 1024ULL;
74 		/* fall through */
75 	case 'M':
76 		val *= 1024ULL;
77 		/* fall through */
78 	case 'k':
79 	case 'K':
80 		val *= 1024ULL;
81 		p++;
82 		break;
83 	}
84 
85 	p = skip_white_spaces(p);
86 	if (*p != '\0')
87 		return -EINVAL;
88 
89 	*value = val;
90 	return 0;
91 }
92 
93 static int
94 parser_read_uint32(uint32_t *value, const char *p)
95 {
96 	uint64_t val = 0;
97 	int ret = parser_read_uint64(&val, p);
98 
99 	if (ret < 0)
100 		return ret;
101 
102 	if (val > UINT32_MAX)
103 		return -ERANGE;
104 
105 	*value = val;
106 	return 0;
107 }
108 
109 static int
110 parser_read_uint16(uint16_t *value, const char *p)
111 {
112 	uint64_t val = 0;
113 	int ret = parser_read_uint64(&val, p);
114 
115 	if (ret < 0)
116 		return ret;
117 
118 	if (val > UINT16_MAX)
119 		return -ERANGE;
120 
121 	*value = val;
122 	return 0;
123 }
124 
125 #define PARSE_DELIMITER " \f\n\r\t\v"
126 
127 static int
128 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
129 {
130 	uint32_t i;
131 
132 	if ((string == NULL) ||
133 		(tokens == NULL) ||
134 		(*n_tokens < 1))
135 		return -EINVAL;
136 
137 	for (i = 0; i < *n_tokens; i++) {
138 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
139 		if (tokens[i] == NULL)
140 			break;
141 	}
142 
143 	if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
144 		return -E2BIG;
145 
146 	*n_tokens = i;
147 	return 0;
148 }
149 
150 static int
151 is_comment(char *in)
152 {
153 	if ((strlen(in) && index("!#%;", in[0])) ||
154 		(strncmp(in, "//", 2) == 0) ||
155 		(strncmp(in, "--", 2) == 0))
156 		return 1;
157 
158 	return 0;
159 }
160 
161 static const char cmd_mempool_help[] =
162 "mempool <mempool_name>\n"
163 "   buffer <buffer_size>\n"
164 "   pool <pool_size>\n"
165 "   cache <cache_size>\n"
166 "   cpu <cpu_id>\n";
167 
168 static void
169 cmd_mempool(char **tokens,
170 	uint32_t n_tokens,
171 	char *out,
172 	size_t out_size,
173 	void *obj)
174 {
175 	struct mempool_params p;
176 	char *name;
177 	struct mempool *mempool;
178 
179 	if (n_tokens != 10) {
180 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
181 		return;
182 	}
183 
184 	name = tokens[1];
185 
186 	if (strcmp(tokens[2], "buffer") != 0) {
187 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
188 		return;
189 	}
190 
191 	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
192 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
193 		return;
194 	}
195 
196 	if (strcmp(tokens[4], "pool") != 0) {
197 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
198 		return;
199 	}
200 
201 	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
202 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
203 		return;
204 	}
205 
206 	if (strcmp(tokens[6], "cache") != 0) {
207 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
208 		return;
209 	}
210 
211 	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
212 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
213 		return;
214 	}
215 
216 	if (strcmp(tokens[8], "cpu") != 0) {
217 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
218 		return;
219 	}
220 
221 	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
222 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
223 		return;
224 	}
225 
226 	mempool = mempool_create(obj, name, &p);
227 	if (mempool == NULL) {
228 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
229 		return;
230 	}
231 }
232 
233 static const char cmd_link_help[] =
234 "link <link_name>\n"
235 "   dev <device_name> | port <port_id>\n"
236 "   rxq <n_queues> <queue_size> <mempool_name>\n"
237 "   txq <n_queues> <queue_size>\n"
238 "   promiscuous on | off\n"
239 "   [rss <qid_0> ... <qid_n>]\n";
240 
241 static void
242 cmd_link(char **tokens,
243 	uint32_t n_tokens,
244 	char *out,
245 	size_t out_size,
246 	void *obj)
247 {
248 	struct link_params p;
249 	struct link_params_rss rss;
250 	struct link *link;
251 	char *name;
252 
253 	memset(&p, 0, sizeof(p));
254 
255 	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
256 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
257 		return;
258 	}
259 	name = tokens[1];
260 
261 	if (strcmp(tokens[2], "dev") == 0)
262 		p.dev_name = tokens[3];
263 	else if (strcmp(tokens[2], "port") == 0) {
264 		p.dev_name = NULL;
265 
266 		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
267 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
268 			return;
269 		}
270 	} else {
271 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
272 		return;
273 	}
274 
275 	if (strcmp(tokens[4], "rxq") != 0) {
276 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
277 		return;
278 	}
279 
280 	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
281 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
282 		return;
283 	}
284 	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
285 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
286 		return;
287 	}
288 
289 	p.rx.mempool_name = tokens[7];
290 
291 	if (strcmp(tokens[8], "txq") != 0) {
292 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
293 		return;
294 	}
295 
296 	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
297 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
298 		return;
299 	}
300 
301 	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
302 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
303 		return;
304 	}
305 
306 	if (strcmp(tokens[11], "promiscuous") != 0) {
307 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
308 		return;
309 	}
310 
311 	if (strcmp(tokens[12], "on") == 0)
312 		p.promiscuous = 1;
313 	else if (strcmp(tokens[12], "off") == 0)
314 		p.promiscuous = 0;
315 	else {
316 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
317 		return;
318 	}
319 
320 	/* RSS */
321 	p.rx.rss = NULL;
322 	if (n_tokens > 13) {
323 		uint32_t queue_id, i;
324 
325 		if (strcmp(tokens[13], "rss") != 0) {
326 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
327 			return;
328 		}
329 
330 		p.rx.rss = &rss;
331 
332 		rss.n_queues = 0;
333 		for (i = 14; i < n_tokens; i++) {
334 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
335 				snprintf(out, out_size, MSG_ARG_INVALID,
336 					"queue_id");
337 				return;
338 			}
339 
340 			rss.queue_id[rss.n_queues] = queue_id;
341 			rss.n_queues++;
342 		}
343 	}
344 
345 	link = link_create(obj, name, &p);
346 	if (link == NULL) {
347 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
348 		return;
349 	}
350 }
351 
352 /* Print the link stats and info */
353 static void
354 print_link_info(struct link *link, char *out, size_t out_size)
355 {
356 	struct rte_eth_stats stats;
357 	struct rte_ether_addr mac_addr;
358 	struct rte_eth_link eth_link;
359 	uint16_t mtu;
360 	int ret;
361 
362 	memset(&stats, 0, sizeof(stats));
363 	rte_eth_stats_get(link->port_id, &stats);
364 
365 	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
366 	if (ret != 0) {
367 		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
368 			 link->name, rte_strerror(-ret));
369 		return;
370 	}
371 
372 	ret = rte_eth_link_get(link->port_id, &eth_link);
373 	if (ret < 0) {
374 		snprintf(out, out_size, "\n%s: link get failed: %s",
375 			 link->name, rte_strerror(-ret));
376 		return;
377 	}
378 
379 	rte_eth_dev_get_mtu(link->port_id, &mtu);
380 
381 	snprintf(out, out_size,
382 		"\n"
383 		"%s: flags=<%s> mtu %u\n"
384 		"\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
385 		"\tport# %u  speed %s\n"
386 		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
387 		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
388 		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
389 		"\tTX errors %" PRIu64"\n",
390 		link->name,
391 		eth_link.link_status == 0 ? "DOWN" : "UP",
392 		mtu,
393 		RTE_ETHER_ADDR_BYTES(&mac_addr),
394 		link->n_rxq,
395 		link->n_txq,
396 		link->port_id,
397 		rte_eth_link_speed_to_str(eth_link.link_speed),
398 		stats.ipackets,
399 		stats.ibytes,
400 		stats.ierrors,
401 		stats.imissed,
402 		stats.rx_nombuf,
403 		stats.opackets,
404 		stats.obytes,
405 		stats.oerrors);
406 }
407 
408 /*
409  * link show [<link_name>]
410  */
411 static void
412 cmd_link_show(char **tokens,
413 	      uint32_t n_tokens,
414 	      char *out,
415 	      size_t out_size,
416 	      void *obj)
417 {
418 	struct link *link;
419 	char *link_name;
420 
421 	if (n_tokens != 2 && n_tokens != 3) {
422 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
423 		return;
424 	}
425 
426 	if (n_tokens == 2) {
427 		link = link_next(obj, NULL);
428 
429 		while (link != NULL) {
430 			out_size = out_size - strlen(out);
431 			out = &out[strlen(out)];
432 
433 			print_link_info(link, out, out_size);
434 			link = link_next(obj, link);
435 		}
436 	} else {
437 		out_size = out_size - strlen(out);
438 		out = &out[strlen(out)];
439 
440 		link_name = tokens[2];
441 		link = link_find(obj, link_name);
442 
443 		if (link == NULL) {
444 			snprintf(out, out_size, MSG_ARG_INVALID,
445 					"Link does not exist");
446 			return;
447 		}
448 		print_link_info(link, out, out_size);
449 	}
450 }
451 
452 static const char cmd_ring_help[] =
453 "ring <ring_name> size <size> numa <numa_node>\n";
454 
455 static void
456 cmd_ring(char **tokens,
457 	uint32_t n_tokens,
458 	char *out,
459 	size_t out_size,
460 	void *obj)
461 {
462 	struct ring_params p;
463 	char *name;
464 	struct ring *ring;
465 
466 	if (n_tokens != 6) {
467 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
468 		return;
469 	}
470 
471 	name = tokens[1];
472 
473 	if (strcmp(tokens[2], "size") != 0) {
474 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
475 		return;
476 	}
477 
478 	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
479 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
480 		return;
481 	}
482 
483 	if (strcmp(tokens[4], "numa") != 0) {
484 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
485 		return;
486 	}
487 
488 	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
489 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
490 		return;
491 	}
492 
493 	ring = ring_create(obj, name, &p);
494 	if (!ring) {
495 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
496 		return;
497 	}
498 }
499 
500 static const char cmd_tap_help[] =
501 "tap <tap_name>\n";
502 
503 static void
504 cmd_tap(char **tokens,
505 	uint32_t n_tokens,
506 	char *out,
507 	size_t out_size,
508 	void *obj)
509 {
510 	struct tap *tap;
511 	char *name;
512 
513 	if (n_tokens < 2) {
514 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
515 		return;
516 	}
517 	name = tokens[1];
518 
519 	tap = tap_create(obj, name);
520 	if (tap == NULL) {
521 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
522 		return;
523 	}
524 }
525 
526 static const char cmd_pipeline_codegen_help[] =
527 "pipeline codegen <spec_file> <code_file>\n";
528 
529 static void
530 cmd_pipeline_codegen(char **tokens,
531 	uint32_t n_tokens,
532 	char *out,
533 	size_t out_size,
534 	void *obj __rte_unused)
535 {
536 	FILE *spec_file = NULL;
537 	FILE *code_file = NULL;
538 	uint32_t err_line;
539 	const char *err_msg;
540 	int status;
541 
542 	if (n_tokens != 4) {
543 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
544 		return;
545 	}
546 
547 	spec_file = fopen(tokens[2], "r");
548 	if (!spec_file) {
549 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
550 		return;
551 	}
552 
553 	code_file = fopen(tokens[3], "w");
554 	if (!code_file) {
555 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
556 		return;
557 	}
558 
559 	status = rte_swx_pipeline_codegen(spec_file,
560 					  code_file,
561 					  &err_line,
562 					  &err_msg);
563 
564 	fclose(spec_file);
565 	fclose(code_file);
566 
567 	if (status) {
568 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
569 			status, err_line, err_msg);
570 		return;
571 	}
572 }
573 
574 static const char cmd_pipeline_libbuild_help[] =
575 "pipeline libbuild <code_file> <lib_file>\n";
576 
577 static void
578 cmd_pipeline_libbuild(char **tokens,
579 	uint32_t n_tokens,
580 	char *out,
581 	size_t out_size,
582 	void *obj __rte_unused)
583 {
584 	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
585 	char *install_dir, *cwd = NULL, *buffer = NULL;
586 	size_t length;
587 	int status = 0;
588 
589 	if (n_tokens != 4) {
590 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
591 		goto free;
592 	}
593 
594 	install_dir = getenv("RTE_INSTALL_DIR");
595 	if (!install_dir) {
596 		cwd = malloc(MAX_LINE_SIZE);
597 		if (!cwd) {
598 			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
599 			goto free;
600 		}
601 
602 		install_dir = getcwd(cwd, MAX_LINE_SIZE);
603 		if (!install_dir) {
604 			snprintf(out, out_size, "Error: Path too long.\n");
605 			goto free;
606 		}
607 	}
608 
609 	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
610 	out_size -= strlen(out);
611 	out += strlen(out);
612 
613 	code_file = tokens[2];
614 	length = strnlen(code_file, MAX_LINE_SIZE);
615 	if ((length < 3) ||
616 	    (code_file[length - 2] != '.') ||
617 	    (code_file[length - 1] != 'c')) {
618 		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
619 		goto free;
620 	}
621 
622 	lib_file = tokens[3];
623 	length = strnlen(lib_file, MAX_LINE_SIZE);
624 	if ((length < 4) ||
625 	    (lib_file[length - 3] != '.') ||
626 	    (lib_file[length - 2] != 's') ||
627 	    (lib_file[length - 1] != 'o')) {
628 		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
629 		goto free;
630 	}
631 
632 	obj_file = malloc(length);
633 	log_file = malloc(length + 2);
634 	if (!obj_file || !log_file) {
635 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
636 		goto free;
637 	}
638 
639 	memcpy(obj_file, lib_file, length - 2);
640 	obj_file[length - 2] = 'o';
641 	obj_file[length - 1] = 0;
642 
643 	memcpy(log_file, lib_file, length - 2);
644 	log_file[length - 2] = 'l';
645 	log_file[length - 1] = 'o';
646 	log_file[length] = 'g';
647 	log_file[length + 1] = 0;
648 
649 	buffer = malloc(MAX_LINE_SIZE);
650 	if (!buffer) {
651 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
652 		return;
653 	}
654 
655 	snprintf(buffer,
656 		 MAX_LINE_SIZE,
657 		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
658 		 "-I %s/lib/pipeline "
659 		 "-I %s/lib/eal/include "
660 		 "-I %s/lib/eal/x86/include "
661 		 "-I %s/lib/eal/include/generic "
662 		 "-I %s/lib/meter "
663 		 "-I %s/lib/port "
664 		 "-I %s/lib/table "
665 		 "-I %s/lib/pipeline "
666 		 "-I %s/config "
667 		 "-I %s/build "
668 		 "-I %s/lib/eal/linux/include "
669 		 ">%s 2>&1 "
670 		 "&& "
671 		 "gcc -shared %s -o %s "
672 		 ">>%s 2>&1",
673 		 obj_file,
674 		 code_file,
675 		 install_dir,
676 		 install_dir,
677 		 install_dir,
678 		 install_dir,
679 		 install_dir,
680 		 install_dir,
681 		 install_dir,
682 		 install_dir,
683 		 install_dir,
684 		 install_dir,
685 		 install_dir,
686 		 log_file,
687 		 obj_file,
688 		 lib_file,
689 		 log_file);
690 
691 	status = system(buffer);
692 	if (status) {
693 		snprintf(out,
694 			 out_size,
695 			 "Library build failed, see file \"%s\" for details.\n",
696 			 log_file);
697 		goto free;
698 	}
699 
700 free:
701 	free(cwd);
702 	free(obj_file);
703 	free(log_file);
704 	free(buffer);
705 }
706 
707 static const char cmd_pipeline_build_help[] =
708 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
709 
710 static void
711 cmd_pipeline_build(char **tokens,
712 	uint32_t n_tokens,
713 	char *out,
714 	size_t out_size,
715 	void *obj __rte_unused)
716 {
717 	struct rte_swx_pipeline *p = NULL;
718 	struct rte_swx_ctl_pipeline *ctl = NULL;
719 	char *pipeline_name, *lib_file_name, *iospec_file_name;
720 	FILE *iospec_file = NULL;
721 	uint32_t numa_node = 0;
722 	int status = 0;
723 
724 	/* Parsing. */
725 	if (n_tokens != 9) {
726 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
727 		return;
728 	}
729 
730 	pipeline_name = tokens[1];
731 
732 	if (strcmp(tokens[2], "build")) {
733 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
734 		return;
735 	}
736 
737 	if (strcmp(tokens[3], "lib")) {
738 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
739 		return;
740 	}
741 
742 	lib_file_name = tokens[4];
743 
744 	if (strcmp(tokens[5], "io")) {
745 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
746 		return;
747 	}
748 
749 	iospec_file_name = tokens[6];
750 
751 	if (strcmp(tokens[7], "numa")) {
752 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
753 		return;
754 	}
755 
756 	if (parser_read_uint32(&numa_node, tokens[8])) {
757 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
758 		return;
759 	}
760 
761 	/* I/O spec file open. */
762 	iospec_file = fopen(iospec_file_name, "r");
763 	if (!iospec_file) {
764 		snprintf(out, out_size, "Cannot open file \"%s\".\n", iospec_file_name);
765 		return;
766 	}
767 
768 	status = rte_swx_pipeline_build_from_lib(&p,
769 						 pipeline_name,
770 						 lib_file_name,
771 						 iospec_file,
772 						 (int)numa_node);
773 	if (status) {
774 		snprintf(out, out_size, "Pipeline build failed (%d).", status);
775 		goto free;
776 	}
777 
778 	ctl = rte_swx_ctl_pipeline_create(p);
779 	if (!ctl) {
780 		snprintf(out, out_size, "Pipeline control create failed.");
781 		goto free;
782 	}
783 
784 free:
785 	if (status)
786 		rte_swx_pipeline_free(p);
787 
788 	if (iospec_file)
789 		fclose(iospec_file);
790 }
791 
792 static void
793 table_entry_free(struct rte_swx_table_entry *entry)
794 {
795 	if (!entry)
796 		return;
797 
798 	free(entry->key);
799 	free(entry->key_mask);
800 	free(entry->action_data);
801 	free(entry);
802 }
803 
804 static int
805 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
806 			   const char *table_name,
807 			   FILE *file,
808 			   uint32_t *file_line_number)
809 {
810 	char *line = NULL;
811 	uint32_t line_id = 0;
812 	int status = 0;
813 
814 	/* Buffer allocation. */
815 	line = malloc(MAX_LINE_SIZE);
816 	if (!line)
817 		return -ENOMEM;
818 
819 	/* File read. */
820 	for (line_id = 1; ; line_id++) {
821 		struct rte_swx_table_entry *entry;
822 		int is_blank_or_comment;
823 
824 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
825 			break;
826 
827 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
828 							      table_name,
829 							      line,
830 							      &is_blank_or_comment);
831 		if (!entry) {
832 			if (is_blank_or_comment)
833 				continue;
834 
835 			status = -EINVAL;
836 			goto error;
837 		}
838 
839 		status = rte_swx_ctl_pipeline_table_entry_add(p,
840 							      table_name,
841 							      entry);
842 		table_entry_free(entry);
843 		if (status)
844 			goto error;
845 	}
846 
847 error:
848 	free(line);
849 	*file_line_number = line_id;
850 	return status;
851 }
852 
853 static const char cmd_pipeline_table_add_help[] =
854 "pipeline <pipeline_name> table <table_name> add <file_name>\n";
855 
856 static void
857 cmd_pipeline_table_add(char **tokens,
858 		       uint32_t n_tokens,
859 		       char *out,
860 		       size_t out_size,
861 		       void *obj)
862 {
863 	struct pipeline *p;
864 	char *pipeline_name, *table_name, *file_name;
865 	FILE *file = NULL;
866 	uint32_t file_line_number = 0;
867 	int status;
868 
869 	if (n_tokens != 6) {
870 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
871 		return;
872 	}
873 
874 	pipeline_name = tokens[1];
875 	p = pipeline_find(obj, pipeline_name);
876 	if (!p || !p->ctl) {
877 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
878 		return;
879 	}
880 
881 	table_name = tokens[3];
882 
883 	file_name = tokens[5];
884 	file = fopen(file_name, "r");
885 	if (!file) {
886 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
887 		return;
888 	}
889 
890 	status = pipeline_table_entries_add(p->ctl,
891 					    table_name,
892 					    file,
893 					    &file_line_number);
894 	if (status)
895 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
896 			 file_name,
897 			 file_line_number);
898 
899 	fclose(file);
900 }
901 
902 static int
903 pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
904 			      const char *table_name,
905 			      FILE *file,
906 			      uint32_t *file_line_number)
907 {
908 	char *line = NULL;
909 	uint32_t line_id = 0;
910 	int status = 0;
911 
912 	/* Buffer allocation. */
913 	line = malloc(MAX_LINE_SIZE);
914 	if (!line)
915 		return -ENOMEM;
916 
917 	/* File read. */
918 	for (line_id = 1; ; line_id++) {
919 		struct rte_swx_table_entry *entry;
920 		int is_blank_or_comment;
921 
922 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
923 			break;
924 
925 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
926 							      table_name,
927 							      line,
928 							      &is_blank_or_comment);
929 		if (!entry) {
930 			if (is_blank_or_comment)
931 				continue;
932 
933 			status = -EINVAL;
934 			goto error;
935 		}
936 
937 		status = rte_swx_ctl_pipeline_table_entry_delete(p,
938 								 table_name,
939 								 entry);
940 		table_entry_free(entry);
941 		if (status)
942 			goto error;
943 	}
944 
945 error:
946 	*file_line_number = line_id;
947 	free(line);
948 	return status;
949 }
950 
951 static const char cmd_pipeline_table_delete_help[] =
952 "pipeline <pipeline_name> table <table_name> delete <file_name>\n";
953 
954 static void
955 cmd_pipeline_table_delete(char **tokens,
956 			  uint32_t n_tokens,
957 			  char *out,
958 			  size_t out_size,
959 			  void *obj)
960 {
961 	struct pipeline *p;
962 	char *pipeline_name, *table_name, *file_name;
963 	FILE *file = NULL;
964 	uint32_t file_line_number = 0;
965 	int status;
966 
967 	if (n_tokens != 6) {
968 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
969 		return;
970 	}
971 
972 	pipeline_name = tokens[1];
973 	p = pipeline_find(obj, pipeline_name);
974 	if (!p || !p->ctl) {
975 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
976 		return;
977 	}
978 
979 	table_name = tokens[3];
980 
981 	file_name = tokens[5];
982 	file = fopen(file_name, "r");
983 	if (!file) {
984 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
985 		return;
986 	}
987 
988 	status = pipeline_table_entries_delete(p->ctl,
989 					       table_name,
990 					       file,
991 					       &file_line_number);
992 	if (status)
993 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
994 			 file_name,
995 			 file_line_number);
996 
997 	fclose(file);
998 }
999 
1000 static int
1001 pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
1002 				 const char *table_name,
1003 				 FILE *file,
1004 				 uint32_t *file_line_number)
1005 {
1006 	char *line = NULL;
1007 	uint32_t line_id = 0;
1008 	int status = 0;
1009 
1010 	/* Buffer allocation. */
1011 	line = malloc(MAX_LINE_SIZE);
1012 	if (!line)
1013 		return -ENOMEM;
1014 
1015 	/* File read. */
1016 	for (line_id = 1; ; line_id++) {
1017 		struct rte_swx_table_entry *entry;
1018 		int is_blank_or_comment;
1019 
1020 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1021 			break;
1022 
1023 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
1024 							      table_name,
1025 							      line,
1026 							      &is_blank_or_comment);
1027 		if (!entry) {
1028 			if (is_blank_or_comment)
1029 				continue;
1030 
1031 			status = -EINVAL;
1032 			goto error;
1033 		}
1034 
1035 		status = rte_swx_ctl_pipeline_table_default_entry_add(p,
1036 								      table_name,
1037 								      entry);
1038 		table_entry_free(entry);
1039 		if (status)
1040 			goto error;
1041 	}
1042 
1043 error:
1044 	*file_line_number = line_id;
1045 	free(line);
1046 	return status;
1047 }
1048 
1049 static const char cmd_pipeline_table_default_help[] =
1050 "pipeline <pipeline_name> table <table_name> default <file_name>\n";
1051 
1052 static void
1053 cmd_pipeline_table_default(char **tokens,
1054 			   uint32_t n_tokens,
1055 			   char *out,
1056 			   size_t out_size,
1057 			   void *obj)
1058 {
1059 	struct pipeline *p;
1060 	char *pipeline_name, *table_name, *file_name;
1061 	FILE *file = NULL;
1062 	uint32_t file_line_number = 0;
1063 	int status;
1064 
1065 	if (n_tokens != 6) {
1066 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1067 		return;
1068 	}
1069 
1070 	pipeline_name = tokens[1];
1071 	p = pipeline_find(obj, pipeline_name);
1072 	if (!p || !p->ctl) {
1073 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1074 		return;
1075 	}
1076 
1077 	table_name = tokens[3];
1078 
1079 	file_name = tokens[5];
1080 	file = fopen(file_name, "r");
1081 	if (!file) {
1082 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1083 		return;
1084 	}
1085 
1086 	status = pipeline_table_default_entry_add(p->ctl,
1087 						  table_name,
1088 						  file,
1089 						  &file_line_number);
1090 	if (status)
1091 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1092 			 file_name,
1093 			 file_line_number);
1094 
1095 	fclose(file);
1096 }
1097 
1098 static const char cmd_pipeline_table_show_help[] =
1099 "pipeline <pipeline_name> table <table_name> show [filename]\n";
1100 
1101 static void
1102 cmd_pipeline_table_show(char **tokens,
1103 	uint32_t n_tokens,
1104 	char *out,
1105 	size_t out_size,
1106 	void *obj)
1107 {
1108 	struct pipeline *p;
1109 	char *pipeline_name, *table_name;
1110 	FILE *file = NULL;
1111 	int status;
1112 
1113 	if (n_tokens != 5 && n_tokens != 6) {
1114 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1115 		return;
1116 	}
1117 
1118 	pipeline_name = tokens[1];
1119 	p = pipeline_find(obj, pipeline_name);
1120 	if (!p || !p->ctl) {
1121 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1122 		return;
1123 	}
1124 
1125 	table_name = tokens[3];
1126 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1127 	if (!file) {
1128 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1129 		return;
1130 	}
1131 
1132 	status = rte_swx_ctl_pipeline_table_fprintf(file, p->ctl, table_name);
1133 	if (status)
1134 		snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
1135 
1136 	if (file)
1137 		fclose(file);
1138 }
1139 
1140 static const char cmd_pipeline_selector_group_add_help[] =
1141 "pipeline <pipeline_name> selector <selector_name> group add\n";
1142 
1143 static void
1144 cmd_pipeline_selector_group_add(char **tokens,
1145 	uint32_t n_tokens,
1146 	char *out,
1147 	size_t out_size,
1148 	void *obj)
1149 {
1150 	struct pipeline *p;
1151 	char *pipeline_name, *selector_name;
1152 	uint32_t group_id;
1153 	int status;
1154 
1155 	if (n_tokens != 6) {
1156 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1157 		return;
1158 	}
1159 
1160 	pipeline_name = tokens[1];
1161 	p = pipeline_find(obj, pipeline_name);
1162 	if (!p || !p->ctl) {
1163 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1164 		return;
1165 	}
1166 
1167 	if (strcmp(tokens[2], "selector") != 0) {
1168 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1169 		return;
1170 	}
1171 
1172 	selector_name = tokens[3];
1173 
1174 	if (strcmp(tokens[4], "group") ||
1175 		strcmp(tokens[5], "add")) {
1176 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
1177 		return;
1178 	}
1179 
1180 	status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
1181 		selector_name,
1182 		&group_id);
1183 	if (status)
1184 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1185 	else
1186 		snprintf(out, out_size, "Group ID: %u\n", group_id);
1187 }
1188 
1189 static const char cmd_pipeline_selector_group_delete_help[] =
1190 "pipeline <pipeline_name> selector <selector_name> group delete <group_id>\n";
1191 
1192 static void
1193 cmd_pipeline_selector_group_delete(char **tokens,
1194 	uint32_t n_tokens,
1195 	char *out,
1196 	size_t out_size,
1197 	void *obj)
1198 {
1199 	struct pipeline *p;
1200 	char *pipeline_name, *selector_name;
1201 	uint32_t group_id;
1202 	int status;
1203 
1204 	if (n_tokens != 7) {
1205 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1206 		return;
1207 	}
1208 
1209 	pipeline_name = tokens[1];
1210 	p = pipeline_find(obj, pipeline_name);
1211 	if (!p || !p->ctl) {
1212 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1213 		return;
1214 	}
1215 
1216 	if (strcmp(tokens[2], "selector") != 0) {
1217 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1218 		return;
1219 	}
1220 
1221 	selector_name = tokens[3];
1222 
1223 	if (strcmp(tokens[4], "group") ||
1224 		strcmp(tokens[5], "delete")) {
1225 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
1226 		return;
1227 	}
1228 
1229 	if (parser_read_uint32(&group_id, tokens[6]) != 0) {
1230 		snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
1231 		return;
1232 	}
1233 
1234 	status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
1235 		selector_name,
1236 		group_id);
1237 	if (status)
1238 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1239 }
1240 
1241 #define GROUP_MEMBER_INFO_TOKENS_MAX 6
1242 
1243 static int
1244 token_is_comment(const char *token)
1245 {
1246 	if ((token[0] == '#') ||
1247 	    (token[0] == ';') ||
1248 	    ((token[0] == '/') && (token[1] == '/')))
1249 		return 1; /* TRUE. */
1250 
1251 	return 0; /* FALSE. */
1252 }
1253 
1254 static int
1255 pipeline_selector_group_member_read(const char *string,
1256 				      uint32_t *group_id,
1257 				      uint32_t *member_id,
1258 				      uint32_t *weight,
1259 				      int *is_blank_or_comment)
1260 {
1261 	char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
1262 	char *s0 = NULL, *s;
1263 	uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
1264 	int blank_or_comment = 0;
1265 
1266 	/* Check input arguments. */
1267 	if (!string || !string[0])
1268 		goto error;
1269 
1270 	/* Memory allocation. */
1271 	s0 = strdup(string);
1272 	if (!s0)
1273 		goto error;
1274 
1275 	/* Parse the string into tokens. */
1276 	for (s = s0; ; ) {
1277 		char *token;
1278 
1279 		token = strtok_r(s, " \f\n\r\t\v", &s);
1280 		if (!token || token_is_comment(token))
1281 			break;
1282 
1283 		if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
1284 			goto error;
1285 
1286 		token_array[n_tokens] = token;
1287 		n_tokens++;
1288 	}
1289 
1290 	if (!n_tokens) {
1291 		blank_or_comment = 1;
1292 		goto error;
1293 	}
1294 
1295 	tokens = token_array;
1296 
1297 	if (n_tokens < 4 ||
1298 		strcmp(tokens[0], "group") ||
1299 		strcmp(tokens[2], "member"))
1300 		goto error;
1301 
1302 	/*
1303 	 * Group ID.
1304 	 */
1305 	if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
1306 		goto error;
1307 	*group_id = group_id_val;
1308 
1309 	/*
1310 	 * Member ID.
1311 	 */
1312 	if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
1313 		goto error;
1314 	*member_id = member_id_val;
1315 
1316 	tokens += 4;
1317 	n_tokens -= 4;
1318 
1319 	/*
1320 	 * Weight.
1321 	 */
1322 	if (n_tokens && !strcmp(tokens[0], "weight")) {
1323 		if (n_tokens < 2)
1324 			goto error;
1325 
1326 		if (parser_read_uint32(&weight_val, tokens[1]) != 0)
1327 			goto error;
1328 		*weight = weight_val;
1329 
1330 		tokens += 2;
1331 		n_tokens -= 2;
1332 	}
1333 
1334 	if (n_tokens)
1335 		goto error;
1336 
1337 	free(s0);
1338 	return 0;
1339 
1340 error:
1341 	free(s0);
1342 	if (is_blank_or_comment)
1343 		*is_blank_or_comment = blank_or_comment;
1344 	return -EINVAL;
1345 }
1346 
1347 static int
1348 pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
1349 			   const char *selector_name,
1350 			   FILE *file,
1351 			   uint32_t *file_line_number)
1352 {
1353 	char *line = NULL;
1354 	uint32_t line_id = 0;
1355 	int status = 0;
1356 
1357 	/* Buffer allocation. */
1358 	line = malloc(MAX_LINE_SIZE);
1359 	if (!line)
1360 		return -ENOMEM;
1361 
1362 	/* File read. */
1363 	for (line_id = 1; ; line_id++) {
1364 		uint32_t group_id, member_id, weight;
1365 		int is_blank_or_comment;
1366 
1367 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1368 			break;
1369 
1370 		status = pipeline_selector_group_member_read(line,
1371 							      &group_id,
1372 							      &member_id,
1373 							      &weight,
1374 							      &is_blank_or_comment);
1375 		if (status) {
1376 			if (is_blank_or_comment)
1377 				continue;
1378 
1379 			goto error;
1380 		}
1381 
1382 		status = rte_swx_ctl_pipeline_selector_group_member_add(p,
1383 			selector_name,
1384 			group_id,
1385 			member_id,
1386 			weight);
1387 		if (status)
1388 			goto error;
1389 	}
1390 
1391 error:
1392 	free(line);
1393 	*file_line_number = line_id;
1394 	return status;
1395 }
1396 
1397 static const char cmd_pipeline_selector_group_member_add_help[] =
1398 "pipeline <pipeline_name> selector <selector_name> group member add <file_name>";
1399 
1400 static void
1401 cmd_pipeline_selector_group_member_add(char **tokens,
1402 	uint32_t n_tokens,
1403 	char *out,
1404 	size_t out_size,
1405 	void *obj)
1406 {
1407 	struct pipeline *p;
1408 	char *pipeline_name, *selector_name, *file_name;
1409 	FILE *file = NULL;
1410 	uint32_t file_line_number = 0;
1411 	int status;
1412 
1413 	if (n_tokens != 8) {
1414 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1415 		return;
1416 	}
1417 
1418 	pipeline_name = tokens[1];
1419 	p = pipeline_find(obj, pipeline_name);
1420 	if (!p || !p->ctl) {
1421 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1422 		return;
1423 	}
1424 
1425 	if (strcmp(tokens[2], "selector") != 0) {
1426 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1427 		return;
1428 	}
1429 
1430 	selector_name = tokens[3];
1431 
1432 	if (strcmp(tokens[4], "group") ||
1433 		strcmp(tokens[5], "member") ||
1434 		strcmp(tokens[6], "add")) {
1435 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
1436 		return;
1437 	}
1438 
1439 	file_name = tokens[7];
1440 	file = fopen(file_name, "r");
1441 	if (!file) {
1442 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1443 		return;
1444 	}
1445 
1446 	status = pipeline_selector_group_members_add(p->ctl,
1447 					    selector_name,
1448 					    file,
1449 					    &file_line_number);
1450 	if (status)
1451 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1452 			 file_name,
1453 			 file_line_number);
1454 
1455 	fclose(file);
1456 }
1457 
1458 static int
1459 pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
1460 			   const char *selector_name,
1461 			   FILE *file,
1462 			   uint32_t *file_line_number)
1463 {
1464 	char *line = NULL;
1465 	uint32_t line_id = 0;
1466 	int status = 0;
1467 
1468 	/* Buffer allocation. */
1469 	line = malloc(MAX_LINE_SIZE);
1470 	if (!line)
1471 		return -ENOMEM;
1472 
1473 	/* File read. */
1474 	for (line_id = 1; ; line_id++) {
1475 		uint32_t group_id, member_id, weight;
1476 		int is_blank_or_comment;
1477 
1478 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1479 			break;
1480 
1481 		status = pipeline_selector_group_member_read(line,
1482 							      &group_id,
1483 							      &member_id,
1484 							      &weight,
1485 							      &is_blank_or_comment);
1486 		if (status) {
1487 			if (is_blank_or_comment)
1488 				continue;
1489 
1490 			goto error;
1491 		}
1492 
1493 		status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
1494 			selector_name,
1495 			group_id,
1496 			member_id);
1497 		if (status)
1498 			goto error;
1499 	}
1500 
1501 error:
1502 	free(line);
1503 	*file_line_number = line_id;
1504 	return status;
1505 }
1506 
1507 static const char cmd_pipeline_selector_group_member_delete_help[] =
1508 "pipeline <pipeline_name> selector <selector_name> group member delete <file_name>";
1509 
1510 static void
1511 cmd_pipeline_selector_group_member_delete(char **tokens,
1512 	uint32_t n_tokens,
1513 	char *out,
1514 	size_t out_size,
1515 	void *obj)
1516 {
1517 	struct pipeline *p;
1518 	char *pipeline_name, *selector_name, *file_name;
1519 	FILE *file = NULL;
1520 	uint32_t file_line_number = 0;
1521 	int status;
1522 
1523 	if (n_tokens != 8) {
1524 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1525 		return;
1526 	}
1527 
1528 	pipeline_name = tokens[1];
1529 	p = pipeline_find(obj, pipeline_name);
1530 	if (!p || !p->ctl) {
1531 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1532 		return;
1533 	}
1534 
1535 	if (strcmp(tokens[2], "selector") != 0) {
1536 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1537 		return;
1538 	}
1539 
1540 	selector_name = tokens[3];
1541 
1542 	if (strcmp(tokens[4], "group") ||
1543 		strcmp(tokens[5], "member") ||
1544 		strcmp(tokens[6], "delete")) {
1545 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
1546 		return;
1547 	}
1548 
1549 	file_name = tokens[7];
1550 	file = fopen(file_name, "r");
1551 	if (!file) {
1552 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1553 		return;
1554 	}
1555 
1556 	status = pipeline_selector_group_members_delete(p->ctl,
1557 					    selector_name,
1558 					    file,
1559 					    &file_line_number);
1560 	if (status)
1561 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1562 			 file_name,
1563 			 file_line_number);
1564 
1565 	fclose(file);
1566 }
1567 
1568 static const char cmd_pipeline_selector_show_help[] =
1569 "pipeline <pipeline_name> selector <selector_name> show [filename]\n";
1570 
1571 static void
1572 cmd_pipeline_selector_show(char **tokens,
1573 	uint32_t n_tokens,
1574 	char *out,
1575 	size_t out_size,
1576 	void *obj)
1577 {
1578 	struct pipeline *p;
1579 	char *pipeline_name, *selector_name;
1580 	FILE *file = NULL;
1581 	int status;
1582 
1583 	if (n_tokens != 5 && n_tokens != 6) {
1584 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1585 		return;
1586 	}
1587 
1588 	pipeline_name = tokens[1];
1589 	p = pipeline_find(obj, pipeline_name);
1590 	if (!p || !p->ctl) {
1591 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1592 		return;
1593 	}
1594 
1595 	selector_name = tokens[3];
1596 
1597 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1598 	if (!file) {
1599 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1600 		return;
1601 	}
1602 
1603 	status = rte_swx_ctl_pipeline_selector_fprintf(file, p->ctl, selector_name);
1604 	if (status)
1605 		snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
1606 
1607 	if (file)
1608 		fclose(file);
1609 }
1610 
1611 static int
1612 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1613 				   const char *learner_name,
1614 				   FILE *file,
1615 				   uint32_t *file_line_number)
1616 {
1617 	char *line = NULL;
1618 	uint32_t line_id = 0;
1619 	int status = 0;
1620 
1621 	/* Buffer allocation. */
1622 	line = malloc(MAX_LINE_SIZE);
1623 	if (!line)
1624 		return -ENOMEM;
1625 
1626 	/* File read. */
1627 	for (line_id = 1; ; line_id++) {
1628 		struct rte_swx_table_entry *entry;
1629 		int is_blank_or_comment;
1630 
1631 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1632 			break;
1633 
1634 		entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1635 									learner_name,
1636 									line,
1637 									&is_blank_or_comment);
1638 		if (!entry) {
1639 			if (is_blank_or_comment)
1640 				continue;
1641 
1642 			status = -EINVAL;
1643 			goto error;
1644 		}
1645 
1646 		status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1647 									learner_name,
1648 									entry);
1649 		table_entry_free(entry);
1650 		if (status)
1651 			goto error;
1652 	}
1653 
1654 error:
1655 	*file_line_number = line_id;
1656 	free(line);
1657 	return status;
1658 }
1659 
1660 static const char cmd_pipeline_learner_default_help[] =
1661 "pipeline <pipeline_name> learner <learner_name> default <file_name>\n";
1662 
1663 static void
1664 cmd_pipeline_learner_default(char **tokens,
1665 			     uint32_t n_tokens,
1666 			     char *out,
1667 			     size_t out_size,
1668 			     void *obj)
1669 {
1670 	struct pipeline *p;
1671 	char *pipeline_name, *learner_name, *file_name;
1672 	FILE *file = NULL;
1673 	uint32_t file_line_number = 0;
1674 	int status;
1675 
1676 	if (n_tokens != 6) {
1677 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1678 		return;
1679 	}
1680 
1681 	pipeline_name = tokens[1];
1682 	p = pipeline_find(obj, pipeline_name);
1683 	if (!p || !p->ctl) {
1684 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1685 		return;
1686 	}
1687 
1688 	learner_name = tokens[3];
1689 
1690 	file_name = tokens[5];
1691 	file = fopen(file_name, "r");
1692 	if (!file) {
1693 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1694 		return;
1695 	}
1696 
1697 	status = pipeline_learner_default_entry_add(p->ctl,
1698 						    learner_name,
1699 						    file,
1700 						    &file_line_number);
1701 	if (status)
1702 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1703 			 file_name,
1704 			 file_line_number);
1705 
1706 	fclose(file);
1707 }
1708 
1709 static const char cmd_pipeline_commit_help[] =
1710 "pipeline <pipeline_name> commit\n";
1711 
1712 static void
1713 cmd_pipeline_commit(char **tokens,
1714 	uint32_t n_tokens,
1715 	char *out,
1716 	size_t out_size,
1717 	void *obj)
1718 {
1719 	struct pipeline *p;
1720 	char *pipeline_name;
1721 	int status;
1722 
1723 	if (n_tokens != 3) {
1724 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1725 		return;
1726 	}
1727 
1728 	pipeline_name = tokens[1];
1729 	p = pipeline_find(obj, pipeline_name);
1730 	if (!p || !p->ctl) {
1731 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1732 		return;
1733 	}
1734 
1735 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1736 	if (status)
1737 		snprintf(out, out_size, "Commit failed. "
1738 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1739 }
1740 
1741 static const char cmd_pipeline_abort_help[] =
1742 "pipeline <pipeline_name> abort\n";
1743 
1744 static void
1745 cmd_pipeline_abort(char **tokens,
1746 	uint32_t n_tokens,
1747 	char *out,
1748 	size_t out_size,
1749 	void *obj)
1750 {
1751 	struct pipeline *p;
1752 	char *pipeline_name;
1753 
1754 	if (n_tokens != 3) {
1755 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1756 		return;
1757 	}
1758 
1759 	pipeline_name = tokens[1];
1760 	p = pipeline_find(obj, pipeline_name);
1761 	if (!p || !p->ctl) {
1762 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1763 		return;
1764 	}
1765 
1766 	rte_swx_ctl_pipeline_abort(p->ctl);
1767 }
1768 
1769 static const char cmd_pipeline_regrd_help[] =
1770 "pipeline <pipeline_name> regrd <register_array_name> <index>\n";
1771 
1772 static void
1773 cmd_pipeline_regrd(char **tokens,
1774 	uint32_t n_tokens,
1775 	char *out,
1776 	size_t out_size,
1777 	void *obj)
1778 {
1779 	struct pipeline *p;
1780 	const char *name;
1781 	uint64_t value;
1782 	uint32_t idx;
1783 	int status;
1784 
1785 	if (n_tokens != 5) {
1786 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1787 		return;
1788 	}
1789 
1790 	p = pipeline_find(obj, tokens[1]);
1791 	if (!p || !p->ctl) {
1792 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1793 		return;
1794 	}
1795 
1796 	if (strcmp(tokens[2], "regrd")) {
1797 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
1798 		return;
1799 	}
1800 
1801 	name = tokens[3];
1802 
1803 	if (parser_read_uint32(&idx, tokens[4])) {
1804 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
1805 		return;
1806 	}
1807 
1808 	status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
1809 	if (status) {
1810 		snprintf(out, out_size, "Command failed.\n");
1811 		return;
1812 	}
1813 
1814 	snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1815 }
1816 
1817 static const char cmd_pipeline_regwr_help[] =
1818 "pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
1819 
1820 static void
1821 cmd_pipeline_regwr(char **tokens,
1822 	uint32_t n_tokens,
1823 	char *out,
1824 	size_t out_size,
1825 	void *obj)
1826 {
1827 	struct pipeline *p;
1828 	const char *name;
1829 	uint64_t value;
1830 	uint32_t idx;
1831 	int status;
1832 
1833 	if (n_tokens != 6) {
1834 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1835 		return;
1836 	}
1837 
1838 	p = pipeline_find(obj, tokens[1]);
1839 	if (!p || !p->ctl) {
1840 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1841 		return;
1842 	}
1843 
1844 	if (strcmp(tokens[2], "regwr")) {
1845 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
1846 		return;
1847 	}
1848 
1849 	name = tokens[3];
1850 
1851 	if (parser_read_uint32(&idx, tokens[4])) {
1852 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
1853 		return;
1854 	}
1855 
1856 	if (parser_read_uint64(&value, tokens[5])) {
1857 		snprintf(out, out_size, MSG_ARG_INVALID, "value");
1858 		return;
1859 	}
1860 
1861 	status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
1862 	if (status) {
1863 		snprintf(out, out_size, "Command failed.\n");
1864 		return;
1865 	}
1866 }
1867 
1868 static const char cmd_pipeline_meter_profile_add_help[] =
1869 "pipeline <pipeline_name> meter profile <profile_name> add "
1870 	"cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
1871 
1872 static void
1873 cmd_pipeline_meter_profile_add(char **tokens,
1874 	uint32_t n_tokens,
1875 	char *out,
1876 	size_t out_size,
1877 	void *obj)
1878 {
1879 	struct rte_meter_trtcm_params params;
1880 	struct pipeline *p;
1881 	const char *profile_name;
1882 	int status;
1883 
1884 	if (n_tokens != 14) {
1885 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1886 		return;
1887 	}
1888 
1889 	p = pipeline_find(obj, tokens[1]);
1890 	if (!p || !p->ctl) {
1891 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1892 		return;
1893 	}
1894 
1895 	if (strcmp(tokens[2], "meter")) {
1896 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1897 		return;
1898 	}
1899 
1900 	if (strcmp(tokens[3], "profile")) {
1901 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1902 		return;
1903 	}
1904 
1905 	profile_name = tokens[4];
1906 
1907 	if (strcmp(tokens[5], "add")) {
1908 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
1909 		return;
1910 	}
1911 
1912 	if (strcmp(tokens[6], "cir")) {
1913 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
1914 		return;
1915 	}
1916 
1917 	if (parser_read_uint64(&params.cir, tokens[7])) {
1918 		snprintf(out, out_size, MSG_ARG_INVALID, "cir");
1919 		return;
1920 	}
1921 
1922 	if (strcmp(tokens[8], "pir")) {
1923 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
1924 		return;
1925 	}
1926 
1927 	if (parser_read_uint64(&params.pir, tokens[9])) {
1928 		snprintf(out, out_size, MSG_ARG_INVALID, "pir");
1929 		return;
1930 	}
1931 
1932 	if (strcmp(tokens[10], "cbs")) {
1933 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
1934 		return;
1935 	}
1936 
1937 	if (parser_read_uint64(&params.cbs, tokens[11])) {
1938 		snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
1939 		return;
1940 	}
1941 
1942 	if (strcmp(tokens[12], "pbs")) {
1943 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
1944 		return;
1945 	}
1946 
1947 	if (parser_read_uint64(&params.pbs, tokens[13])) {
1948 		snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
1949 		return;
1950 	}
1951 
1952 	status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
1953 	if (status) {
1954 		snprintf(out, out_size, "Command failed.\n");
1955 		return;
1956 	}
1957 }
1958 
1959 static const char cmd_pipeline_meter_profile_delete_help[] =
1960 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
1961 
1962 static void
1963 cmd_pipeline_meter_profile_delete(char **tokens,
1964 	uint32_t n_tokens,
1965 	char *out,
1966 	size_t out_size,
1967 	void *obj)
1968 {
1969 	struct pipeline *p;
1970 	const char *profile_name;
1971 	int status;
1972 
1973 	if (n_tokens != 6) {
1974 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1975 		return;
1976 	}
1977 
1978 	p = pipeline_find(obj, tokens[1]);
1979 	if (!p || !p->ctl) {
1980 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1981 		return;
1982 	}
1983 
1984 	if (strcmp(tokens[2], "meter")) {
1985 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1986 		return;
1987 	}
1988 
1989 	if (strcmp(tokens[3], "profile")) {
1990 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1991 		return;
1992 	}
1993 
1994 	profile_name = tokens[4];
1995 
1996 	if (strcmp(tokens[5], "delete")) {
1997 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
1998 		return;
1999 	}
2000 
2001 	status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
2002 	if (status) {
2003 		snprintf(out, out_size, "Command failed.\n");
2004 		return;
2005 	}
2006 }
2007 
2008 static const char cmd_pipeline_meter_reset_help[] =
2009 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2010 	"reset\n";
2011 
2012 static void
2013 cmd_pipeline_meter_reset(char **tokens,
2014 	uint32_t n_tokens,
2015 	char *out,
2016 	size_t out_size,
2017 	void *obj)
2018 {
2019 	struct pipeline *p;
2020 	const char *name;
2021 	uint32_t idx0 = 0, idx1 = 0;
2022 
2023 	if (n_tokens != 9) {
2024 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2025 		return;
2026 	}
2027 
2028 	p = pipeline_find(obj, tokens[1]);
2029 	if (!p || !p->ctl) {
2030 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2031 		return;
2032 	}
2033 
2034 	if (strcmp(tokens[2], "meter")) {
2035 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2036 		return;
2037 	}
2038 
2039 	name = tokens[3];
2040 
2041 	if (strcmp(tokens[4], "from")) {
2042 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2043 		return;
2044 	}
2045 
2046 	if (parser_read_uint32(&idx0, tokens[5])) {
2047 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2048 		return;
2049 	}
2050 
2051 	if (strcmp(tokens[6], "to")) {
2052 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2053 		return;
2054 	}
2055 
2056 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2057 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2058 		return;
2059 	}
2060 
2061 	if (strcmp(tokens[8], "reset")) {
2062 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
2063 		return;
2064 	}
2065 
2066 	for ( ; idx0 <= idx1; idx0++) {
2067 		int status;
2068 
2069 		status = rte_swx_ctl_meter_reset(p->p, name, idx0);
2070 		if (status) {
2071 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2072 			return;
2073 		}
2074 	}
2075 }
2076 
2077 static const char cmd_pipeline_meter_set_help[] =
2078 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2079 	"set profile <profile_name>\n";
2080 
2081 static void
2082 cmd_pipeline_meter_set(char **tokens,
2083 	uint32_t n_tokens,
2084 	char *out,
2085 	size_t out_size,
2086 	void *obj)
2087 {
2088 	struct pipeline *p;
2089 	const char *name, *profile_name;
2090 	uint32_t idx0 = 0, idx1 = 0;
2091 
2092 	if (n_tokens != 11) {
2093 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2094 		return;
2095 	}
2096 
2097 	p = pipeline_find(obj, tokens[1]);
2098 	if (!p || !p->ctl) {
2099 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2100 		return;
2101 	}
2102 
2103 	if (strcmp(tokens[2], "meter")) {
2104 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2105 		return;
2106 	}
2107 
2108 	name = tokens[3];
2109 
2110 	if (strcmp(tokens[4], "from")) {
2111 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2112 		return;
2113 	}
2114 
2115 	if (parser_read_uint32(&idx0, tokens[5])) {
2116 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2117 		return;
2118 	}
2119 
2120 	if (strcmp(tokens[6], "to")) {
2121 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2122 		return;
2123 	}
2124 
2125 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2126 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2127 		return;
2128 	}
2129 
2130 	if (strcmp(tokens[8], "set")) {
2131 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
2132 		return;
2133 	}
2134 
2135 	if (strcmp(tokens[9], "profile")) {
2136 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2137 		return;
2138 	}
2139 
2140 	profile_name = tokens[10];
2141 
2142 	for ( ; idx0 <= idx1; idx0++) {
2143 		int status;
2144 
2145 		status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
2146 		if (status) {
2147 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2148 			return;
2149 		}
2150 	}
2151 }
2152 
2153 static const char cmd_pipeline_meter_stats_help[] =
2154 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2155 	"stats\n";
2156 
2157 static void
2158 cmd_pipeline_meter_stats(char **tokens,
2159 	uint32_t n_tokens,
2160 	char *out,
2161 	size_t out_size,
2162 	void *obj)
2163 {
2164 	struct rte_swx_ctl_meter_stats stats;
2165 	struct pipeline *p;
2166 	const char *name;
2167 	uint32_t idx0 = 0, idx1 = 0;
2168 
2169 	if (n_tokens != 9) {
2170 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2171 		return;
2172 	}
2173 
2174 	p = pipeline_find(obj, tokens[1]);
2175 	if (!p || !p->ctl) {
2176 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2177 		return;
2178 	}
2179 
2180 	if (strcmp(tokens[2], "meter")) {
2181 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2182 		return;
2183 	}
2184 
2185 	name = tokens[3];
2186 
2187 	if (strcmp(tokens[4], "from")) {
2188 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2189 		return;
2190 	}
2191 
2192 	if (parser_read_uint32(&idx0, tokens[5])) {
2193 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2194 		return;
2195 	}
2196 
2197 	if (strcmp(tokens[6], "to")) {
2198 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2199 		return;
2200 	}
2201 
2202 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2203 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2204 		return;
2205 	}
2206 
2207 	if (strcmp(tokens[8], "stats")) {
2208 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2209 		return;
2210 	}
2211 
2212 	/* Table header. */
2213 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2214 		 "-------",
2215 		 "----------------", "----------------", "----------------",
2216 		 "----------------", "----------------", "----------------");
2217 	out_size -= strlen(out);
2218 	out += strlen(out);
2219 
2220 	snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2221 		 "METER #",
2222 		 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2223 		 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2224 	out_size -= strlen(out);
2225 	out += strlen(out);
2226 
2227 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2228 		 "-------",
2229 		 "----------------", "----------------", "----------------",
2230 		 "----------------", "----------------", "----------------");
2231 	out_size -= strlen(out);
2232 	out += strlen(out);
2233 
2234 	/* Table rows. */
2235 	for ( ; idx0 <= idx1; idx0++) {
2236 		int status;
2237 
2238 		status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
2239 		if (status) {
2240 			snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
2241 			out_size -= strlen(out);
2242 			out += strlen(out);
2243 			return;
2244 		}
2245 
2246 		snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2247 			 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2248 			 idx0,
2249 			 stats.n_pkts[RTE_COLOR_GREEN],
2250 			 stats.n_pkts[RTE_COLOR_YELLOW],
2251 			 stats.n_pkts[RTE_COLOR_RED],
2252 			 stats.n_bytes[RTE_COLOR_GREEN],
2253 			 stats.n_bytes[RTE_COLOR_YELLOW],
2254 			 stats.n_bytes[RTE_COLOR_RED]);
2255 		out_size -= strlen(out);
2256 		out += strlen(out);
2257 	}
2258 }
2259 
2260 static const char cmd_pipeline_stats_help[] =
2261 "pipeline <pipeline_name> stats\n";
2262 
2263 static void
2264 cmd_pipeline_stats(char **tokens,
2265 	uint32_t n_tokens,
2266 	char *out,
2267 	size_t out_size,
2268 	void *obj)
2269 {
2270 	struct rte_swx_ctl_pipeline_info info;
2271 	struct pipeline *p;
2272 	uint32_t i;
2273 	int status;
2274 
2275 	if (n_tokens != 3) {
2276 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2277 		return;
2278 	}
2279 
2280 	p = pipeline_find(obj, tokens[1]);
2281 	if (!p || !p->ctl) {
2282 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2283 		return;
2284 	}
2285 
2286 	if (strcmp(tokens[2], "stats")) {
2287 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2288 		return;
2289 	}
2290 
2291 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
2292 	if (status) {
2293 		snprintf(out, out_size, "Pipeline info get error.");
2294 		return;
2295 	}
2296 
2297 	snprintf(out, out_size, "Input ports:\n");
2298 	out_size -= strlen(out);
2299 	out += strlen(out);
2300 
2301 	for (i = 0; i < info.n_ports_in; i++) {
2302 		struct rte_swx_port_in_stats stats;
2303 
2304 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
2305 
2306 		snprintf(out, out_size, "\tPort %u:"
2307 			" packets %" PRIu64
2308 			" bytes %" PRIu64
2309 			" empty %" PRIu64 "\n",
2310 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
2311 		out_size -= strlen(out);
2312 		out += strlen(out);
2313 	}
2314 
2315 	snprintf(out, out_size, "\nOutput ports:\n");
2316 	out_size -= strlen(out);
2317 	out += strlen(out);
2318 
2319 	for (i = 0; i < info.n_ports_out; i++) {
2320 		struct rte_swx_port_out_stats stats;
2321 
2322 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
2323 
2324 		if (i != info.n_ports_out - 1)
2325 			snprintf(out, out_size, "\tPort %u:", i);
2326 		else
2327 			snprintf(out, out_size, "\tDROP:");
2328 
2329 		out_size -= strlen(out);
2330 		out += strlen(out);
2331 
2332 		snprintf(out,
2333 			out_size,
2334 			" packets %" PRIu64
2335 			" bytes %" PRIu64
2336 			" clone %" PRIu64
2337 			" clonerr %" PRIu64 "\n",
2338 			stats.n_pkts,
2339 			stats.n_bytes,
2340 			stats.n_pkts_clone,
2341 			stats.n_pkts_clone_err);
2342 
2343 		out_size -= strlen(out);
2344 		out += strlen(out);
2345 	}
2346 
2347 	snprintf(out, out_size, "\nTables:\n");
2348 	out_size -= strlen(out);
2349 	out += strlen(out);
2350 
2351 	for (i = 0; i < info.n_tables; i++) {
2352 		struct rte_swx_ctl_table_info table_info;
2353 		uint64_t n_pkts_action[info.n_actions];
2354 		struct rte_swx_table_stats stats = {
2355 			.n_pkts_hit = 0,
2356 			.n_pkts_miss = 0,
2357 			.n_pkts_action = n_pkts_action,
2358 		};
2359 		uint32_t j;
2360 
2361 		status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
2362 		if (status) {
2363 			snprintf(out, out_size, "Table info get error.");
2364 			return;
2365 		}
2366 
2367 		status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
2368 		if (status) {
2369 			snprintf(out, out_size, "Table stats read error.");
2370 			return;
2371 		}
2372 
2373 		snprintf(out, out_size, "\tTable %s:\n"
2374 			"\t\tHit (packets): %" PRIu64 "\n"
2375 			"\t\tMiss (packets): %" PRIu64 "\n",
2376 			table_info.name,
2377 			stats.n_pkts_hit,
2378 			stats.n_pkts_miss);
2379 		out_size -= strlen(out);
2380 		out += strlen(out);
2381 
2382 		for (j = 0; j < info.n_actions; j++) {
2383 			struct rte_swx_ctl_action_info action_info;
2384 
2385 			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2386 			if (status) {
2387 				snprintf(out, out_size, "Action info get error.");
2388 				return;
2389 			}
2390 
2391 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2392 				action_info.name,
2393 				stats.n_pkts_action[j]);
2394 			out_size -= strlen(out);
2395 			out += strlen(out);
2396 		}
2397 	}
2398 
2399 	snprintf(out, out_size, "\nLearner tables:\n");
2400 	out_size -= strlen(out);
2401 	out += strlen(out);
2402 
2403 	for (i = 0; i < info.n_learners; i++) {
2404 		struct rte_swx_ctl_learner_info learner_info;
2405 		uint64_t n_pkts_action[info.n_actions];
2406 		struct rte_swx_learner_stats stats = {
2407 			.n_pkts_hit = 0,
2408 			.n_pkts_miss = 0,
2409 			.n_pkts_action = n_pkts_action,
2410 		};
2411 		uint32_t j;
2412 
2413 		status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
2414 		if (status) {
2415 			snprintf(out, out_size, "Learner table info get error.");
2416 			return;
2417 		}
2418 
2419 		status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
2420 		if (status) {
2421 			snprintf(out, out_size, "Learner table stats read error.");
2422 			return;
2423 		}
2424 
2425 		snprintf(out, out_size, "\tLearner table %s:\n"
2426 			"\t\tHit (packets): %" PRIu64 "\n"
2427 			"\t\tMiss (packets): %" PRIu64 "\n"
2428 			"\t\tLearn OK (packets): %" PRIu64 "\n"
2429 			"\t\tLearn error (packets): %" PRIu64 "\n"
2430 			"\t\tRearm (packets): %" PRIu64 "\n"
2431 			"\t\tForget (packets): %" PRIu64 "\n",
2432 			learner_info.name,
2433 			stats.n_pkts_hit,
2434 			stats.n_pkts_miss,
2435 			stats.n_pkts_learn_ok,
2436 			stats.n_pkts_learn_err,
2437 			stats.n_pkts_rearm,
2438 			stats.n_pkts_forget);
2439 		out_size -= strlen(out);
2440 		out += strlen(out);
2441 
2442 		for (j = 0; j < info.n_actions; j++) {
2443 			struct rte_swx_ctl_action_info action_info;
2444 
2445 			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2446 			if (status) {
2447 				snprintf(out, out_size, "Action info get error.");
2448 				return;
2449 			}
2450 
2451 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2452 				action_info.name,
2453 				stats.n_pkts_action[j]);
2454 			out_size -= strlen(out);
2455 			out += strlen(out);
2456 		}
2457 	}
2458 }
2459 
2460 static const char cmd_pipeline_mirror_help[] =
2461 "pipeline <pipeline_name> mirror slots <n_slots> sessions <n_sessions>\n";
2462 
2463 static void
2464 cmd_pipeline_mirror(char **tokens,
2465 	uint32_t n_tokens,
2466 	char *out,
2467 	size_t out_size,
2468 	void *obj)
2469 {
2470 	struct rte_swx_pipeline_mirroring_params params;
2471 	struct pipeline *p;
2472 	int status;
2473 
2474 	if (n_tokens != 7) {
2475 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2476 		return;
2477 	}
2478 
2479 	if (strcmp(tokens[0], "pipeline")) {
2480 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2481 		return;
2482 	}
2483 
2484 	p = pipeline_find(obj, tokens[1]);
2485 	if (!p) {
2486 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2487 		return;
2488 	}
2489 
2490 	if (strcmp(tokens[2], "mirror")) {
2491 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2492 		return;
2493 	}
2494 
2495 	if (strcmp(tokens[3], "slots")) {
2496 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "slots");
2497 		return;
2498 	}
2499 
2500 	if (parser_read_uint32(&params.n_slots, tokens[4])) {
2501 		snprintf(out, out_size, MSG_ARG_INVALID, "n_slots");
2502 		return;
2503 	}
2504 
2505 	if (strcmp(tokens[5], "sessions")) {
2506 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sessions");
2507 		return;
2508 	}
2509 
2510 	if (parser_read_uint32(&params.n_sessions, tokens[6])) {
2511 		snprintf(out, out_size, MSG_ARG_INVALID, "n_sessions");
2512 		return;
2513 	}
2514 
2515 	status = rte_swx_pipeline_mirroring_config(p->p, &params);
2516 	if (status) {
2517 		snprintf(out, out_size, "Command failed!\n");
2518 		return;
2519 	}
2520 }
2521 
2522 static const char cmd_pipeline_mirror_session_help[] =
2523 "pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow "
2524 "truncate <truncation_length>\n";
2525 
2526 static void
2527 cmd_pipeline_mirror_session(char **tokens,
2528 	uint32_t n_tokens,
2529 	char *out,
2530 	size_t out_size,
2531 	void *obj)
2532 {
2533 	struct rte_swx_pipeline_mirroring_session_params params;
2534 	struct pipeline *p;
2535 	uint32_t session_id = 0;
2536 	int status;
2537 
2538 	if (n_tokens != 11) {
2539 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2540 		return;
2541 	}
2542 
2543 	if (strcmp(tokens[0], "pipeline")) {
2544 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2545 		return;
2546 	}
2547 
2548 	p = pipeline_find(obj, tokens[1]);
2549 	if (!p || !p->ctl) {
2550 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2551 		return;
2552 	}
2553 
2554 	if (strcmp(tokens[2], "mirror")) {
2555 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2556 		return;
2557 	}
2558 
2559 	if (strcmp(tokens[3], "session")) {
2560 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "session");
2561 		return;
2562 	}
2563 
2564 	if (parser_read_uint32(&session_id, tokens[4])) {
2565 		snprintf(out, out_size, MSG_ARG_INVALID, "session_id");
2566 		return;
2567 	}
2568 
2569 	if (strcmp(tokens[5], "port")) {
2570 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2571 		return;
2572 	}
2573 
2574 	if (parser_read_uint32(&params.port_id, tokens[6])) {
2575 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2576 		return;
2577 	}
2578 
2579 	if (strcmp(tokens[7], "clone")) {
2580 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "clone");
2581 		return;
2582 	}
2583 
2584 	if (!strcmp(tokens[8], "fast"))
2585 		params.fast_clone = 1;
2586 	else if (!strcmp(tokens[8], "slow"))
2587 		params.fast_clone = 0;
2588 	else {
2589 		snprintf(out, out_size, MSG_ARG_INVALID, "clone");
2590 		return;
2591 	}
2592 
2593 	if (strcmp(tokens[9], "truncate")) {
2594 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "truncate");
2595 		return;
2596 	}
2597 
2598 	if (parser_read_uint32(&params.truncation_length, tokens[10])) {
2599 		snprintf(out, out_size, MSG_ARG_INVALID, "truncation_length");
2600 		return;
2601 	}
2602 
2603 	status = rte_swx_ctl_pipeline_mirroring_session_set(p->p, session_id, &params);
2604 	if (status) {
2605 		snprintf(out, out_size, "Command failed!\n");
2606 		return;
2607 	}
2608 }
2609 
2610 static const char cmd_thread_pipeline_enable_help[] =
2611 "thread <thread_id> pipeline <pipeline_name> enable\n";
2612 
2613 static void
2614 cmd_thread_pipeline_enable(char **tokens,
2615 	uint32_t n_tokens,
2616 	char *out,
2617 	size_t out_size,
2618 	void *obj)
2619 {
2620 	char *pipeline_name;
2621 	struct pipeline *p;
2622 	uint32_t thread_id;
2623 	int status;
2624 
2625 	if (n_tokens != 5) {
2626 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2627 		return;
2628 	}
2629 
2630 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2631 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2632 		return;
2633 	}
2634 
2635 	if (strcmp(tokens[2], "pipeline") != 0) {
2636 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2637 		return;
2638 	}
2639 
2640 	pipeline_name = tokens[3];
2641 	p = pipeline_find(obj, pipeline_name);
2642 	if (!p || !p->ctl) {
2643 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2644 		return;
2645 	}
2646 
2647 	if (strcmp(tokens[4], "enable") != 0) {
2648 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2649 		return;
2650 	}
2651 
2652 	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
2653 	if (status) {
2654 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
2655 		return;
2656 	}
2657 }
2658 
2659 static const char cmd_thread_pipeline_disable_help[] =
2660 "thread <thread_id> pipeline <pipeline_name> disable\n";
2661 
2662 static void
2663 cmd_thread_pipeline_disable(char **tokens,
2664 	uint32_t n_tokens,
2665 	char *out,
2666 	size_t out_size,
2667 	void *obj)
2668 {
2669 	struct pipeline *p;
2670 	char *pipeline_name;
2671 	uint32_t thread_id;
2672 	int status;
2673 
2674 	if (n_tokens != 5) {
2675 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2676 		return;
2677 	}
2678 
2679 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2680 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2681 		return;
2682 	}
2683 
2684 	if (strcmp(tokens[2], "pipeline") != 0) {
2685 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2686 		return;
2687 	}
2688 
2689 	pipeline_name = tokens[3];
2690 	p = pipeline_find(obj, pipeline_name);
2691 	if (!p || !p->ctl) {
2692 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2693 		return;
2694 	}
2695 
2696 	if (strcmp(tokens[4], "disable") != 0) {
2697 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2698 		return;
2699 	}
2700 
2701 	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
2702 	if (status) {
2703 		snprintf(out, out_size, MSG_CMD_FAIL,
2704 			"thread pipeline disable");
2705 		return;
2706 	}
2707 }
2708 
2709 static void
2710 cmd_help(char **tokens,
2711 	 uint32_t n_tokens,
2712 	 char *out,
2713 	 size_t out_size,
2714 	 void *arg __rte_unused)
2715 {
2716 	tokens++;
2717 	n_tokens--;
2718 
2719 	if (n_tokens == 0) {
2720 		snprintf(out, out_size,
2721 			"Type 'help <command>' for command details.\n\n"
2722 			"List of commands:\n"
2723 			"\tmempool\n"
2724 			"\tlink\n"
2725 			"\ttap\n"
2726 			"\tpipeline codegen\n"
2727 			"\tpipeline libbuild\n"
2728 			"\tpipeline build\n"
2729 			"\tpipeline table add\n"
2730 			"\tpipeline table delete\n"
2731 			"\tpipeline table default\n"
2732 			"\tpipeline table show\n"
2733 			"\tpipeline selector group add\n"
2734 			"\tpipeline selector group delete\n"
2735 			"\tpipeline selector group member add\n"
2736 			"\tpipeline selector group member delete\n"
2737 			"\tpipeline selector show\n"
2738 			"\tpipeline learner default\n"
2739 			"\tpipeline commit\n"
2740 			"\tpipeline abort\n"
2741 			"\tpipeline regrd\n"
2742 			"\tpipeline regwr\n"
2743 			"\tpipeline meter profile add\n"
2744 			"\tpipeline meter profile delete\n"
2745 			"\tpipeline meter reset\n"
2746 			"\tpipeline meter set\n"
2747 			"\tpipeline meter stats\n"
2748 			"\tpipeline stats\n"
2749 			"\tpipeline mirror\n"
2750 			"\tpipeline mirror session\n"
2751 			"\tthread pipeline enable\n"
2752 			"\tthread pipeline disable\n\n");
2753 		return;
2754 	}
2755 
2756 	if (strcmp(tokens[0], "mempool") == 0) {
2757 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
2758 		return;
2759 	}
2760 
2761 	if (strcmp(tokens[0], "link") == 0) {
2762 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
2763 		return;
2764 	}
2765 
2766 	if (strcmp(tokens[0], "ring") == 0) {
2767 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
2768 		return;
2769 	}
2770 
2771 	if (strcmp(tokens[0], "tap") == 0) {
2772 		snprintf(out, out_size, "\n%s\n", cmd_tap_help);
2773 		return;
2774 	}
2775 
2776 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2777 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
2778 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
2779 		return;
2780 	}
2781 
2782 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2783 		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
2784 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
2785 		return;
2786 	}
2787 
2788 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2789 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
2790 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
2791 		return;
2792 	}
2793 
2794 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2795 		(n_tokens == 3) &&
2796 		(strcmp(tokens[1], "table") == 0) &&
2797 		(strcmp(tokens[2], "add") == 0)) {
2798 		snprintf(out, out_size, "\n%s\n",
2799 			cmd_pipeline_table_add_help);
2800 		return;
2801 	}
2802 
2803 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2804 		(n_tokens == 3) &&
2805 		(strcmp(tokens[1], "table") == 0) &&
2806 		(strcmp(tokens[2], "delete") == 0)) {
2807 		snprintf(out, out_size, "\n%s\n",
2808 			cmd_pipeline_table_delete_help);
2809 		return;
2810 	}
2811 
2812 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2813 		(n_tokens == 3) &&
2814 		(strcmp(tokens[1], "table") == 0) &&
2815 		(strcmp(tokens[2], "default") == 0)) {
2816 		snprintf(out, out_size, "\n%s\n",
2817 			cmd_pipeline_table_default_help);
2818 		return;
2819 	}
2820 
2821 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2822 		(n_tokens == 3) &&
2823 		(strcmp(tokens[1], "table") == 0) &&
2824 		(strcmp(tokens[2], "show") == 0)) {
2825 		snprintf(out, out_size, "\n%s\n",
2826 			cmd_pipeline_table_show_help);
2827 		return;
2828 	}
2829 
2830 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2831 		(n_tokens == 4) &&
2832 		(strcmp(tokens[1], "selector") == 0) &&
2833 		(strcmp(tokens[2], "group") == 0) &&
2834 		(strcmp(tokens[3], "add") == 0)) {
2835 		snprintf(out, out_size, "\n%s\n",
2836 			cmd_pipeline_selector_group_add_help);
2837 		return;
2838 	}
2839 
2840 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2841 		(n_tokens == 4) &&
2842 		(strcmp(tokens[1], "selector") == 0) &&
2843 		(strcmp(tokens[2], "group") == 0) &&
2844 		(strcmp(tokens[3], "delete") == 0)) {
2845 		snprintf(out, out_size, "\n%s\n",
2846 			cmd_pipeline_selector_group_delete_help);
2847 		return;
2848 	}
2849 
2850 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2851 		(n_tokens == 5) &&
2852 		(strcmp(tokens[1], "selector") == 0) &&
2853 		(strcmp(tokens[2], "group") == 0) &&
2854 		(strcmp(tokens[3], "member") == 0) &&
2855 		(strcmp(tokens[4], "add") == 0)) {
2856 		snprintf(out, out_size, "\n%s\n",
2857 			cmd_pipeline_selector_group_member_add_help);
2858 		return;
2859 	}
2860 
2861 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2862 		(n_tokens == 5) &&
2863 		(strcmp(tokens[1], "selector") == 0) &&
2864 		(strcmp(tokens[2], "group") == 0) &&
2865 		(strcmp(tokens[3], "member") == 0) &&
2866 		(strcmp(tokens[4], "delete") == 0)) {
2867 		snprintf(out, out_size, "\n%s\n",
2868 			cmd_pipeline_selector_group_member_delete_help);
2869 		return;
2870 	}
2871 
2872 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2873 		(n_tokens == 3) &&
2874 		(strcmp(tokens[1], "selector") == 0) &&
2875 		(strcmp(tokens[2], "show") == 0)) {
2876 		snprintf(out, out_size, "\n%s\n",
2877 			cmd_pipeline_selector_show_help);
2878 		return;
2879 	}
2880 
2881 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2882 		(n_tokens == 3) &&
2883 		(strcmp(tokens[1], "learner") == 0) &&
2884 		(strcmp(tokens[2], "default") == 0)) {
2885 		snprintf(out, out_size, "\n%s\n",
2886 			cmd_pipeline_learner_default_help);
2887 		return;
2888 	}
2889 
2890 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2891 		(n_tokens == 2) &&
2892 		(strcmp(tokens[1], "commit") == 0)) {
2893 		snprintf(out, out_size, "\n%s\n",
2894 			cmd_pipeline_commit_help);
2895 		return;
2896 	}
2897 
2898 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2899 		(n_tokens == 2) &&
2900 		(strcmp(tokens[1], "abort") == 0)) {
2901 		snprintf(out, out_size, "\n%s\n",
2902 			cmd_pipeline_abort_help);
2903 		return;
2904 	}
2905 
2906 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2907 		(n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
2908 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
2909 		return;
2910 	}
2911 
2912 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2913 		(n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
2914 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
2915 		return;
2916 	}
2917 
2918 	if (!strcmp(tokens[0], "pipeline") &&
2919 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
2920 		&& !strcmp(tokens[2], "profile")
2921 		&& !strcmp(tokens[3], "add")) {
2922 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
2923 		return;
2924 	}
2925 
2926 	if (!strcmp(tokens[0], "pipeline") &&
2927 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
2928 		&& !strcmp(tokens[2], "profile")
2929 		&& !strcmp(tokens[3], "delete")) {
2930 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
2931 		return;
2932 	}
2933 
2934 	if (!strcmp(tokens[0], "pipeline") &&
2935 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2936 		&& !strcmp(tokens[2], "reset")) {
2937 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
2938 		return;
2939 	}
2940 
2941 	if (!strcmp(tokens[0], "pipeline") &&
2942 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2943 		&& !strcmp(tokens[2], "set")) {
2944 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
2945 		return;
2946 	}
2947 
2948 	if (!strcmp(tokens[0], "pipeline") &&
2949 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2950 		&& !strcmp(tokens[2], "stats")) {
2951 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
2952 		return;
2953 	}
2954 
2955 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2956 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
2957 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
2958 		return;
2959 	}
2960 
2961 	if (!strcmp(tokens[0], "pipeline") &&
2962 		(n_tokens == 2) && !strcmp(tokens[1], "mirror")) {
2963 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_help);
2964 		return;
2965 	}
2966 
2967 	if (!strcmp(tokens[0], "pipeline") &&
2968 		(n_tokens == 3) && !strcmp(tokens[1], "mirror")
2969 		&& !strcmp(tokens[2], "session")) {
2970 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_session_help);
2971 		return;
2972 	}
2973 
2974 	if ((n_tokens == 3) &&
2975 		(strcmp(tokens[0], "thread") == 0) &&
2976 		(strcmp(tokens[1], "pipeline") == 0)) {
2977 		if (strcmp(tokens[2], "enable") == 0) {
2978 			snprintf(out, out_size, "\n%s\n",
2979 				cmd_thread_pipeline_enable_help);
2980 			return;
2981 		}
2982 
2983 		if (strcmp(tokens[2], "disable") == 0) {
2984 			snprintf(out, out_size, "\n%s\n",
2985 				cmd_thread_pipeline_disable_help);
2986 			return;
2987 		}
2988 	}
2989 
2990 	snprintf(out, out_size, "Invalid command\n");
2991 }
2992 
2993 void
2994 cli_process(char *in, char *out, size_t out_size, void *obj)
2995 {
2996 	char *tokens[CMD_MAX_TOKENS];
2997 	uint32_t n_tokens = RTE_DIM(tokens);
2998 	int status;
2999 
3000 	if (is_comment(in))
3001 		return;
3002 
3003 	status = parse_tokenize_string(in, tokens, &n_tokens);
3004 	if (status) {
3005 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3006 		return;
3007 	}
3008 
3009 	if (n_tokens == 0)
3010 		return;
3011 
3012 	if (strcmp(tokens[0], "help") == 0) {
3013 		cmd_help(tokens, n_tokens, out, out_size, obj);
3014 		return;
3015 	}
3016 
3017 	if (strcmp(tokens[0], "mempool") == 0) {
3018 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
3019 		return;
3020 	}
3021 
3022 	if (strcmp(tokens[0], "link") == 0) {
3023 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3024 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
3025 			return;
3026 		}
3027 
3028 		cmd_link(tokens, n_tokens, out, out_size, obj);
3029 		return;
3030 	}
3031 
3032 	if (strcmp(tokens[0], "ring") == 0) {
3033 		cmd_ring(tokens, n_tokens, out, out_size, obj);
3034 		return;
3035 	}
3036 
3037 	if (strcmp(tokens[0], "tap") == 0) {
3038 		cmd_tap(tokens, n_tokens, out, out_size, obj);
3039 		return;
3040 	}
3041 
3042 	if (strcmp(tokens[0], "pipeline") == 0) {
3043 		if ((n_tokens >= 3) &&
3044 			(strcmp(tokens[1], "codegen") == 0)) {
3045 			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
3046 				obj);
3047 			return;
3048 		}
3049 
3050 		if ((n_tokens >= 3) &&
3051 			(strcmp(tokens[1], "libbuild") == 0)) {
3052 			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
3053 				obj);
3054 			return;
3055 		}
3056 
3057 		if ((n_tokens >= 3) &&
3058 			(strcmp(tokens[2], "build") == 0)) {
3059 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
3060 				obj);
3061 			return;
3062 		}
3063 
3064 		if ((n_tokens >= 5) &&
3065 			(strcmp(tokens[2], "table") == 0) &&
3066 			(strcmp(tokens[4], "add") == 0)) {
3067 			cmd_pipeline_table_add(tokens, n_tokens, out,
3068 				out_size, obj);
3069 			return;
3070 		}
3071 
3072 		if ((n_tokens >= 5) &&
3073 			(strcmp(tokens[2], "table") == 0) &&
3074 			(strcmp(tokens[4], "delete") == 0)) {
3075 			cmd_pipeline_table_delete(tokens, n_tokens, out,
3076 				out_size, obj);
3077 			return;
3078 		}
3079 
3080 		if ((n_tokens >= 5) &&
3081 			(strcmp(tokens[2], "table") == 0) &&
3082 			(strcmp(tokens[4], "default") == 0)) {
3083 			cmd_pipeline_table_default(tokens, n_tokens, out,
3084 				out_size, obj);
3085 			return;
3086 		}
3087 
3088 		if ((n_tokens >= 5) &&
3089 			(strcmp(tokens[2], "table") == 0) &&
3090 			(strcmp(tokens[4], "show") == 0)) {
3091 			cmd_pipeline_table_show(tokens, n_tokens, out,
3092 				out_size, obj);
3093 			return;
3094 		}
3095 
3096 		if ((n_tokens >= 6) &&
3097 			(strcmp(tokens[2], "selector") == 0) &&
3098 			(strcmp(tokens[4], "group") == 0) &&
3099 			(strcmp(tokens[5], "add") == 0)) {
3100 			cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3101 				out_size, obj);
3102 			return;
3103 		}
3104 
3105 		if ((n_tokens >= 6) &&
3106 			(strcmp(tokens[2], "selector") == 0) &&
3107 			(strcmp(tokens[4], "group") == 0) &&
3108 			(strcmp(tokens[5], "delete") == 0)) {
3109 			cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3110 				out_size, obj);
3111 			return;
3112 		}
3113 
3114 		if ((n_tokens >= 7) &&
3115 			(strcmp(tokens[2], "selector") == 0) &&
3116 			(strcmp(tokens[4], "group") == 0) &&
3117 			(strcmp(tokens[5], "member") == 0) &&
3118 			(strcmp(tokens[6], "add") == 0)) {
3119 			cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3120 				out_size, obj);
3121 			return;
3122 		}
3123 
3124 		if ((n_tokens >= 7) &&
3125 			(strcmp(tokens[2], "selector") == 0) &&
3126 			(strcmp(tokens[4], "group") == 0) &&
3127 			(strcmp(tokens[5], "member") == 0) &&
3128 			(strcmp(tokens[6], "delete") == 0)) {
3129 			cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3130 				out_size, obj);
3131 			return;
3132 		}
3133 
3134 		if ((n_tokens >= 5) &&
3135 			(strcmp(tokens[2], "selector") == 0) &&
3136 			(strcmp(tokens[4], "show") == 0)) {
3137 			cmd_pipeline_selector_show(tokens, n_tokens, out,
3138 				out_size, obj);
3139 			return;
3140 		}
3141 
3142 		if ((n_tokens >= 5) &&
3143 			(strcmp(tokens[2], "learner") == 0) &&
3144 			(strcmp(tokens[4], "default") == 0)) {
3145 			cmd_pipeline_learner_default(tokens, n_tokens, out,
3146 				out_size, obj);
3147 			return;
3148 		}
3149 
3150 		if ((n_tokens >= 3) &&
3151 			(strcmp(tokens[2], "commit") == 0)) {
3152 			cmd_pipeline_commit(tokens, n_tokens, out,
3153 				out_size, obj);
3154 			return;
3155 		}
3156 
3157 		if ((n_tokens >= 3) &&
3158 			(strcmp(tokens[2], "abort") == 0)) {
3159 			cmd_pipeline_abort(tokens, n_tokens, out,
3160 				out_size, obj);
3161 			return;
3162 		}
3163 
3164 		if ((n_tokens >= 3) &&
3165 			(strcmp(tokens[2], "regrd") == 0)) {
3166 			cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3167 			return;
3168 		}
3169 
3170 		if ((n_tokens >= 3) &&
3171 			(strcmp(tokens[2], "regwr") == 0)) {
3172 			cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3173 			return;
3174 		}
3175 
3176 		if ((n_tokens >= 6) &&
3177 			(strcmp(tokens[2], "meter") == 0) &&
3178 			(strcmp(tokens[3], "profile") == 0) &&
3179 			(strcmp(tokens[5], "add") == 0)) {
3180 			cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3181 			return;
3182 		}
3183 
3184 		if ((n_tokens >= 6) &&
3185 			(strcmp(tokens[2], "meter") == 0) &&
3186 			(strcmp(tokens[3], "profile") == 0) &&
3187 			(strcmp(tokens[5], "delete") == 0)) {
3188 			cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3189 			return;
3190 		}
3191 
3192 		if ((n_tokens >= 9) &&
3193 			(strcmp(tokens[2], "meter") == 0) &&
3194 			(strcmp(tokens[8], "reset") == 0)) {
3195 			cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3196 			return;
3197 		}
3198 
3199 		if ((n_tokens >= 9) &&
3200 			(strcmp(tokens[2], "meter") == 0) &&
3201 			(strcmp(tokens[8], "set") == 0)) {
3202 			cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3203 			return;
3204 		}
3205 
3206 		if ((n_tokens >= 9) &&
3207 			(strcmp(tokens[2], "meter") == 0) &&
3208 			(strcmp(tokens[8], "stats") == 0)) {
3209 			cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3210 			return;
3211 		}
3212 
3213 		if ((n_tokens >= 3) &&
3214 			(strcmp(tokens[2], "stats") == 0)) {
3215 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3216 				obj);
3217 			return;
3218 		}
3219 
3220 		if ((n_tokens >= 4) &&
3221 			(strcmp(tokens[2], "mirror") == 0) &&
3222 			(strcmp(tokens[3], "slots") == 0)) {
3223 			cmd_pipeline_mirror(tokens, n_tokens, out, out_size, obj);
3224 			return;
3225 		}
3226 
3227 		if ((n_tokens >= 4) &&
3228 			(strcmp(tokens[2], "mirror") == 0) &&
3229 			(strcmp(tokens[3], "session") == 0)) {
3230 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
3231 			return;
3232 		}
3233 	}
3234 
3235 	if (strcmp(tokens[0], "thread") == 0) {
3236 		if ((n_tokens >= 5) &&
3237 			(strcmp(tokens[4], "enable") == 0)) {
3238 			cmd_thread_pipeline_enable(tokens, n_tokens,
3239 				out, out_size, obj);
3240 			return;
3241 		}
3242 
3243 		if ((n_tokens >= 5) &&
3244 			(strcmp(tokens[4], "disable") == 0)) {
3245 			cmd_thread_pipeline_disable(tokens, n_tokens,
3246 				out, out_size, obj);
3247 			return;
3248 		}
3249 	}
3250 
3251 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
3252 }
3253 
3254 int
3255 cli_script_process(const char *file_name,
3256 	size_t msg_in_len_max,
3257 	size_t msg_out_len_max,
3258 	void *obj)
3259 {
3260 	char *msg_in = NULL, *msg_out = NULL;
3261 	FILE *f = NULL;
3262 
3263 	/* Check input arguments */
3264 	if ((file_name == NULL) ||
3265 		(strlen(file_name) == 0) ||
3266 		(msg_in_len_max == 0) ||
3267 		(msg_out_len_max == 0))
3268 		return -EINVAL;
3269 
3270 	msg_in = malloc(msg_in_len_max + 1);
3271 	msg_out = malloc(msg_out_len_max + 1);
3272 	if ((msg_in == NULL) ||
3273 		(msg_out == NULL)) {
3274 		free(msg_out);
3275 		free(msg_in);
3276 		return -ENOMEM;
3277 	}
3278 
3279 	/* Open input file */
3280 	f = fopen(file_name, "r");
3281 	if (f == NULL) {
3282 		free(msg_out);
3283 		free(msg_in);
3284 		return -EIO;
3285 	}
3286 
3287 	/* Read file */
3288 	for ( ; ; ) {
3289 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
3290 			break;
3291 
3292 		printf("%s", msg_in);
3293 		msg_out[0] = 0;
3294 
3295 		cli_process(msg_in,
3296 			msg_out,
3297 			msg_out_len_max,
3298 			obj);
3299 
3300 		if (strlen(msg_out))
3301 			printf("%s", msg_out);
3302 	}
3303 
3304 	/* Close file */
3305 	fclose(f);
3306 	free(msg_out);
3307 	free(msg_in);
3308 	return 0;
3309 }
3310