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