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