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