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