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