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