xref: /dpdk/examples/pipeline/cli.c (revision c99f115bbd9d56630fc5a8d404443e0761684832)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include <rte_common.h>
11 #include <rte_ethdev.h>
12 #include <rte_swx_port_ethdev.h>
13 #include <rte_swx_port_ring.h>
14 #include <rte_swx_port_source_sink.h>
15 #include <rte_swx_port_fd.h>
16 #include <rte_swx_pipeline.h>
17 #include <rte_swx_ctl.h>
18 
19 #include "cli.h"
20 
21 #include "obj.h"
22 #include "thread.h"
23 
24 #ifndef CMD_MAX_TOKENS
25 #define CMD_MAX_TOKENS     256
26 #endif
27 
28 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
29 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
30 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
31 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
32 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
33 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
34 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
35 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
36 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
37 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
38 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
39 
40 #define skip_white_spaces(pos)			\
41 ({						\
42 	__typeof__(pos) _p = (pos);		\
43 	for ( ; isspace(*_p); _p++)		\
44 		;				\
45 	_p;					\
46 })
47 
48 static int
49 parser_read_uint64(uint64_t *value, const char *p)
50 {
51 	char *next;
52 	uint64_t val;
53 
54 	p = skip_white_spaces(p);
55 	if (!isdigit(*p))
56 		return -EINVAL;
57 
58 	val = strtoul(p, &next, 10);
59 	if (p == next)
60 		return -EINVAL;
61 
62 	p = next;
63 	switch (*p) {
64 	case 'T':
65 		val *= 1024ULL;
66 		/* fall through */
67 	case 'G':
68 		val *= 1024ULL;
69 		/* fall through */
70 	case 'M':
71 		val *= 1024ULL;
72 		/* fall through */
73 	case 'k':
74 	case 'K':
75 		val *= 1024ULL;
76 		p++;
77 		break;
78 	}
79 
80 	p = skip_white_spaces(p);
81 	if (*p != '\0')
82 		return -EINVAL;
83 
84 	*value = val;
85 	return 0;
86 }
87 
88 static int
89 parser_read_uint32(uint32_t *value, const char *p)
90 {
91 	uint64_t val = 0;
92 	int ret = parser_read_uint64(&val, p);
93 
94 	if (ret < 0)
95 		return ret;
96 
97 	if (val > UINT32_MAX)
98 		return -ERANGE;
99 
100 	*value = val;
101 	return 0;
102 }
103 
104 static int
105 parser_read_uint16(uint16_t *value, const char *p)
106 {
107 	uint64_t val = 0;
108 	int ret = parser_read_uint64(&val, p);
109 
110 	if (ret < 0)
111 		return ret;
112 
113 	if (val > UINT16_MAX)
114 		return -ERANGE;
115 
116 	*value = val;
117 	return 0;
118 }
119 
120 #define PARSE_DELIMITER " \f\n\r\t\v"
121 
122 static int
123 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
124 {
125 	uint32_t i;
126 
127 	if ((string == NULL) ||
128 		(tokens == NULL) ||
129 		(*n_tokens < 1))
130 		return -EINVAL;
131 
132 	for (i = 0; i < *n_tokens; i++) {
133 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
134 		if (tokens[i] == NULL)
135 			break;
136 	}
137 
138 	if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
139 		return -E2BIG;
140 
141 	*n_tokens = i;
142 	return 0;
143 }
144 
145 static int
146 is_comment(char *in)
147 {
148 	if ((strlen(in) && index("!#%;", in[0])) ||
149 		(strncmp(in, "//", 2) == 0) ||
150 		(strncmp(in, "--", 2) == 0))
151 		return 1;
152 
153 	return 0;
154 }
155 
156 static const char cmd_mempool_help[] =
157 "mempool <mempool_name>\n"
158 "   buffer <buffer_size>\n"
159 "   pool <pool_size>\n"
160 "   cache <cache_size>\n"
161 "   cpu <cpu_id>\n";
162 
163 static void
164 cmd_mempool(char **tokens,
165 	uint32_t n_tokens,
166 	char *out,
167 	size_t out_size,
168 	void *obj)
169 {
170 	struct mempool_params p;
171 	char *name;
172 	struct mempool *mempool;
173 
174 	if (n_tokens != 10) {
175 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
176 		return;
177 	}
178 
179 	name = tokens[1];
180 
181 	if (strcmp(tokens[2], "buffer") != 0) {
182 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
183 		return;
184 	}
185 
186 	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
187 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
188 		return;
189 	}
190 
191 	if (strcmp(tokens[4], "pool") != 0) {
192 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
193 		return;
194 	}
195 
196 	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
197 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
198 		return;
199 	}
200 
201 	if (strcmp(tokens[6], "cache") != 0) {
202 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
203 		return;
204 	}
205 
206 	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
207 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
208 		return;
209 	}
210 
211 	if (strcmp(tokens[8], "cpu") != 0) {
212 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
213 		return;
214 	}
215 
216 	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
217 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
218 		return;
219 	}
220 
221 	mempool = mempool_create(obj, name, &p);
222 	if (mempool == NULL) {
223 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
224 		return;
225 	}
226 }
227 
228 static const char cmd_link_help[] =
229 "link <link_name>\n"
230 "   dev <device_name> | port <port_id>\n"
231 "   rxq <n_queues> <queue_size> <mempool_name>\n"
232 "   txq <n_queues> <queue_size>\n"
233 "   promiscuous on | off\n"
234 "   [rss <qid_0> ... <qid_n>]\n";
235 
236 static void
237 cmd_link(char **tokens,
238 	uint32_t n_tokens,
239 	char *out,
240 	size_t out_size,
241 	void *obj)
242 {
243 	struct link_params p;
244 	struct link_params_rss rss;
245 	struct link *link;
246 	char *name;
247 
248 	memset(&p, 0, sizeof(p));
249 
250 	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
251 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
252 		return;
253 	}
254 	name = tokens[1];
255 
256 	if (strcmp(tokens[2], "dev") == 0)
257 		p.dev_name = tokens[3];
258 	else if (strcmp(tokens[2], "port") == 0) {
259 		p.dev_name = NULL;
260 
261 		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
262 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
263 			return;
264 		}
265 	} else {
266 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
267 		return;
268 	}
269 
270 	if (strcmp(tokens[4], "rxq") != 0) {
271 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
272 		return;
273 	}
274 
275 	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
276 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
277 		return;
278 	}
279 	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
280 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
281 		return;
282 	}
283 
284 	p.rx.mempool_name = tokens[7];
285 
286 	if (strcmp(tokens[8], "txq") != 0) {
287 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
288 		return;
289 	}
290 
291 	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
292 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
293 		return;
294 	}
295 
296 	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
297 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
298 		return;
299 	}
300 
301 	if (strcmp(tokens[11], "promiscuous") != 0) {
302 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
303 		return;
304 	}
305 
306 	if (strcmp(tokens[12], "on") == 0)
307 		p.promiscuous = 1;
308 	else if (strcmp(tokens[12], "off") == 0)
309 		p.promiscuous = 0;
310 	else {
311 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
312 		return;
313 	}
314 
315 	/* RSS */
316 	p.rx.rss = NULL;
317 	if (n_tokens > 13) {
318 		uint32_t queue_id, i;
319 
320 		if (strcmp(tokens[13], "rss") != 0) {
321 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
322 			return;
323 		}
324 
325 		p.rx.rss = &rss;
326 
327 		rss.n_queues = 0;
328 		for (i = 14; i < n_tokens; i++) {
329 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
330 				snprintf(out, out_size, MSG_ARG_INVALID,
331 					"queue_id");
332 				return;
333 			}
334 
335 			rss.queue_id[rss.n_queues] = queue_id;
336 			rss.n_queues++;
337 		}
338 	}
339 
340 	link = link_create(obj, name, &p);
341 	if (link == NULL) {
342 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
343 		return;
344 	}
345 }
346 
347 /* Print the link stats and info */
348 static void
349 print_link_info(struct link *link, char *out, size_t out_size)
350 {
351 	struct rte_eth_stats stats;
352 	struct rte_ether_addr mac_addr;
353 	struct rte_eth_link eth_link;
354 	uint16_t mtu;
355 	int ret;
356 
357 	memset(&stats, 0, sizeof(stats));
358 	rte_eth_stats_get(link->port_id, &stats);
359 
360 	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
361 	if (ret != 0) {
362 		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
363 			 link->name, rte_strerror(-ret));
364 		return;
365 	}
366 
367 	ret = rte_eth_link_get(link->port_id, &eth_link);
368 	if (ret < 0) {
369 		snprintf(out, out_size, "\n%s: link get failed: %s",
370 			 link->name, rte_strerror(-ret));
371 		return;
372 	}
373 
374 	rte_eth_dev_get_mtu(link->port_id, &mtu);
375 
376 	snprintf(out, out_size,
377 		"\n"
378 		"%s: flags=<%s> mtu %u\n"
379 		"\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
380 		"\tport# %u  speed %s\n"
381 		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
382 		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
383 		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
384 		"\tTX errors %" PRIu64"\n",
385 		link->name,
386 		eth_link.link_status == 0 ? "DOWN" : "UP",
387 		mtu,
388 		mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
389 		mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
390 		mac_addr.addr_bytes[4], mac_addr.addr_bytes[5],
391 		link->n_rxq,
392 		link->n_txq,
393 		link->port_id,
394 		rte_eth_link_speed_to_str(eth_link.link_speed),
395 		stats.ipackets,
396 		stats.ibytes,
397 		stats.ierrors,
398 		stats.imissed,
399 		stats.rx_nombuf,
400 		stats.opackets,
401 		stats.obytes,
402 		stats.oerrors);
403 }
404 
405 /*
406  * link show [<link_name>]
407  */
408 static void
409 cmd_link_show(char **tokens,
410 	      uint32_t n_tokens,
411 	      char *out,
412 	      size_t out_size,
413 	      void *obj)
414 {
415 	struct link *link;
416 	char *link_name;
417 
418 	if (n_tokens != 2 && n_tokens != 3) {
419 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
420 		return;
421 	}
422 
423 	if (n_tokens == 2) {
424 		link = link_next(obj, NULL);
425 
426 		while (link != NULL) {
427 			out_size = out_size - strlen(out);
428 			out = &out[strlen(out)];
429 
430 			print_link_info(link, out, out_size);
431 			link = link_next(obj, link);
432 		}
433 	} else {
434 		out_size = out_size - strlen(out);
435 		out = &out[strlen(out)];
436 
437 		link_name = tokens[2];
438 		link = link_find(obj, link_name);
439 
440 		if (link == NULL) {
441 			snprintf(out, out_size, MSG_ARG_INVALID,
442 					"Link does not exist");
443 			return;
444 		}
445 		print_link_info(link, out, out_size);
446 	}
447 }
448 
449 static const char cmd_ring_help[] =
450 "ring <ring_name> size <size> numa <numa_node>\n";
451 
452 static void
453 cmd_ring(char **tokens,
454 	uint32_t n_tokens,
455 	char *out,
456 	size_t out_size,
457 	void *obj)
458 {
459 	struct ring_params p;
460 	char *name;
461 	struct ring *ring;
462 
463 	if (n_tokens != 6) {
464 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
465 		return;
466 	}
467 
468 	name = tokens[1];
469 
470 	if (strcmp(tokens[2], "size") != 0) {
471 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
472 		return;
473 	}
474 
475 	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
476 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
477 		return;
478 	}
479 
480 	if (strcmp(tokens[4], "numa") != 0) {
481 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
482 		return;
483 	}
484 
485 	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
486 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
487 		return;
488 	}
489 
490 	ring = ring_create(obj, name, &p);
491 	if (!ring) {
492 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
493 		return;
494 	}
495 }
496 
497 static const char cmd_tap_help[] =
498 "tap <tap_name>\n";
499 
500 static void
501 cmd_tap(char **tokens,
502 	uint32_t n_tokens,
503 	char *out,
504 	size_t out_size,
505 	void *obj)
506 {
507 	struct tap *tap;
508 	char *name;
509 
510 	if (n_tokens < 2) {
511 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
512 		return;
513 	}
514 	name = tokens[1];
515 
516 	tap = tap_create(obj, name);
517 	if (tap == NULL) {
518 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
519 		return;
520 	}
521 }
522 
523 static const char cmd_pipeline_create_help[] =
524 "pipeline <pipeline_name> create <numa_node>\n";
525 
526 static void
527 cmd_pipeline_create(char **tokens,
528 	uint32_t n_tokens,
529 	char *out,
530 	size_t out_size,
531 	void *obj)
532 {
533 	struct pipeline *p;
534 	char *name;
535 	uint32_t numa_node;
536 
537 	if (n_tokens != 4) {
538 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
539 		return;
540 	}
541 
542 	name = tokens[1];
543 
544 	if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
545 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
546 		return;
547 	}
548 
549 	p = pipeline_create(obj, name, (int)numa_node);
550 	if (!p) {
551 		snprintf(out, out_size, "pipeline create error.");
552 		return;
553 	}
554 }
555 
556 static const char cmd_pipeline_port_in_help[] =
557 "pipeline <pipeline_name> port in <port_id>\n"
558 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
559 "   ring <ring_name> bsz <burst_size>\n"
560 "   | source <mempool_name> <file_name>\n"
561 "   | tap <tap_name> mempool <mempool_name> mtu <mtu> bsz <burst_size>\n";
562 
563 static void
564 cmd_pipeline_port_in(char **tokens,
565 	uint32_t n_tokens,
566 	char *out,
567 	size_t out_size,
568 	void *obj)
569 {
570 	struct pipeline *p;
571 	int status;
572 	uint32_t port_id = 0, t0;
573 
574 	if (n_tokens < 6) {
575 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
576 		return;
577 	}
578 
579 	p = pipeline_find(obj, tokens[1]);
580 	if (!p || p->ctl) {
581 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
582 		return;
583 	}
584 
585 	if (strcmp(tokens[2], "port") != 0) {
586 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
587 		return;
588 	}
589 
590 	if (strcmp(tokens[3], "in") != 0) {
591 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
592 		return;
593 	}
594 
595 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
596 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
597 		return;
598 	}
599 
600 	t0 = 5;
601 
602 	if (strcmp(tokens[t0], "link") == 0) {
603 		struct rte_swx_port_ethdev_reader_params params;
604 		struct link *link;
605 
606 		if (n_tokens < t0 + 6) {
607 			snprintf(out, out_size, MSG_ARG_MISMATCH,
608 				"pipeline port in link");
609 			return;
610 		}
611 
612 		link = link_find(obj, tokens[t0 + 1]);
613 		if (!link) {
614 			snprintf(out, out_size, MSG_ARG_INVALID,
615 				"link_name");
616 			return;
617 		}
618 		params.dev_name = link->dev_name;
619 
620 		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
621 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
622 			return;
623 		}
624 
625 		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
626 			snprintf(out, out_size, MSG_ARG_INVALID,
627 				"queue_id");
628 			return;
629 		}
630 
631 		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
632 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
633 			return;
634 		}
635 
636 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
637 			snprintf(out, out_size, MSG_ARG_INVALID,
638 				"burst_size");
639 			return;
640 		}
641 
642 		t0 += 6;
643 
644 		status = rte_swx_pipeline_port_in_config(p->p,
645 			port_id,
646 			"ethdev",
647 			&params);
648 	} else if (strcmp(tokens[t0], "ring") == 0) {
649 		struct rte_swx_port_ring_reader_params params;
650 		struct ring *ring;
651 
652 		if (n_tokens < t0 + 4) {
653 			snprintf(out, out_size, MSG_ARG_MISMATCH,
654 				"pipeline port in ring");
655 			return;
656 		}
657 
658 		ring = ring_find(obj, tokens[t0 + 1]);
659 		if (!ring) {
660 			snprintf(out, out_size, MSG_ARG_INVALID,
661 				"ring_name");
662 			return;
663 		}
664 		params.name = ring->name;
665 
666 		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
667 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
668 			return;
669 		}
670 
671 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
672 			snprintf(out, out_size, MSG_ARG_INVALID,
673 				"burst_size");
674 			return;
675 		}
676 
677 		t0 += 4;
678 
679 		status = rte_swx_pipeline_port_in_config(p->p,
680 			port_id,
681 			"ring",
682 			&params);
683 	} else if (strcmp(tokens[t0], "source") == 0) {
684 		struct rte_swx_port_source_params params;
685 		struct mempool *mp;
686 
687 		if (n_tokens < t0 + 3) {
688 			snprintf(out, out_size, MSG_ARG_MISMATCH,
689 				"pipeline port in source");
690 			return;
691 		}
692 
693 		mp = mempool_find(obj, tokens[t0 + 1]);
694 		if (!mp) {
695 			snprintf(out, out_size, MSG_ARG_INVALID,
696 				"mempool_name");
697 			return;
698 		}
699 		params.pool = mp->m;
700 
701 		params.file_name = tokens[t0 + 2];
702 
703 		t0 += 3;
704 
705 		status = rte_swx_pipeline_port_in_config(p->p,
706 			port_id,
707 			"source",
708 			&params);
709 	} else if (strcmp(tokens[t0], "tap") == 0) {
710 		struct rte_swx_port_fd_reader_params params;
711 		struct tap *tap;
712 		struct mempool *mp;
713 
714 		if (n_tokens < t0 + 8) {
715 			snprintf(out, out_size, MSG_ARG_MISMATCH,
716 				"pipeline port in tap");
717 			return;
718 		}
719 
720 		tap = tap_find(obj, tokens[t0 + 1]);
721 		if (!tap) {
722 			snprintf(out, out_size, MSG_ARG_INVALID,
723 				"tap_name");
724 			return;
725 		}
726 		params.fd = tap->fd;
727 
728 		if (strcmp(tokens[t0 + 2], "mempool") != 0) {
729 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
730 				"mempool");
731 			return;
732 		}
733 
734 		mp = mempool_find(obj, tokens[t0 + 3]);
735 		if (!mp) {
736 			snprintf(out, out_size, MSG_ARG_INVALID,
737 				"mempool_name");
738 			return;
739 		}
740 		params.mempool = mp->m;
741 
742 		if (strcmp(tokens[t0 + 4], "mtu") != 0) {
743 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
744 				"mtu");
745 			return;
746 		}
747 
748 		if (parser_read_uint32(&params.mtu, tokens[t0 + 5]) != 0) {
749 			snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
750 			return;
751 		}
752 
753 		if (strcmp(tokens[t0 + 6], "bsz") != 0) {
754 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
755 			return;
756 		}
757 
758 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 7])) {
759 			snprintf(out, out_size, MSG_ARG_INVALID,
760 				"burst_size");
761 			return;
762 		}
763 
764 		t0 += 8;
765 
766 		status = rte_swx_pipeline_port_in_config(p->p,
767 			port_id,
768 			"fd",
769 			&params);
770 
771 	} else {
772 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
773 		return;
774 	}
775 
776 	if (status) {
777 		snprintf(out, out_size, "port in error.");
778 		return;
779 	}
780 
781 	if (n_tokens != t0) {
782 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
783 		return;
784 	}
785 }
786 
787 static const char cmd_pipeline_port_out_help[] =
788 "pipeline <pipeline_name> port out <port_id>\n"
789 "   link <link_name> txq <txq_id> bsz <burst_size>\n"
790 "   ring <ring_name> bsz <burst_size>\n"
791 "   | sink <file_name> | none\n"
792 "   | tap <tap_name> bsz <burst_size>\n";
793 
794 static void
795 cmd_pipeline_port_out(char **tokens,
796 	uint32_t n_tokens,
797 	char *out,
798 	size_t out_size,
799 	void *obj)
800 {
801 	struct pipeline *p;
802 	int status;
803 	uint32_t port_id = 0, t0;
804 
805 	if (n_tokens < 6) {
806 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
807 		return;
808 	}
809 
810 	p = pipeline_find(obj, tokens[1]);
811 	if (!p || p->ctl) {
812 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
813 		return;
814 	}
815 
816 	if (strcmp(tokens[2], "port") != 0) {
817 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
818 		return;
819 	}
820 
821 	if (strcmp(tokens[3], "out") != 0) {
822 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
823 		return;
824 	}
825 
826 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
827 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
828 		return;
829 	}
830 
831 	t0 = 5;
832 
833 	if (strcmp(tokens[t0], "link") == 0) {
834 		struct rte_swx_port_ethdev_writer_params params;
835 		struct link *link;
836 
837 		if (n_tokens < t0 + 6) {
838 			snprintf(out, out_size, MSG_ARG_MISMATCH,
839 				"pipeline port out link");
840 			return;
841 		}
842 
843 		link = link_find(obj, tokens[t0 + 1]);
844 		if (!link) {
845 			snprintf(out, out_size, MSG_ARG_INVALID,
846 				"link_name");
847 			return;
848 		}
849 		params.dev_name = link->dev_name;
850 
851 		if (strcmp(tokens[t0 + 2], "txq") != 0) {
852 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
853 			return;
854 		}
855 
856 		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
857 			snprintf(out, out_size, MSG_ARG_INVALID,
858 				"queue_id");
859 			return;
860 		}
861 
862 		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
863 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
864 			return;
865 		}
866 
867 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
868 			snprintf(out, out_size, MSG_ARG_INVALID,
869 				"burst_size");
870 			return;
871 		}
872 
873 		t0 += 6;
874 
875 		status = rte_swx_pipeline_port_out_config(p->p,
876 			port_id,
877 			"ethdev",
878 			&params);
879 	} else if (strcmp(tokens[t0], "ring") == 0) {
880 		struct rte_swx_port_ring_writer_params params;
881 		struct ring *ring;
882 
883 		if (n_tokens < t0 + 4) {
884 			snprintf(out, out_size, MSG_ARG_MISMATCH,
885 				"pipeline port out link");
886 			return;
887 		}
888 
889 		ring = ring_find(obj, tokens[t0 + 1]);
890 		if (!ring) {
891 			snprintf(out, out_size, MSG_ARG_INVALID,
892 				"ring_name");
893 			return;
894 		}
895 		params.name = ring->name;
896 
897 		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
898 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
899 			return;
900 		}
901 
902 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
903 			snprintf(out, out_size, MSG_ARG_INVALID,
904 				"burst_size");
905 			return;
906 		}
907 
908 		t0 += 4;
909 
910 		status = rte_swx_pipeline_port_out_config(p->p,
911 			port_id,
912 			"ring",
913 			&params);
914 	} else if (strcmp(tokens[t0], "sink") == 0) {
915 		struct rte_swx_port_sink_params params;
916 
917 		params.file_name = strcmp(tokens[t0 + 1], "none") ?
918 			tokens[t0 + 1] : NULL;
919 
920 		t0 += 2;
921 
922 		status = rte_swx_pipeline_port_out_config(p->p,
923 			port_id,
924 			"sink",
925 			&params);
926 	} else if (strcmp(tokens[t0], "tap") == 0) {
927 		struct rte_swx_port_fd_writer_params params;
928 		struct tap *tap;
929 
930 		if (n_tokens < t0 + 4) {
931 			snprintf(out, out_size, MSG_ARG_MISMATCH,
932 				"pipeline port out tap");
933 			return;
934 		}
935 
936 		tap = tap_find(obj, tokens[t0 + 1]);
937 		if (!tap) {
938 			snprintf(out, out_size, MSG_ARG_INVALID,
939 				"tap_name");
940 			return;
941 		}
942 		params.fd = tap->fd;
943 
944 		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
945 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
946 			return;
947 		}
948 
949 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
950 			snprintf(out, out_size, MSG_ARG_INVALID,
951 				"burst_size");
952 			return;
953 		}
954 
955 		t0 += 4;
956 
957 		status = rte_swx_pipeline_port_out_config(p->p,
958 			port_id,
959 			"fd",
960 			&params);
961 	} else {
962 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
963 		return;
964 	}
965 
966 	if (status) {
967 		snprintf(out, out_size, "port out error.");
968 		return;
969 	}
970 
971 	if (n_tokens != t0) {
972 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
973 		return;
974 	}
975 }
976 
977 static const char cmd_pipeline_build_help[] =
978 "pipeline <pipeline_name> build <spec_file>\n";
979 
980 static void
981 cmd_pipeline_build(char **tokens,
982 	uint32_t n_tokens,
983 	char *out,
984 	size_t out_size,
985 	void *obj)
986 {
987 	struct pipeline *p = NULL;
988 	FILE *spec = NULL;
989 	uint32_t err_line;
990 	const char *err_msg;
991 	int status;
992 
993 	if (n_tokens != 4) {
994 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
995 		return;
996 	}
997 
998 	p = pipeline_find(obj, tokens[1]);
999 	if (!p || p->ctl) {
1000 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1001 		return;
1002 	}
1003 
1004 	spec = fopen(tokens[3], "r");
1005 	if (!spec) {
1006 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
1007 		return;
1008 	}
1009 
1010 	status = rte_swx_pipeline_build_from_spec(p->p,
1011 		spec,
1012 		&err_line,
1013 		&err_msg);
1014 	fclose(spec);
1015 	if (status) {
1016 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
1017 			status, err_line, err_msg);
1018 		return;
1019 	}
1020 
1021 	p->ctl = rte_swx_ctl_pipeline_create(p->p);
1022 	if (!p->ctl) {
1023 		snprintf(out, out_size, "Pipeline control create failed.");
1024 		rte_swx_pipeline_free(p->p);
1025 		return;
1026 	}
1027 }
1028 
1029 static void
1030 table_entry_free(struct rte_swx_table_entry *entry)
1031 {
1032 	if (!entry)
1033 		return;
1034 
1035 	free(entry->key);
1036 	free(entry->key_mask);
1037 	free(entry->action_data);
1038 	free(entry);
1039 }
1040 
1041 static const char cmd_pipeline_table_update_help[] =
1042 "pipeline <pipeline_name> table <table_name> update <file_name_add> "
1043 "<file_name_delete> <file_name_default>";
1044 
1045 static void
1046 cmd_pipeline_table_update(char **tokens,
1047 	uint32_t n_tokens,
1048 	char *out,
1049 	size_t out_size,
1050 	void *obj)
1051 {
1052 	struct pipeline *p;
1053 	char *pipeline_name, *table_name, *line = NULL;
1054 	char *file_name_add, *file_name_delete, *file_name_default;
1055 	FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL;
1056 	uint32_t line_id;
1057 	int status;
1058 
1059 	if (n_tokens != 8) {
1060 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1061 		return;
1062 	}
1063 
1064 	pipeline_name = tokens[1];
1065 	p = pipeline_find(obj, pipeline_name);
1066 	if (!p || !p->ctl) {
1067 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1068 		return;
1069 	}
1070 
1071 	if (strcmp(tokens[2], "table") != 0) {
1072 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
1073 		return;
1074 	}
1075 
1076 	table_name = tokens[3];
1077 
1078 	if (strcmp(tokens[4], "update") != 0) {
1079 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update");
1080 		return;
1081 	}
1082 
1083 	file_name_add = tokens[5];
1084 	file_name_delete = tokens[6];
1085 	file_name_default = tokens[7];
1086 
1087 	/* File open. */
1088 	if (strcmp(file_name_add, "none")) {
1089 		file_add = fopen(file_name_add, "r");
1090 		if (!file_add) {
1091 			snprintf(out, out_size, "Cannot open file %s",
1092 				file_name_add);
1093 			goto error;
1094 		}
1095 	}
1096 
1097 	if (strcmp(file_name_delete, "none")) {
1098 		file_delete = fopen(file_name_delete, "r");
1099 		if (!file_delete) {
1100 			snprintf(out, out_size, "Cannot open file %s",
1101 				file_name_delete);
1102 			goto error;
1103 		}
1104 	}
1105 
1106 	if (strcmp(file_name_default, "none")) {
1107 		file_default = fopen(file_name_default, "r");
1108 		if (!file_default) {
1109 			snprintf(out, out_size, "Cannot open file %s",
1110 				file_name_default);
1111 			goto error;
1112 		}
1113 	}
1114 
1115 	if (!file_add && !file_delete && !file_default) {
1116 		snprintf(out, out_size, "Nothing to be done.");
1117 		return;
1118 	}
1119 
1120 	/* Buffer allocation. */
1121 	line = malloc(2048);
1122 	if (!line) {
1123 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
1124 		goto error;
1125 	}
1126 
1127 	/* Add. */
1128 	if (file_add)
1129 		for (line_id = 1; ; line_id++) {
1130 			struct rte_swx_table_entry *entry;
1131 			int is_blank_or_comment;
1132 
1133 			if (fgets(line, 2048, file_add) == NULL)
1134 				break;
1135 
1136 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
1137 				table_name,
1138 				line,
1139 				&is_blank_or_comment);
1140 			if (!entry) {
1141 				if (is_blank_or_comment)
1142 					continue;
1143 
1144 				snprintf(out, out_size, MSG_FILE_ERR,
1145 					file_name_add, line_id);
1146 				goto error;
1147 			}
1148 
1149 			status = rte_swx_ctl_pipeline_table_entry_add(p->ctl,
1150 				table_name,
1151 				entry);
1152 			table_entry_free(entry);
1153 			if (status) {
1154 				snprintf(out, out_size,
1155 					"Invalid entry in file %s at line %u",
1156 					file_name_add, line_id);
1157 				goto error;
1158 			}
1159 		}
1160 
1161 
1162 	/* Delete. */
1163 	if (file_delete)
1164 		for (line_id = 1; ; line_id++) {
1165 			struct rte_swx_table_entry *entry;
1166 			int is_blank_or_comment;
1167 
1168 			if (fgets(line, 2048, file_delete) == NULL)
1169 				break;
1170 
1171 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
1172 				table_name,
1173 				line,
1174 				&is_blank_or_comment);
1175 			if (!entry) {
1176 				if (is_blank_or_comment)
1177 					continue;
1178 
1179 				snprintf(out, out_size, MSG_FILE_ERR,
1180 					file_name_delete, line_id);
1181 				goto error;
1182 			}
1183 
1184 			status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
1185 				table_name,
1186 				entry);
1187 			table_entry_free(entry);
1188 			if (status)  {
1189 				snprintf(out, out_size,
1190 					"Invalid entry in file %s at line %u",
1191 					file_name_delete, line_id);
1192 				goto error;
1193 			}
1194 		}
1195 
1196 	/* Default. */
1197 	if (file_default)
1198 		for (line_id = 1; ; line_id++) {
1199 			struct rte_swx_table_entry *entry;
1200 			int is_blank_or_comment;
1201 
1202 			if (fgets(line, 2048, file_default) == NULL)
1203 				break;
1204 
1205 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
1206 				table_name,
1207 				line,
1208 				&is_blank_or_comment);
1209 			if (!entry) {
1210 				if (is_blank_or_comment)
1211 					continue;
1212 
1213 				snprintf(out, out_size, MSG_FILE_ERR,
1214 					file_name_default, line_id);
1215 				goto error;
1216 			}
1217 
1218 			status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
1219 				table_name,
1220 				entry);
1221 			table_entry_free(entry);
1222 			if (status) {
1223 				snprintf(out, out_size,
1224 					"Invalid entry in file %s at line %u",
1225 					file_name_default, line_id);
1226 				goto error;
1227 			}
1228 		}
1229 
1230 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1231 	if (status) {
1232 		snprintf(out, out_size, "Commit failed.");
1233 		goto error;
1234 	}
1235 
1236 
1237 	rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
1238 
1239 	free(line);
1240 	if (file_add)
1241 		fclose(file_add);
1242 	if (file_delete)
1243 		fclose(file_delete);
1244 	if (file_default)
1245 		fclose(file_default);
1246 	return;
1247 
1248 error:
1249 	rte_swx_ctl_pipeline_abort(p->ctl);
1250 	free(line);
1251 	if (file_add)
1252 		fclose(file_add);
1253 	if (file_delete)
1254 		fclose(file_delete);
1255 	if (file_default)
1256 		fclose(file_default);
1257 }
1258 
1259 static const char cmd_pipeline_stats_help[] =
1260 "pipeline <pipeline_name> stats\n";
1261 
1262 static void
1263 cmd_pipeline_stats(char **tokens,
1264 	uint32_t n_tokens,
1265 	char *out,
1266 	size_t out_size,
1267 	void *obj)
1268 {
1269 	struct rte_swx_ctl_pipeline_info info;
1270 	struct pipeline *p;
1271 	uint32_t i;
1272 	int status;
1273 
1274 	if (n_tokens != 3) {
1275 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1276 		return;
1277 	}
1278 
1279 	p = pipeline_find(obj, tokens[1]);
1280 	if (!p || !p->ctl) {
1281 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1282 		return;
1283 	}
1284 
1285 	if (strcmp(tokens[2], "stats")) {
1286 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1287 		return;
1288 	}
1289 
1290 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1291 	if (status) {
1292 		snprintf(out, out_size, "Pipeline info get error.");
1293 		return;
1294 	}
1295 
1296 	snprintf(out, out_size, "Input ports:\n");
1297 	out_size -= strlen(out);
1298 	out += strlen(out);
1299 
1300 	for (i = 0; i < info.n_ports_in; i++) {
1301 		struct rte_swx_port_in_stats stats;
1302 
1303 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1304 
1305 		snprintf(out, out_size, "\tPort %u:"
1306 			" packets %" PRIu64
1307 			" bytes %" PRIu64
1308 			" empty %" PRIu64 "\n",
1309 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1310 		out_size -= strlen(out);
1311 		out += strlen(out);
1312 	}
1313 
1314 	snprintf(out, out_size, "Output ports:\n");
1315 	out_size -= strlen(out);
1316 	out += strlen(out);
1317 
1318 	for (i = 0; i < info.n_ports_out; i++) {
1319 		struct rte_swx_port_out_stats stats;
1320 
1321 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1322 
1323 		snprintf(out, out_size, "\tPort %u:"
1324 			" packets %" PRIu64
1325 			" bytes %" PRIu64 "\n",
1326 			i, stats.n_pkts, stats.n_bytes);
1327 		out_size -= strlen(out);
1328 		out += strlen(out);
1329 	}
1330 }
1331 
1332 static const char cmd_thread_pipeline_enable_help[] =
1333 "thread <thread_id> pipeline <pipeline_name> enable\n";
1334 
1335 static void
1336 cmd_thread_pipeline_enable(char **tokens,
1337 	uint32_t n_tokens,
1338 	char *out,
1339 	size_t out_size,
1340 	void *obj)
1341 {
1342 	char *pipeline_name;
1343 	struct pipeline *p;
1344 	uint32_t thread_id;
1345 	int status;
1346 
1347 	if (n_tokens != 5) {
1348 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1349 		return;
1350 	}
1351 
1352 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1353 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1354 		return;
1355 	}
1356 
1357 	if (strcmp(tokens[2], "pipeline") != 0) {
1358 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1359 		return;
1360 	}
1361 
1362 	pipeline_name = tokens[3];
1363 	p = pipeline_find(obj, pipeline_name);
1364 	if (!p || !p->ctl) {
1365 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1366 		return;
1367 	}
1368 
1369 	if (strcmp(tokens[4], "enable") != 0) {
1370 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
1371 		return;
1372 	}
1373 
1374 	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
1375 	if (status) {
1376 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
1377 		return;
1378 	}
1379 }
1380 
1381 static const char cmd_thread_pipeline_disable_help[] =
1382 "thread <thread_id> pipeline <pipeline_name> disable\n";
1383 
1384 static void
1385 cmd_thread_pipeline_disable(char **tokens,
1386 	uint32_t n_tokens,
1387 	char *out,
1388 	size_t out_size,
1389 	void *obj)
1390 {
1391 	struct pipeline *p;
1392 	char *pipeline_name;
1393 	uint32_t thread_id;
1394 	int status;
1395 
1396 	if (n_tokens != 5) {
1397 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1398 		return;
1399 	}
1400 
1401 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1402 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1403 		return;
1404 	}
1405 
1406 	if (strcmp(tokens[2], "pipeline") != 0) {
1407 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1408 		return;
1409 	}
1410 
1411 	pipeline_name = tokens[3];
1412 	p = pipeline_find(obj, pipeline_name);
1413 	if (!p || !p->ctl) {
1414 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1415 		return;
1416 	}
1417 
1418 	if (strcmp(tokens[4], "disable") != 0) {
1419 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
1420 		return;
1421 	}
1422 
1423 	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
1424 	if (status) {
1425 		snprintf(out, out_size, MSG_CMD_FAIL,
1426 			"thread pipeline disable");
1427 		return;
1428 	}
1429 }
1430 
1431 static void
1432 cmd_help(char **tokens,
1433 	 uint32_t n_tokens,
1434 	 char *out,
1435 	 size_t out_size,
1436 	 void *arg __rte_unused)
1437 {
1438 	tokens++;
1439 	n_tokens--;
1440 
1441 	if (n_tokens == 0) {
1442 		snprintf(out, out_size,
1443 			"Type 'help <command>' for command details.\n\n"
1444 			"List of commands:\n"
1445 			"\tmempool\n"
1446 			"\tlink\n"
1447 			"\ttap\n"
1448 			"\tpipeline create\n"
1449 			"\tpipeline port in\n"
1450 			"\tpipeline port out\n"
1451 			"\tpipeline build\n"
1452 			"\tpipeline table update\n"
1453 			"\tpipeline stats\n"
1454 			"\tthread pipeline enable\n"
1455 			"\tthread pipeline disable\n\n");
1456 		return;
1457 	}
1458 
1459 	if (strcmp(tokens[0], "mempool") == 0) {
1460 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1461 		return;
1462 	}
1463 
1464 	if (strcmp(tokens[0], "link") == 0) {
1465 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
1466 		return;
1467 	}
1468 
1469 	if (strcmp(tokens[0], "ring") == 0) {
1470 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
1471 		return;
1472 	}
1473 
1474 	if (strcmp(tokens[0], "tap") == 0) {
1475 		snprintf(out, out_size, "\n%s\n", cmd_tap_help);
1476 		return;
1477 	}
1478 
1479 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1480 		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
1481 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1482 		return;
1483 	}
1484 
1485 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1486 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
1487 		if (strcmp(tokens[2], "in") == 0) {
1488 			snprintf(out, out_size, "\n%s\n",
1489 				cmd_pipeline_port_in_help);
1490 			return;
1491 		}
1492 
1493 		if (strcmp(tokens[2], "out") == 0) {
1494 			snprintf(out, out_size, "\n%s\n",
1495 				cmd_pipeline_port_out_help);
1496 			return;
1497 		}
1498 	}
1499 
1500 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1501 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
1502 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
1503 		return;
1504 	}
1505 
1506 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1507 		(n_tokens == 3) &&
1508 		(strcmp(tokens[1], "table") == 0) &&
1509 		(strcmp(tokens[2], "update") == 0)) {
1510 		snprintf(out, out_size, "\n%s\n",
1511 			cmd_pipeline_table_update_help);
1512 		return;
1513 	}
1514 
1515 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1516 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
1517 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
1518 		return;
1519 	}
1520 
1521 	if ((n_tokens == 3) &&
1522 		(strcmp(tokens[0], "thread") == 0) &&
1523 		(strcmp(tokens[1], "pipeline") == 0)) {
1524 		if (strcmp(tokens[2], "enable") == 0) {
1525 			snprintf(out, out_size, "\n%s\n",
1526 				cmd_thread_pipeline_enable_help);
1527 			return;
1528 		}
1529 
1530 		if (strcmp(tokens[2], "disable") == 0) {
1531 			snprintf(out, out_size, "\n%s\n",
1532 				cmd_thread_pipeline_disable_help);
1533 			return;
1534 		}
1535 	}
1536 
1537 	snprintf(out, out_size, "Invalid command\n");
1538 }
1539 
1540 void
1541 cli_process(char *in, char *out, size_t out_size, void *obj)
1542 {
1543 	char *tokens[CMD_MAX_TOKENS];
1544 	uint32_t n_tokens = RTE_DIM(tokens);
1545 	int status;
1546 
1547 	if (is_comment(in))
1548 		return;
1549 
1550 	status = parse_tokenize_string(in, tokens, &n_tokens);
1551 	if (status) {
1552 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
1553 		return;
1554 	}
1555 
1556 	if (n_tokens == 0)
1557 		return;
1558 
1559 	if (strcmp(tokens[0], "help") == 0) {
1560 		cmd_help(tokens, n_tokens, out, out_size, obj);
1561 		return;
1562 	}
1563 
1564 	if (strcmp(tokens[0], "mempool") == 0) {
1565 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
1566 		return;
1567 	}
1568 
1569 	if (strcmp(tokens[0], "link") == 0) {
1570 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
1571 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
1572 			return;
1573 		}
1574 
1575 		cmd_link(tokens, n_tokens, out, out_size, obj);
1576 		return;
1577 	}
1578 
1579 	if (strcmp(tokens[0], "ring") == 0) {
1580 		cmd_ring(tokens, n_tokens, out, out_size, obj);
1581 		return;
1582 	}
1583 
1584 	if (strcmp(tokens[0], "tap") == 0) {
1585 		cmd_tap(tokens, n_tokens, out, out_size, obj);
1586 		return;
1587 	}
1588 
1589 	if (strcmp(tokens[0], "pipeline") == 0) {
1590 		if ((n_tokens >= 3) &&
1591 			(strcmp(tokens[2], "create") == 0)) {
1592 			cmd_pipeline_create(tokens, n_tokens, out, out_size,
1593 				obj);
1594 			return;
1595 		}
1596 
1597 		if ((n_tokens >= 4) &&
1598 			(strcmp(tokens[2], "port") == 0) &&
1599 			(strcmp(tokens[3], "in") == 0)) {
1600 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
1601 				obj);
1602 			return;
1603 		}
1604 
1605 		if ((n_tokens >= 4) &&
1606 			(strcmp(tokens[2], "port") == 0) &&
1607 			(strcmp(tokens[3], "out") == 0)) {
1608 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
1609 				obj);
1610 			return;
1611 		}
1612 
1613 		if ((n_tokens >= 3) &&
1614 			(strcmp(tokens[2], "build") == 0)) {
1615 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
1616 				obj);
1617 			return;
1618 		}
1619 
1620 		if ((n_tokens >= 3) &&
1621 			(strcmp(tokens[2], "table") == 0)) {
1622 			cmd_pipeline_table_update(tokens, n_tokens, out,
1623 				out_size, obj);
1624 			return;
1625 		}
1626 
1627 		if ((n_tokens >= 3) &&
1628 			(strcmp(tokens[2], "stats") == 0)) {
1629 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
1630 				obj);
1631 			return;
1632 		}
1633 	}
1634 
1635 	if (strcmp(tokens[0], "thread") == 0) {
1636 		if ((n_tokens >= 5) &&
1637 			(strcmp(tokens[4], "enable") == 0)) {
1638 			cmd_thread_pipeline_enable(tokens, n_tokens,
1639 				out, out_size, obj);
1640 			return;
1641 		}
1642 
1643 		if ((n_tokens >= 5) &&
1644 			(strcmp(tokens[4], "disable") == 0)) {
1645 			cmd_thread_pipeline_disable(tokens, n_tokens,
1646 				out, out_size, obj);
1647 			return;
1648 		}
1649 	}
1650 
1651 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
1652 }
1653 
1654 int
1655 cli_script_process(const char *file_name,
1656 	size_t msg_in_len_max,
1657 	size_t msg_out_len_max,
1658 	void *obj)
1659 {
1660 	char *msg_in = NULL, *msg_out = NULL;
1661 	FILE *f = NULL;
1662 
1663 	/* Check input arguments */
1664 	if ((file_name == NULL) ||
1665 		(strlen(file_name) == 0) ||
1666 		(msg_in_len_max == 0) ||
1667 		(msg_out_len_max == 0))
1668 		return -EINVAL;
1669 
1670 	msg_in = malloc(msg_in_len_max + 1);
1671 	msg_out = malloc(msg_out_len_max + 1);
1672 	if ((msg_in == NULL) ||
1673 		(msg_out == NULL)) {
1674 		free(msg_out);
1675 		free(msg_in);
1676 		return -ENOMEM;
1677 	}
1678 
1679 	/* Open input file */
1680 	f = fopen(file_name, "r");
1681 	if (f == NULL) {
1682 		free(msg_out);
1683 		free(msg_in);
1684 		return -EIO;
1685 	}
1686 
1687 	/* Read file */
1688 	for ( ; ; ) {
1689 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
1690 			break;
1691 
1692 		printf("%s", msg_in);
1693 		msg_out[0] = 0;
1694 
1695 		cli_process(msg_in,
1696 			msg_out,
1697 			msg_out_len_max,
1698 			obj);
1699 
1700 		if (strlen(msg_out))
1701 			printf("%s", msg_out);
1702 	}
1703 
1704 	/* Close file */
1705 	fclose(f);
1706 	free(msg_out);
1707 	free(msg_in);
1708 	return 0;
1709 }
1710