xref: /dpdk/examples/pipeline/cli.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
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 int
1831 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1832 				   const char *learner_name,
1833 				   FILE *file,
1834 				   uint32_t *file_line_number)
1835 {
1836 	char *line = NULL;
1837 	uint32_t line_id = 0;
1838 	int status = 0;
1839 
1840 	/* Buffer allocation. */
1841 	line = malloc(MAX_LINE_SIZE);
1842 	if (!line)
1843 		return -ENOMEM;
1844 
1845 	/* File read. */
1846 	for (line_id = 1; ; line_id++) {
1847 		struct rte_swx_table_entry *entry;
1848 		int is_blank_or_comment;
1849 
1850 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1851 			break;
1852 
1853 		entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1854 									learner_name,
1855 									line,
1856 									&is_blank_or_comment);
1857 		if (!entry) {
1858 			if (is_blank_or_comment)
1859 				continue;
1860 
1861 			status = -EINVAL;
1862 			goto error;
1863 		}
1864 
1865 		status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1866 									learner_name,
1867 									entry);
1868 		table_entry_free(entry);
1869 		if (status)
1870 			goto error;
1871 	}
1872 
1873 error:
1874 	*file_line_number = line_id;
1875 	free(line);
1876 	return status;
1877 }
1878 
1879 static const char cmd_pipeline_learner_default_help[] =
1880 "pipeline <pipeline_name> learner <learner_name> default <file_name>\n";
1881 
1882 static void
1883 cmd_pipeline_learner_default(char **tokens,
1884 			     uint32_t n_tokens,
1885 			     char *out,
1886 			     size_t out_size,
1887 			     void *obj)
1888 {
1889 	struct pipeline *p;
1890 	char *pipeline_name, *learner_name, *file_name;
1891 	FILE *file = NULL;
1892 	uint32_t file_line_number = 0;
1893 	int status;
1894 
1895 	if (n_tokens != 6) {
1896 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1897 		return;
1898 	}
1899 
1900 	pipeline_name = tokens[1];
1901 	p = pipeline_find(obj, pipeline_name);
1902 	if (!p || !p->ctl) {
1903 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1904 		return;
1905 	}
1906 
1907 	learner_name = tokens[3];
1908 
1909 	file_name = tokens[5];
1910 	file = fopen(file_name, "r");
1911 	if (!file) {
1912 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1913 		return;
1914 	}
1915 
1916 	status = pipeline_learner_default_entry_add(p->ctl,
1917 						    learner_name,
1918 						    file,
1919 						    &file_line_number);
1920 	if (status)
1921 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1922 			 file_name,
1923 			 file_line_number);
1924 
1925 	fclose(file);
1926 }
1927 
1928 static const char cmd_pipeline_commit_help[] =
1929 "pipeline <pipeline_name> commit\n";
1930 
1931 static void
1932 cmd_pipeline_commit(char **tokens,
1933 	uint32_t n_tokens,
1934 	char *out,
1935 	size_t out_size,
1936 	void *obj)
1937 {
1938 	struct pipeline *p;
1939 	char *pipeline_name;
1940 	int status;
1941 
1942 	if (n_tokens != 3) {
1943 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1944 		return;
1945 	}
1946 
1947 	pipeline_name = tokens[1];
1948 	p = pipeline_find(obj, pipeline_name);
1949 	if (!p || !p->ctl) {
1950 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1951 		return;
1952 	}
1953 
1954 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1955 	if (status)
1956 		snprintf(out, out_size, "Commit failed. "
1957 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1958 }
1959 
1960 static const char cmd_pipeline_abort_help[] =
1961 "pipeline <pipeline_name> abort\n";
1962 
1963 static void
1964 cmd_pipeline_abort(char **tokens,
1965 	uint32_t n_tokens,
1966 	char *out,
1967 	size_t out_size,
1968 	void *obj)
1969 {
1970 	struct pipeline *p;
1971 	char *pipeline_name;
1972 
1973 	if (n_tokens != 3) {
1974 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1975 		return;
1976 	}
1977 
1978 	pipeline_name = tokens[1];
1979 	p = pipeline_find(obj, pipeline_name);
1980 	if (!p || !p->ctl) {
1981 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1982 		return;
1983 	}
1984 
1985 	rte_swx_ctl_pipeline_abort(p->ctl);
1986 }
1987 
1988 static const char cmd_pipeline_regrd_help[] =
1989 "pipeline <pipeline_name> regrd <register_array_name> <index>\n";
1990 
1991 static void
1992 cmd_pipeline_regrd(char **tokens,
1993 	uint32_t n_tokens,
1994 	char *out,
1995 	size_t out_size,
1996 	void *obj)
1997 {
1998 	struct pipeline *p;
1999 	const char *name;
2000 	uint64_t value;
2001 	uint32_t idx;
2002 	int status;
2003 
2004 	if (n_tokens != 5) {
2005 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2006 		return;
2007 	}
2008 
2009 	p = pipeline_find(obj, tokens[1]);
2010 	if (!p || !p->ctl) {
2011 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2012 		return;
2013 	}
2014 
2015 	if (strcmp(tokens[2], "regrd")) {
2016 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
2017 		return;
2018 	}
2019 
2020 	name = tokens[3];
2021 
2022 	if (parser_read_uint32(&idx, tokens[4])) {
2023 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
2024 		return;
2025 	}
2026 
2027 	status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
2028 	if (status) {
2029 		snprintf(out, out_size, "Command failed.\n");
2030 		return;
2031 	}
2032 
2033 	snprintf(out, out_size, "0x%" PRIx64 "\n", value);
2034 }
2035 
2036 static const char cmd_pipeline_regwr_help[] =
2037 "pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
2038 
2039 static void
2040 cmd_pipeline_regwr(char **tokens,
2041 	uint32_t n_tokens,
2042 	char *out,
2043 	size_t out_size,
2044 	void *obj)
2045 {
2046 	struct pipeline *p;
2047 	const char *name;
2048 	uint64_t value;
2049 	uint32_t idx;
2050 	int status;
2051 
2052 	if (n_tokens != 6) {
2053 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2054 		return;
2055 	}
2056 
2057 	p = pipeline_find(obj, tokens[1]);
2058 	if (!p || !p->ctl) {
2059 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2060 		return;
2061 	}
2062 
2063 	if (strcmp(tokens[2], "regwr")) {
2064 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
2065 		return;
2066 	}
2067 
2068 	name = tokens[3];
2069 
2070 	if (parser_read_uint32(&idx, tokens[4])) {
2071 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
2072 		return;
2073 	}
2074 
2075 	if (parser_read_uint64(&value, tokens[5])) {
2076 		snprintf(out, out_size, MSG_ARG_INVALID, "value");
2077 		return;
2078 	}
2079 
2080 	status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
2081 	if (status) {
2082 		snprintf(out, out_size, "Command failed.\n");
2083 		return;
2084 	}
2085 }
2086 
2087 static const char cmd_pipeline_meter_profile_add_help[] =
2088 "pipeline <pipeline_name> meter profile <profile_name> add "
2089 	"cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
2090 
2091 static void
2092 cmd_pipeline_meter_profile_add(char **tokens,
2093 	uint32_t n_tokens,
2094 	char *out,
2095 	size_t out_size,
2096 	void *obj)
2097 {
2098 	struct rte_meter_trtcm_params params;
2099 	struct pipeline *p;
2100 	const char *profile_name;
2101 	int status;
2102 
2103 	if (n_tokens != 14) {
2104 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2105 		return;
2106 	}
2107 
2108 	p = pipeline_find(obj, tokens[1]);
2109 	if (!p || !p->ctl) {
2110 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2111 		return;
2112 	}
2113 
2114 	if (strcmp(tokens[2], "meter")) {
2115 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2116 		return;
2117 	}
2118 
2119 	if (strcmp(tokens[3], "profile")) {
2120 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2121 		return;
2122 	}
2123 
2124 	profile_name = tokens[4];
2125 
2126 	if (strcmp(tokens[5], "add")) {
2127 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
2128 		return;
2129 	}
2130 
2131 	if (strcmp(tokens[6], "cir")) {
2132 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
2133 		return;
2134 	}
2135 
2136 	if (parser_read_uint64(&params.cir, tokens[7])) {
2137 		snprintf(out, out_size, MSG_ARG_INVALID, "cir");
2138 		return;
2139 	}
2140 
2141 	if (strcmp(tokens[8], "pir")) {
2142 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
2143 		return;
2144 	}
2145 
2146 	if (parser_read_uint64(&params.pir, tokens[9])) {
2147 		snprintf(out, out_size, MSG_ARG_INVALID, "pir");
2148 		return;
2149 	}
2150 
2151 	if (strcmp(tokens[10], "cbs")) {
2152 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
2153 		return;
2154 	}
2155 
2156 	if (parser_read_uint64(&params.cbs, tokens[11])) {
2157 		snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
2158 		return;
2159 	}
2160 
2161 	if (strcmp(tokens[12], "pbs")) {
2162 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
2163 		return;
2164 	}
2165 
2166 	if (parser_read_uint64(&params.pbs, tokens[13])) {
2167 		snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
2168 		return;
2169 	}
2170 
2171 	status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
2172 	if (status) {
2173 		snprintf(out, out_size, "Command failed.\n");
2174 		return;
2175 	}
2176 }
2177 
2178 static const char cmd_pipeline_meter_profile_delete_help[] =
2179 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
2180 
2181 static void
2182 cmd_pipeline_meter_profile_delete(char **tokens,
2183 	uint32_t n_tokens,
2184 	char *out,
2185 	size_t out_size,
2186 	void *obj)
2187 {
2188 	struct pipeline *p;
2189 	const char *profile_name;
2190 	int status;
2191 
2192 	if (n_tokens != 6) {
2193 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2194 		return;
2195 	}
2196 
2197 	p = pipeline_find(obj, tokens[1]);
2198 	if (!p || !p->ctl) {
2199 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2200 		return;
2201 	}
2202 
2203 	if (strcmp(tokens[2], "meter")) {
2204 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2205 		return;
2206 	}
2207 
2208 	if (strcmp(tokens[3], "profile")) {
2209 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2210 		return;
2211 	}
2212 
2213 	profile_name = tokens[4];
2214 
2215 	if (strcmp(tokens[5], "delete")) {
2216 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
2217 		return;
2218 	}
2219 
2220 	status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
2221 	if (status) {
2222 		snprintf(out, out_size, "Command failed.\n");
2223 		return;
2224 	}
2225 }
2226 
2227 static const char cmd_pipeline_meter_reset_help[] =
2228 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2229 	"reset\n";
2230 
2231 static void
2232 cmd_pipeline_meter_reset(char **tokens,
2233 	uint32_t n_tokens,
2234 	char *out,
2235 	size_t out_size,
2236 	void *obj)
2237 {
2238 	struct pipeline *p;
2239 	const char *name;
2240 	uint32_t idx0 = 0, idx1 = 0;
2241 
2242 	if (n_tokens != 9) {
2243 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2244 		return;
2245 	}
2246 
2247 	p = pipeline_find(obj, tokens[1]);
2248 	if (!p || !p->ctl) {
2249 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2250 		return;
2251 	}
2252 
2253 	if (strcmp(tokens[2], "meter")) {
2254 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2255 		return;
2256 	}
2257 
2258 	name = tokens[3];
2259 
2260 	if (strcmp(tokens[4], "from")) {
2261 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2262 		return;
2263 	}
2264 
2265 	if (parser_read_uint32(&idx0, tokens[5])) {
2266 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2267 		return;
2268 	}
2269 
2270 	if (strcmp(tokens[6], "to")) {
2271 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2272 		return;
2273 	}
2274 
2275 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2276 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2277 		return;
2278 	}
2279 
2280 	if (strcmp(tokens[8], "reset")) {
2281 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
2282 		return;
2283 	}
2284 
2285 	for ( ; idx0 <= idx1; idx0++) {
2286 		int status;
2287 
2288 		status = rte_swx_ctl_meter_reset(p->p, name, idx0);
2289 		if (status) {
2290 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2291 			return;
2292 		}
2293 	}
2294 }
2295 
2296 static const char cmd_pipeline_meter_set_help[] =
2297 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2298 	"set profile <profile_name>\n";
2299 
2300 static void
2301 cmd_pipeline_meter_set(char **tokens,
2302 	uint32_t n_tokens,
2303 	char *out,
2304 	size_t out_size,
2305 	void *obj)
2306 {
2307 	struct pipeline *p;
2308 	const char *name, *profile_name;
2309 	uint32_t idx0 = 0, idx1 = 0;
2310 
2311 	if (n_tokens != 11) {
2312 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2313 		return;
2314 	}
2315 
2316 	p = pipeline_find(obj, tokens[1]);
2317 	if (!p || !p->ctl) {
2318 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2319 		return;
2320 	}
2321 
2322 	if (strcmp(tokens[2], "meter")) {
2323 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2324 		return;
2325 	}
2326 
2327 	name = tokens[3];
2328 
2329 	if (strcmp(tokens[4], "from")) {
2330 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2331 		return;
2332 	}
2333 
2334 	if (parser_read_uint32(&idx0, tokens[5])) {
2335 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2336 		return;
2337 	}
2338 
2339 	if (strcmp(tokens[6], "to")) {
2340 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2341 		return;
2342 	}
2343 
2344 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2345 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2346 		return;
2347 	}
2348 
2349 	if (strcmp(tokens[8], "set")) {
2350 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
2351 		return;
2352 	}
2353 
2354 	if (strcmp(tokens[9], "profile")) {
2355 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2356 		return;
2357 	}
2358 
2359 	profile_name = tokens[10];
2360 
2361 	for ( ; idx0 <= idx1; idx0++) {
2362 		int status;
2363 
2364 		status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
2365 		if (status) {
2366 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2367 			return;
2368 		}
2369 	}
2370 }
2371 
2372 static const char cmd_pipeline_meter_stats_help[] =
2373 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2374 	"stats\n";
2375 
2376 static void
2377 cmd_pipeline_meter_stats(char **tokens,
2378 	uint32_t n_tokens,
2379 	char *out,
2380 	size_t out_size,
2381 	void *obj)
2382 {
2383 	struct rte_swx_ctl_meter_stats stats;
2384 	struct pipeline *p;
2385 	const char *name;
2386 	uint32_t idx0 = 0, idx1 = 0;
2387 
2388 	if (n_tokens != 9) {
2389 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2390 		return;
2391 	}
2392 
2393 	p = pipeline_find(obj, tokens[1]);
2394 	if (!p || !p->ctl) {
2395 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2396 		return;
2397 	}
2398 
2399 	if (strcmp(tokens[2], "meter")) {
2400 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2401 		return;
2402 	}
2403 
2404 	name = tokens[3];
2405 
2406 	if (strcmp(tokens[4], "from")) {
2407 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2408 		return;
2409 	}
2410 
2411 	if (parser_read_uint32(&idx0, tokens[5])) {
2412 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2413 		return;
2414 	}
2415 
2416 	if (strcmp(tokens[6], "to")) {
2417 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2418 		return;
2419 	}
2420 
2421 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2422 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2423 		return;
2424 	}
2425 
2426 	if (strcmp(tokens[8], "stats")) {
2427 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2428 		return;
2429 	}
2430 
2431 	/* Table header. */
2432 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2433 		 "-------",
2434 		 "----------------", "----------------", "----------------",
2435 		 "----------------", "----------------", "----------------");
2436 	out_size -= strlen(out);
2437 	out += strlen(out);
2438 
2439 	snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2440 		 "METER #",
2441 		 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2442 		 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2443 	out_size -= strlen(out);
2444 	out += strlen(out);
2445 
2446 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2447 		 "-------",
2448 		 "----------------", "----------------", "----------------",
2449 		 "----------------", "----------------", "----------------");
2450 	out_size -= strlen(out);
2451 	out += strlen(out);
2452 
2453 	/* Table rows. */
2454 	for ( ; idx0 <= idx1; idx0++) {
2455 		int status;
2456 
2457 		status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
2458 		if (status) {
2459 			snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
2460 			out_size -= strlen(out);
2461 			out += strlen(out);
2462 			return;
2463 		}
2464 
2465 		snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2466 			 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2467 			 idx0,
2468 			 stats.n_pkts[RTE_COLOR_GREEN],
2469 			 stats.n_pkts[RTE_COLOR_YELLOW],
2470 			 stats.n_pkts[RTE_COLOR_RED],
2471 			 stats.n_bytes[RTE_COLOR_GREEN],
2472 			 stats.n_bytes[RTE_COLOR_YELLOW],
2473 			 stats.n_bytes[RTE_COLOR_RED]);
2474 		out_size -= strlen(out);
2475 		out += strlen(out);
2476 	}
2477 }
2478 
2479 static const char cmd_pipeline_stats_help[] =
2480 "pipeline <pipeline_name> stats\n";
2481 
2482 static void
2483 cmd_pipeline_stats(char **tokens,
2484 	uint32_t n_tokens,
2485 	char *out,
2486 	size_t out_size,
2487 	void *obj)
2488 {
2489 	struct rte_swx_ctl_pipeline_info info;
2490 	struct pipeline *p;
2491 	uint32_t i;
2492 	int status;
2493 
2494 	if (n_tokens != 3) {
2495 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2496 		return;
2497 	}
2498 
2499 	p = pipeline_find(obj, tokens[1]);
2500 	if (!p || !p->ctl) {
2501 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2502 		return;
2503 	}
2504 
2505 	if (strcmp(tokens[2], "stats")) {
2506 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2507 		return;
2508 	}
2509 
2510 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
2511 	if (status) {
2512 		snprintf(out, out_size, "Pipeline info get error.");
2513 		return;
2514 	}
2515 
2516 	snprintf(out, out_size, "Input ports:\n");
2517 	out_size -= strlen(out);
2518 	out += strlen(out);
2519 
2520 	for (i = 0; i < info.n_ports_in; i++) {
2521 		struct rte_swx_port_in_stats stats;
2522 
2523 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
2524 
2525 		snprintf(out, out_size, "\tPort %u:"
2526 			" packets %" PRIu64
2527 			" bytes %" PRIu64
2528 			" empty %" PRIu64 "\n",
2529 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
2530 		out_size -= strlen(out);
2531 		out += strlen(out);
2532 	}
2533 
2534 	snprintf(out, out_size, "\nOutput ports:\n");
2535 	out_size -= strlen(out);
2536 	out += strlen(out);
2537 
2538 	for (i = 0; i < info.n_ports_out; i++) {
2539 		struct rte_swx_port_out_stats stats;
2540 
2541 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
2542 
2543 		snprintf(out, out_size, "\tPort %u:"
2544 			" packets %" PRIu64
2545 			" bytes %" PRIu64 "\n",
2546 			i, stats.n_pkts, stats.n_bytes);
2547 		out_size -= strlen(out);
2548 		out += strlen(out);
2549 	}
2550 
2551 	snprintf(out, out_size, "\nTables:\n");
2552 	out_size -= strlen(out);
2553 	out += strlen(out);
2554 
2555 	for (i = 0; i < info.n_tables; i++) {
2556 		struct rte_swx_ctl_table_info table_info;
2557 		uint64_t n_pkts_action[info.n_actions];
2558 		struct rte_swx_table_stats stats = {
2559 			.n_pkts_hit = 0,
2560 			.n_pkts_miss = 0,
2561 			.n_pkts_action = n_pkts_action,
2562 		};
2563 		uint32_t j;
2564 
2565 		status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
2566 		if (status) {
2567 			snprintf(out, out_size, "Table info get error.");
2568 			return;
2569 		}
2570 
2571 		status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
2572 		if (status) {
2573 			snprintf(out, out_size, "Table stats read error.");
2574 			return;
2575 		}
2576 
2577 		snprintf(out, out_size, "\tTable %s:\n"
2578 			"\t\tHit (packets): %" PRIu64 "\n"
2579 			"\t\tMiss (packets): %" PRIu64 "\n",
2580 			table_info.name,
2581 			stats.n_pkts_hit,
2582 			stats.n_pkts_miss);
2583 		out_size -= strlen(out);
2584 		out += strlen(out);
2585 
2586 		for (j = 0; j < info.n_actions; j++) {
2587 			struct rte_swx_ctl_action_info action_info;
2588 
2589 			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2590 			if (status) {
2591 				snprintf(out, out_size, "Action info get error.");
2592 				return;
2593 			}
2594 
2595 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2596 				action_info.name,
2597 				stats.n_pkts_action[j]);
2598 			out_size -= strlen(out);
2599 			out += strlen(out);
2600 		}
2601 	}
2602 
2603 	snprintf(out, out_size, "\nLearner tables:\n");
2604 	out_size -= strlen(out);
2605 	out += strlen(out);
2606 
2607 	for (i = 0; i < info.n_learners; i++) {
2608 		struct rte_swx_ctl_learner_info learner_info;
2609 		uint64_t n_pkts_action[info.n_actions];
2610 		struct rte_swx_learner_stats stats = {
2611 			.n_pkts_hit = 0,
2612 			.n_pkts_miss = 0,
2613 			.n_pkts_action = n_pkts_action,
2614 		};
2615 		uint32_t j;
2616 
2617 		status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
2618 		if (status) {
2619 			snprintf(out, out_size, "Learner table info get error.");
2620 			return;
2621 		}
2622 
2623 		status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
2624 		if (status) {
2625 			snprintf(out, out_size, "Learner table stats read error.");
2626 			return;
2627 		}
2628 
2629 		snprintf(out, out_size, "\tLearner table %s:\n"
2630 			"\t\tHit (packets): %" PRIu64 "\n"
2631 			"\t\tMiss (packets): %" PRIu64 "\n"
2632 			"\t\tLearn OK (packets): %" PRIu64 "\n"
2633 			"\t\tLearn error (packets): %" PRIu64 "\n"
2634 			"\t\tForget (packets): %" PRIu64 "\n",
2635 			learner_info.name,
2636 			stats.n_pkts_hit,
2637 			stats.n_pkts_miss,
2638 			stats.n_pkts_learn_ok,
2639 			stats.n_pkts_learn_err,
2640 			stats.n_pkts_forget);
2641 		out_size -= strlen(out);
2642 		out += strlen(out);
2643 
2644 		for (j = 0; j < info.n_actions; j++) {
2645 			struct rte_swx_ctl_action_info action_info;
2646 
2647 			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2648 			if (status) {
2649 				snprintf(out, out_size, "Action info get error.");
2650 				return;
2651 			}
2652 
2653 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2654 				action_info.name,
2655 				stats.n_pkts_action[j]);
2656 			out_size -= strlen(out);
2657 			out += strlen(out);
2658 		}
2659 	}
2660 }
2661 
2662 static const char cmd_thread_pipeline_enable_help[] =
2663 "thread <thread_id> pipeline <pipeline_name> enable\n";
2664 
2665 static void
2666 cmd_thread_pipeline_enable(char **tokens,
2667 	uint32_t n_tokens,
2668 	char *out,
2669 	size_t out_size,
2670 	void *obj)
2671 {
2672 	char *pipeline_name;
2673 	struct pipeline *p;
2674 	uint32_t thread_id;
2675 	int status;
2676 
2677 	if (n_tokens != 5) {
2678 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2679 		return;
2680 	}
2681 
2682 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2683 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2684 		return;
2685 	}
2686 
2687 	if (strcmp(tokens[2], "pipeline") != 0) {
2688 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2689 		return;
2690 	}
2691 
2692 	pipeline_name = tokens[3];
2693 	p = pipeline_find(obj, pipeline_name);
2694 	if (!p || !p->ctl) {
2695 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2696 		return;
2697 	}
2698 
2699 	if (strcmp(tokens[4], "enable") != 0) {
2700 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2701 		return;
2702 	}
2703 
2704 	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
2705 	if (status) {
2706 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
2707 		return;
2708 	}
2709 }
2710 
2711 static const char cmd_thread_pipeline_disable_help[] =
2712 "thread <thread_id> pipeline <pipeline_name> disable\n";
2713 
2714 static void
2715 cmd_thread_pipeline_disable(char **tokens,
2716 	uint32_t n_tokens,
2717 	char *out,
2718 	size_t out_size,
2719 	void *obj)
2720 {
2721 	struct pipeline *p;
2722 	char *pipeline_name;
2723 	uint32_t thread_id;
2724 	int status;
2725 
2726 	if (n_tokens != 5) {
2727 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2728 		return;
2729 	}
2730 
2731 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2732 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2733 		return;
2734 	}
2735 
2736 	if (strcmp(tokens[2], "pipeline") != 0) {
2737 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2738 		return;
2739 	}
2740 
2741 	pipeline_name = tokens[3];
2742 	p = pipeline_find(obj, pipeline_name);
2743 	if (!p || !p->ctl) {
2744 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2745 		return;
2746 	}
2747 
2748 	if (strcmp(tokens[4], "disable") != 0) {
2749 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2750 		return;
2751 	}
2752 
2753 	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
2754 	if (status) {
2755 		snprintf(out, out_size, MSG_CMD_FAIL,
2756 			"thread pipeline disable");
2757 		return;
2758 	}
2759 }
2760 
2761 static void
2762 cmd_help(char **tokens,
2763 	 uint32_t n_tokens,
2764 	 char *out,
2765 	 size_t out_size,
2766 	 void *arg __rte_unused)
2767 {
2768 	tokens++;
2769 	n_tokens--;
2770 
2771 	if (n_tokens == 0) {
2772 		snprintf(out, out_size,
2773 			"Type 'help <command>' for command details.\n\n"
2774 			"List of commands:\n"
2775 			"\tmempool\n"
2776 			"\tlink\n"
2777 			"\ttap\n"
2778 			"\tpipeline create\n"
2779 			"\tpipeline port in\n"
2780 			"\tpipeline port out\n"
2781 			"\tpipeline build\n"
2782 			"\tpipeline table add\n"
2783 			"\tpipeline table delete\n"
2784 			"\tpipeline table default\n"
2785 			"\tpipeline table show\n"
2786 			"\tpipeline selector group add\n"
2787 			"\tpipeline selector group delete\n"
2788 			"\tpipeline selector group member add\n"
2789 			"\tpipeline selector group member delete\n"
2790 			"\tpipeline selector show\n"
2791 			"\tpipeline learner default\n"
2792 			"\tpipeline commit\n"
2793 			"\tpipeline abort\n"
2794 			"\tpipeline regrd\n"
2795 			"\tpipeline regwr\n"
2796 			"\tpipeline meter profile add\n"
2797 			"\tpipeline meter profile delete\n"
2798 			"\tpipeline meter reset\n"
2799 			"\tpipeline meter set\n"
2800 			"\tpipeline meter stats\n"
2801 			"\tpipeline stats\n"
2802 			"\tthread pipeline enable\n"
2803 			"\tthread pipeline disable\n\n");
2804 		return;
2805 	}
2806 
2807 	if (strcmp(tokens[0], "mempool") == 0) {
2808 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
2809 		return;
2810 	}
2811 
2812 	if (strcmp(tokens[0], "link") == 0) {
2813 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
2814 		return;
2815 	}
2816 
2817 	if (strcmp(tokens[0], "ring") == 0) {
2818 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
2819 		return;
2820 	}
2821 
2822 	if (strcmp(tokens[0], "tap") == 0) {
2823 		snprintf(out, out_size, "\n%s\n", cmd_tap_help);
2824 		return;
2825 	}
2826 
2827 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2828 		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
2829 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
2830 		return;
2831 	}
2832 
2833 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2834 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
2835 		if (strcmp(tokens[2], "in") == 0) {
2836 			snprintf(out, out_size, "\n%s\n",
2837 				cmd_pipeline_port_in_help);
2838 			return;
2839 		}
2840 
2841 		if (strcmp(tokens[2], "out") == 0) {
2842 			snprintf(out, out_size, "\n%s\n",
2843 				cmd_pipeline_port_out_help);
2844 			return;
2845 		}
2846 	}
2847 
2848 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2849 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
2850 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
2851 		return;
2852 	}
2853 
2854 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2855 		(n_tokens == 3) &&
2856 		(strcmp(tokens[1], "table") == 0) &&
2857 		(strcmp(tokens[2], "add") == 0)) {
2858 		snprintf(out, out_size, "\n%s\n",
2859 			cmd_pipeline_table_add_help);
2860 		return;
2861 	}
2862 
2863 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2864 		(n_tokens == 3) &&
2865 		(strcmp(tokens[1], "table") == 0) &&
2866 		(strcmp(tokens[2], "delete") == 0)) {
2867 		snprintf(out, out_size, "\n%s\n",
2868 			cmd_pipeline_table_delete_help);
2869 		return;
2870 	}
2871 
2872 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2873 		(n_tokens == 3) &&
2874 		(strcmp(tokens[1], "table") == 0) &&
2875 		(strcmp(tokens[2], "default") == 0)) {
2876 		snprintf(out, out_size, "\n%s\n",
2877 			cmd_pipeline_table_default_help);
2878 		return;
2879 	}
2880 
2881 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2882 		(n_tokens == 3) &&
2883 		(strcmp(tokens[1], "table") == 0) &&
2884 		(strcmp(tokens[2], "show") == 0)) {
2885 		snprintf(out, out_size, "\n%s\n",
2886 			cmd_pipeline_table_show_help);
2887 		return;
2888 	}
2889 
2890 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2891 		(n_tokens == 4) &&
2892 		(strcmp(tokens[1], "selector") == 0) &&
2893 		(strcmp(tokens[2], "group") == 0) &&
2894 		(strcmp(tokens[3], "add") == 0)) {
2895 		snprintf(out, out_size, "\n%s\n",
2896 			cmd_pipeline_selector_group_add_help);
2897 		return;
2898 	}
2899 
2900 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2901 		(n_tokens == 4) &&
2902 		(strcmp(tokens[1], "selector") == 0) &&
2903 		(strcmp(tokens[2], "group") == 0) &&
2904 		(strcmp(tokens[3], "delete") == 0)) {
2905 		snprintf(out, out_size, "\n%s\n",
2906 			cmd_pipeline_selector_group_delete_help);
2907 		return;
2908 	}
2909 
2910 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2911 		(n_tokens == 5) &&
2912 		(strcmp(tokens[1], "selector") == 0) &&
2913 		(strcmp(tokens[2], "group") == 0) &&
2914 		(strcmp(tokens[3], "member") == 0) &&
2915 		(strcmp(tokens[4], "add") == 0)) {
2916 		snprintf(out, out_size, "\n%s\n",
2917 			cmd_pipeline_selector_group_member_add_help);
2918 		return;
2919 	}
2920 
2921 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2922 		(n_tokens == 5) &&
2923 		(strcmp(tokens[1], "selector") == 0) &&
2924 		(strcmp(tokens[2], "group") == 0) &&
2925 		(strcmp(tokens[3], "member") == 0) &&
2926 		(strcmp(tokens[4], "delete") == 0)) {
2927 		snprintf(out, out_size, "\n%s\n",
2928 			cmd_pipeline_selector_group_member_delete_help);
2929 		return;
2930 	}
2931 
2932 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2933 		(n_tokens == 3) &&
2934 		(strcmp(tokens[1], "selector") == 0) &&
2935 		(strcmp(tokens[2], "show") == 0)) {
2936 		snprintf(out, out_size, "\n%s\n",
2937 			cmd_pipeline_selector_show_help);
2938 		return;
2939 	}
2940 
2941 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2942 		(n_tokens == 3) &&
2943 		(strcmp(tokens[1], "learner") == 0) &&
2944 		(strcmp(tokens[2], "default") == 0)) {
2945 		snprintf(out, out_size, "\n%s\n",
2946 			cmd_pipeline_learner_default_help);
2947 		return;
2948 	}
2949 
2950 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2951 		(n_tokens == 2) &&
2952 		(strcmp(tokens[1], "commit") == 0)) {
2953 		snprintf(out, out_size, "\n%s\n",
2954 			cmd_pipeline_commit_help);
2955 		return;
2956 	}
2957 
2958 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2959 		(n_tokens == 2) &&
2960 		(strcmp(tokens[1], "abort") == 0)) {
2961 		snprintf(out, out_size, "\n%s\n",
2962 			cmd_pipeline_abort_help);
2963 		return;
2964 	}
2965 
2966 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2967 		(n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
2968 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
2969 		return;
2970 	}
2971 
2972 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2973 		(n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
2974 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
2975 		return;
2976 	}
2977 
2978 	if (!strcmp(tokens[0], "pipeline") &&
2979 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
2980 		&& !strcmp(tokens[2], "profile")
2981 		&& !strcmp(tokens[3], "add")) {
2982 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
2983 		return;
2984 	}
2985 
2986 	if (!strcmp(tokens[0], "pipeline") &&
2987 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
2988 		&& !strcmp(tokens[2], "profile")
2989 		&& !strcmp(tokens[3], "delete")) {
2990 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
2991 		return;
2992 	}
2993 
2994 	if (!strcmp(tokens[0], "pipeline") &&
2995 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2996 		&& !strcmp(tokens[2], "reset")) {
2997 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
2998 		return;
2999 	}
3000 
3001 	if (!strcmp(tokens[0], "pipeline") &&
3002 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3003 		&& !strcmp(tokens[2], "set")) {
3004 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
3005 		return;
3006 	}
3007 
3008 	if (!strcmp(tokens[0], "pipeline") &&
3009 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3010 		&& !strcmp(tokens[2], "stats")) {
3011 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
3012 		return;
3013 	}
3014 
3015 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3016 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
3017 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
3018 		return;
3019 	}
3020 
3021 	if ((n_tokens == 3) &&
3022 		(strcmp(tokens[0], "thread") == 0) &&
3023 		(strcmp(tokens[1], "pipeline") == 0)) {
3024 		if (strcmp(tokens[2], "enable") == 0) {
3025 			snprintf(out, out_size, "\n%s\n",
3026 				cmd_thread_pipeline_enable_help);
3027 			return;
3028 		}
3029 
3030 		if (strcmp(tokens[2], "disable") == 0) {
3031 			snprintf(out, out_size, "\n%s\n",
3032 				cmd_thread_pipeline_disable_help);
3033 			return;
3034 		}
3035 	}
3036 
3037 	snprintf(out, out_size, "Invalid command\n");
3038 }
3039 
3040 void
3041 cli_process(char *in, char *out, size_t out_size, void *obj)
3042 {
3043 	char *tokens[CMD_MAX_TOKENS];
3044 	uint32_t n_tokens = RTE_DIM(tokens);
3045 	int status;
3046 
3047 	if (is_comment(in))
3048 		return;
3049 
3050 	status = parse_tokenize_string(in, tokens, &n_tokens);
3051 	if (status) {
3052 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3053 		return;
3054 	}
3055 
3056 	if (n_tokens == 0)
3057 		return;
3058 
3059 	if (strcmp(tokens[0], "help") == 0) {
3060 		cmd_help(tokens, n_tokens, out, out_size, obj);
3061 		return;
3062 	}
3063 
3064 	if (strcmp(tokens[0], "mempool") == 0) {
3065 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
3066 		return;
3067 	}
3068 
3069 	if (strcmp(tokens[0], "link") == 0) {
3070 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3071 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
3072 			return;
3073 		}
3074 
3075 		cmd_link(tokens, n_tokens, out, out_size, obj);
3076 		return;
3077 	}
3078 
3079 	if (strcmp(tokens[0], "ring") == 0) {
3080 		cmd_ring(tokens, n_tokens, out, out_size, obj);
3081 		return;
3082 	}
3083 
3084 	if (strcmp(tokens[0], "tap") == 0) {
3085 		cmd_tap(tokens, n_tokens, out, out_size, obj);
3086 		return;
3087 	}
3088 
3089 	if (strcmp(tokens[0], "pipeline") == 0) {
3090 		if ((n_tokens >= 3) &&
3091 			(strcmp(tokens[2], "create") == 0)) {
3092 			cmd_pipeline_create(tokens, n_tokens, out, out_size,
3093 				obj);
3094 			return;
3095 		}
3096 
3097 		if ((n_tokens >= 4) &&
3098 			(strcmp(tokens[2], "port") == 0) &&
3099 			(strcmp(tokens[3], "in") == 0)) {
3100 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
3101 				obj);
3102 			return;
3103 		}
3104 
3105 		if ((n_tokens >= 4) &&
3106 			(strcmp(tokens[2], "port") == 0) &&
3107 			(strcmp(tokens[3], "out") == 0)) {
3108 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
3109 				obj);
3110 			return;
3111 		}
3112 
3113 		if ((n_tokens >= 3) &&
3114 			(strcmp(tokens[2], "build") == 0)) {
3115 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
3116 				obj);
3117 			return;
3118 		}
3119 
3120 		if ((n_tokens >= 5) &&
3121 			(strcmp(tokens[2], "table") == 0) &&
3122 			(strcmp(tokens[4], "add") == 0)) {
3123 			cmd_pipeline_table_add(tokens, n_tokens, out,
3124 				out_size, obj);
3125 			return;
3126 		}
3127 
3128 		if ((n_tokens >= 5) &&
3129 			(strcmp(tokens[2], "table") == 0) &&
3130 			(strcmp(tokens[4], "delete") == 0)) {
3131 			cmd_pipeline_table_delete(tokens, n_tokens, out,
3132 				out_size, obj);
3133 			return;
3134 		}
3135 
3136 		if ((n_tokens >= 5) &&
3137 			(strcmp(tokens[2], "table") == 0) &&
3138 			(strcmp(tokens[4], "default") == 0)) {
3139 			cmd_pipeline_table_default(tokens, n_tokens, out,
3140 				out_size, obj);
3141 			return;
3142 		}
3143 
3144 		if ((n_tokens >= 5) &&
3145 			(strcmp(tokens[2], "table") == 0) &&
3146 			(strcmp(tokens[4], "show") == 0)) {
3147 			cmd_pipeline_table_show(tokens, n_tokens, out,
3148 				out_size, obj);
3149 			return;
3150 		}
3151 
3152 		if ((n_tokens >= 6) &&
3153 			(strcmp(tokens[2], "selector") == 0) &&
3154 			(strcmp(tokens[4], "group") == 0) &&
3155 			(strcmp(tokens[5], "add") == 0)) {
3156 			cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3157 				out_size, obj);
3158 			return;
3159 		}
3160 
3161 		if ((n_tokens >= 6) &&
3162 			(strcmp(tokens[2], "selector") == 0) &&
3163 			(strcmp(tokens[4], "group") == 0) &&
3164 			(strcmp(tokens[5], "delete") == 0)) {
3165 			cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3166 				out_size, obj);
3167 			return;
3168 		}
3169 
3170 		if ((n_tokens >= 7) &&
3171 			(strcmp(tokens[2], "selector") == 0) &&
3172 			(strcmp(tokens[4], "group") == 0) &&
3173 			(strcmp(tokens[5], "member") == 0) &&
3174 			(strcmp(tokens[6], "add") == 0)) {
3175 			cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3176 				out_size, obj);
3177 			return;
3178 		}
3179 
3180 		if ((n_tokens >= 7) &&
3181 			(strcmp(tokens[2], "selector") == 0) &&
3182 			(strcmp(tokens[4], "group") == 0) &&
3183 			(strcmp(tokens[5], "member") == 0) &&
3184 			(strcmp(tokens[6], "delete") == 0)) {
3185 			cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3186 				out_size, obj);
3187 			return;
3188 		}
3189 
3190 		if ((n_tokens >= 5) &&
3191 			(strcmp(tokens[2], "selector") == 0) &&
3192 			(strcmp(tokens[4], "show") == 0)) {
3193 			cmd_pipeline_selector_show(tokens, n_tokens, out,
3194 				out_size, obj);
3195 			return;
3196 		}
3197 
3198 		if ((n_tokens >= 5) &&
3199 			(strcmp(tokens[2], "learner") == 0) &&
3200 			(strcmp(tokens[4], "default") == 0)) {
3201 			cmd_pipeline_learner_default(tokens, n_tokens, out,
3202 				out_size, obj);
3203 			return;
3204 		}
3205 
3206 		if ((n_tokens >= 3) &&
3207 			(strcmp(tokens[2], "commit") == 0)) {
3208 			cmd_pipeline_commit(tokens, n_tokens, out,
3209 				out_size, obj);
3210 			return;
3211 		}
3212 
3213 		if ((n_tokens >= 3) &&
3214 			(strcmp(tokens[2], "abort") == 0)) {
3215 			cmd_pipeline_abort(tokens, n_tokens, out,
3216 				out_size, obj);
3217 			return;
3218 		}
3219 
3220 		if ((n_tokens >= 3) &&
3221 			(strcmp(tokens[2], "regrd") == 0)) {
3222 			cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3223 			return;
3224 		}
3225 
3226 		if ((n_tokens >= 3) &&
3227 			(strcmp(tokens[2], "regwr") == 0)) {
3228 			cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3229 			return;
3230 		}
3231 
3232 		if ((n_tokens >= 6) &&
3233 			(strcmp(tokens[2], "meter") == 0) &&
3234 			(strcmp(tokens[3], "profile") == 0) &&
3235 			(strcmp(tokens[5], "add") == 0)) {
3236 			cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3237 			return;
3238 		}
3239 
3240 		if ((n_tokens >= 6) &&
3241 			(strcmp(tokens[2], "meter") == 0) &&
3242 			(strcmp(tokens[3], "profile") == 0) &&
3243 			(strcmp(tokens[5], "delete") == 0)) {
3244 			cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3245 			return;
3246 		}
3247 
3248 		if ((n_tokens >= 9) &&
3249 			(strcmp(tokens[2], "meter") == 0) &&
3250 			(strcmp(tokens[8], "reset") == 0)) {
3251 			cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3252 			return;
3253 		}
3254 
3255 		if ((n_tokens >= 9) &&
3256 			(strcmp(tokens[2], "meter") == 0) &&
3257 			(strcmp(tokens[8], "set") == 0)) {
3258 			cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3259 			return;
3260 		}
3261 
3262 		if ((n_tokens >= 9) &&
3263 			(strcmp(tokens[2], "meter") == 0) &&
3264 			(strcmp(tokens[8], "stats") == 0)) {
3265 			cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3266 			return;
3267 		}
3268 
3269 		if ((n_tokens >= 3) &&
3270 			(strcmp(tokens[2], "stats") == 0)) {
3271 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3272 				obj);
3273 			return;
3274 		}
3275 	}
3276 
3277 	if (strcmp(tokens[0], "thread") == 0) {
3278 		if ((n_tokens >= 5) &&
3279 			(strcmp(tokens[4], "enable") == 0)) {
3280 			cmd_thread_pipeline_enable(tokens, n_tokens,
3281 				out, out_size, obj);
3282 			return;
3283 		}
3284 
3285 		if ((n_tokens >= 5) &&
3286 			(strcmp(tokens[4], "disable") == 0)) {
3287 			cmd_thread_pipeline_disable(tokens, n_tokens,
3288 				out, out_size, obj);
3289 			return;
3290 		}
3291 	}
3292 
3293 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
3294 }
3295 
3296 int
3297 cli_script_process(const char *file_name,
3298 	size_t msg_in_len_max,
3299 	size_t msg_out_len_max,
3300 	void *obj)
3301 {
3302 	char *msg_in = NULL, *msg_out = NULL;
3303 	FILE *f = NULL;
3304 
3305 	/* Check input arguments */
3306 	if ((file_name == NULL) ||
3307 		(strlen(file_name) == 0) ||
3308 		(msg_in_len_max == 0) ||
3309 		(msg_out_len_max == 0))
3310 		return -EINVAL;
3311 
3312 	msg_in = malloc(msg_in_len_max + 1);
3313 	msg_out = malloc(msg_out_len_max + 1);
3314 	if ((msg_in == NULL) ||
3315 		(msg_out == NULL)) {
3316 		free(msg_out);
3317 		free(msg_in);
3318 		return -ENOMEM;
3319 	}
3320 
3321 	/* Open input file */
3322 	f = fopen(file_name, "r");
3323 	if (f == NULL) {
3324 		free(msg_out);
3325 		free(msg_in);
3326 		return -EIO;
3327 	}
3328 
3329 	/* Read file */
3330 	for ( ; ; ) {
3331 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
3332 			break;
3333 
3334 		printf("%s", msg_in);
3335 		msg_out[0] = 0;
3336 
3337 		cli_process(msg_in,
3338 			msg_out,
3339 			msg_out_len_max,
3340 			obj);
3341 
3342 		if (strlen(msg_out))
3343 			printf("%s", msg_out);
3344 	}
3345 
3346 	/* Close file */
3347 	fclose(f);
3348 	free(msg_out);
3349 	free(msg_in);
3350 	return 0;
3351 }
3352