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