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