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