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