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