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