xref: /dpdk/examples/pipeline/cli.c (revision 079981e98090cd98e066a4bcad1ff780c34f2f69)
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> <file_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_delete = fopen(file_name_delete, "r");
840 		if (!file_delete) {
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_default = fopen(file_name_default, "r");
849 		if (!file_default) {
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 
897 	/* Delete. */
898 	if (file_delete)
899 		for (line_id = 1; ; line_id++) {
900 			struct rte_swx_table_entry *entry;
901 
902 			if (fgets(line, 2048, file_delete) == NULL)
903 				break;
904 
905 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
906 				table_name,
907 				line);
908 			if (!entry) {
909 				snprintf(out, out_size, MSG_FILE_ERR,
910 					file_name_delete, line_id);
911 				goto error;
912 			}
913 
914 			status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
915 				table_name,
916 				entry);
917 			if (status)  {
918 				snprintf(out, out_size,
919 					"Invalid entry in file %s at line %u",
920 					file_name_delete, line_id);
921 				goto error;
922 			}
923 		}
924 
925 	/* Default. */
926 	if (file_default)
927 		for (line_id = 1; ; line_id++) {
928 			struct rte_swx_table_entry *entry;
929 
930 			if (fgets(line, 2048, file_default) == NULL)
931 				break;
932 
933 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
934 				table_name,
935 				line);
936 			if (!entry) {
937 				snprintf(out, out_size, MSG_FILE_ERR,
938 					file_name_default, line_id);
939 				goto error;
940 			}
941 
942 			status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
943 				table_name,
944 				entry);
945 			if (status) {
946 				snprintf(out, out_size,
947 					"Invalid entry in file %s at line %u",
948 					file_name_default, line_id);
949 				goto error;
950 			}
951 		}
952 
953 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
954 	if (status) {
955 		snprintf(out, out_size, "Commit failed.");
956 		goto error;
957 	}
958 
959 
960 	rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
961 
962 	free(line);
963 	if (file_add)
964 		fclose(file_add);
965 	if (file_delete)
966 		fclose(file_delete);
967 	if (file_default)
968 		fclose(file_default);
969 	return;
970 
971 error:
972 	rte_swx_ctl_pipeline_abort(p->ctl);
973 	free(line);
974 	if (file_add)
975 		fclose(file_add);
976 	if (file_delete)
977 		fclose(file_delete);
978 	if (file_default)
979 		fclose(file_default);
980 }
981 
982 static const char cmd_pipeline_stats_help[] =
983 "pipeline <pipeline_name> stats\n";
984 
985 static void
986 cmd_pipeline_stats(char **tokens,
987 	uint32_t n_tokens,
988 	char *out,
989 	size_t out_size,
990 	void *obj)
991 {
992 	struct rte_swx_ctl_pipeline_info info;
993 	struct pipeline *p;
994 	uint32_t i;
995 	int status;
996 
997 	if (n_tokens != 3) {
998 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
999 		return;
1000 	}
1001 
1002 	p = pipeline_find(obj, tokens[1]);
1003 	if (!p || !p->ctl) {
1004 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1005 		return;
1006 	}
1007 
1008 	if (strcmp(tokens[2], "stats")) {
1009 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1010 		return;
1011 	}
1012 
1013 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1014 	if (status) {
1015 		snprintf(out, out_size, "Pipeline info get error.");
1016 		return;
1017 	}
1018 
1019 	snprintf(out, out_size, "Input ports:\n");
1020 	out_size -= strlen(out);
1021 	out += strlen(out);
1022 
1023 	for (i = 0; i < info.n_ports_in; i++) {
1024 		struct rte_swx_port_in_stats stats;
1025 
1026 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1027 
1028 		snprintf(out, out_size, "\tPort %u:"
1029 			" packets %" PRIu64
1030 			" bytes %" PRIu64
1031 			" empty %" PRIu64 "\n",
1032 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1033 		out_size -= strlen(out);
1034 		out += strlen(out);
1035 	}
1036 
1037 	snprintf(out, out_size, "Output ports:\n");
1038 	out_size -= strlen(out);
1039 	out += strlen(out);
1040 
1041 	for (i = 0; i < info.n_ports_out; i++) {
1042 		struct rte_swx_port_out_stats stats;
1043 
1044 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1045 
1046 		snprintf(out, out_size, "\tPort %u:"
1047 			" packets %" PRIu64
1048 			" bytes %" PRIu64 "\n",
1049 			i, stats.n_pkts, stats.n_bytes);
1050 		out_size -= strlen(out);
1051 		out += strlen(out);
1052 	}
1053 }
1054 
1055 static const char cmd_thread_pipeline_enable_help[] =
1056 "thread <thread_id> pipeline <pipeline_name> enable\n";
1057 
1058 static void
1059 cmd_thread_pipeline_enable(char **tokens,
1060 	uint32_t n_tokens,
1061 	char *out,
1062 	size_t out_size,
1063 	void *obj)
1064 {
1065 	char *pipeline_name;
1066 	struct pipeline *p;
1067 	uint32_t thread_id;
1068 	int status;
1069 
1070 	if (n_tokens != 5) {
1071 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1072 		return;
1073 	}
1074 
1075 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1076 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1077 		return;
1078 	}
1079 
1080 	if (strcmp(tokens[2], "pipeline") != 0) {
1081 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1082 		return;
1083 	}
1084 
1085 	pipeline_name = tokens[3];
1086 	p = pipeline_find(obj, pipeline_name);
1087 	if (!p || !p->ctl) {
1088 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1089 		return;
1090 	}
1091 
1092 	if (strcmp(tokens[4], "enable") != 0) {
1093 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
1094 		return;
1095 	}
1096 
1097 	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
1098 	if (status) {
1099 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
1100 		return;
1101 	}
1102 }
1103 
1104 static const char cmd_thread_pipeline_disable_help[] =
1105 "thread <thread_id> pipeline <pipeline_name> disable\n";
1106 
1107 static void
1108 cmd_thread_pipeline_disable(char **tokens,
1109 	uint32_t n_tokens,
1110 	char *out,
1111 	size_t out_size,
1112 	void *obj)
1113 {
1114 	struct pipeline *p;
1115 	char *pipeline_name;
1116 	uint32_t thread_id;
1117 	int status;
1118 
1119 	if (n_tokens != 5) {
1120 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1121 		return;
1122 	}
1123 
1124 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1125 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1126 		return;
1127 	}
1128 
1129 	if (strcmp(tokens[2], "pipeline") != 0) {
1130 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1131 		return;
1132 	}
1133 
1134 	pipeline_name = tokens[3];
1135 	p = pipeline_find(obj, pipeline_name);
1136 	if (!p || !p->ctl) {
1137 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1138 		return;
1139 	}
1140 
1141 	if (strcmp(tokens[4], "disable") != 0) {
1142 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
1143 		return;
1144 	}
1145 
1146 	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
1147 	if (status) {
1148 		snprintf(out, out_size, MSG_CMD_FAIL,
1149 			"thread pipeline disable");
1150 		return;
1151 	}
1152 }
1153 
1154 static void
1155 cmd_help(char **tokens,
1156 	 uint32_t n_tokens,
1157 	 char *out,
1158 	 size_t out_size,
1159 	 void *arg __rte_unused)
1160 {
1161 	tokens++;
1162 	n_tokens--;
1163 
1164 	if (n_tokens == 0) {
1165 		snprintf(out, out_size,
1166 			"Type 'help <command>' for command details.\n\n"
1167 			"List of commands:\n"
1168 			"\tmempool\n"
1169 			"\tlink\n"
1170 			"\tpipeline create\n"
1171 			"\tpipeline port in\n"
1172 			"\tpipeline port out\n"
1173 			"\tpipeline build\n"
1174 			"\tpipeline table update\n"
1175 			"\tpipeline stats\n"
1176 			"\tthread pipeline enable\n"
1177 			"\tthread pipeline disable\n\n");
1178 		return;
1179 	}
1180 
1181 	if (strcmp(tokens[0], "mempool") == 0) {
1182 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1183 		return;
1184 	}
1185 
1186 	if (strcmp(tokens[0], "link") == 0) {
1187 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
1188 		return;
1189 	}
1190 
1191 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1192 		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
1193 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1194 		return;
1195 	}
1196 
1197 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1198 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
1199 		if (strcmp(tokens[2], "in") == 0) {
1200 			snprintf(out, out_size, "\n%s\n",
1201 				cmd_pipeline_port_in_help);
1202 			return;
1203 		}
1204 
1205 		if (strcmp(tokens[2], "out") == 0) {
1206 			snprintf(out, out_size, "\n%s\n",
1207 				cmd_pipeline_port_out_help);
1208 			return;
1209 		}
1210 	}
1211 
1212 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1213 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
1214 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
1215 		return;
1216 	}
1217 
1218 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1219 		(n_tokens == 3) &&
1220 		(strcmp(tokens[1], "table") == 0) &&
1221 		(strcmp(tokens[2], "update") == 0)) {
1222 		snprintf(out, out_size, "\n%s\n",
1223 			cmd_pipeline_table_update_help);
1224 		return;
1225 	}
1226 
1227 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1228 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
1229 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
1230 		return;
1231 	}
1232 
1233 	if ((n_tokens == 3) &&
1234 		(strcmp(tokens[0], "thread") == 0) &&
1235 		(strcmp(tokens[1], "pipeline") == 0)) {
1236 		if (strcmp(tokens[2], "enable") == 0) {
1237 			snprintf(out, out_size, "\n%s\n",
1238 				cmd_thread_pipeline_enable_help);
1239 			return;
1240 		}
1241 
1242 		if (strcmp(tokens[2], "disable") == 0) {
1243 			snprintf(out, out_size, "\n%s\n",
1244 				cmd_thread_pipeline_disable_help);
1245 			return;
1246 		}
1247 	}
1248 
1249 	snprintf(out, out_size, "Invalid command\n");
1250 }
1251 
1252 void
1253 cli_process(char *in, char *out, size_t out_size, void *obj)
1254 {
1255 	char *tokens[CMD_MAX_TOKENS];
1256 	uint32_t n_tokens = RTE_DIM(tokens);
1257 	int status;
1258 
1259 	if (is_comment(in))
1260 		return;
1261 
1262 	status = parse_tokenize_string(in, tokens, &n_tokens);
1263 	if (status) {
1264 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
1265 		return;
1266 	}
1267 
1268 	if (n_tokens == 0)
1269 		return;
1270 
1271 	if (strcmp(tokens[0], "help") == 0) {
1272 		cmd_help(tokens, n_tokens, out, out_size, obj);
1273 		return;
1274 	}
1275 
1276 	if (strcmp(tokens[0], "mempool") == 0) {
1277 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
1278 		return;
1279 	}
1280 
1281 	if (strcmp(tokens[0], "link") == 0) {
1282 		if (strcmp(tokens[1], "show") == 0) {
1283 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
1284 			return;
1285 		}
1286 
1287 		cmd_link(tokens, n_tokens, out, out_size, obj);
1288 		return;
1289 	}
1290 
1291 	if (strcmp(tokens[0], "pipeline") == 0) {
1292 		if ((n_tokens >= 3) &&
1293 			(strcmp(tokens[2], "create") == 0)) {
1294 			cmd_pipeline_create(tokens, n_tokens, out, out_size,
1295 				obj);
1296 			return;
1297 		}
1298 
1299 		if ((n_tokens >= 4) &&
1300 			(strcmp(tokens[2], "port") == 0) &&
1301 			(strcmp(tokens[3], "in") == 0)) {
1302 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
1303 				obj);
1304 			return;
1305 		}
1306 
1307 		if ((n_tokens >= 4) &&
1308 			(strcmp(tokens[2], "port") == 0) &&
1309 			(strcmp(tokens[3], "out") == 0)) {
1310 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
1311 				obj);
1312 			return;
1313 		}
1314 
1315 		if ((n_tokens >= 3) &&
1316 			(strcmp(tokens[2], "build") == 0)) {
1317 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
1318 				obj);
1319 			return;
1320 		}
1321 
1322 		if ((n_tokens >= 3) &&
1323 			(strcmp(tokens[2], "table") == 0)) {
1324 			cmd_pipeline_table_update(tokens, n_tokens, out,
1325 				out_size, obj);
1326 			return;
1327 		}
1328 
1329 		if ((n_tokens >= 3) &&
1330 			(strcmp(tokens[2], "stats") == 0)) {
1331 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
1332 				obj);
1333 			return;
1334 		}
1335 	}
1336 
1337 	if (strcmp(tokens[0], "thread") == 0) {
1338 		if ((n_tokens >= 5) &&
1339 			(strcmp(tokens[4], "enable") == 0)) {
1340 			cmd_thread_pipeline_enable(tokens, n_tokens,
1341 				out, out_size, obj);
1342 			return;
1343 		}
1344 
1345 		if ((n_tokens >= 5) &&
1346 			(strcmp(tokens[4], "disable") == 0)) {
1347 			cmd_thread_pipeline_disable(tokens, n_tokens,
1348 				out, out_size, obj);
1349 			return;
1350 		}
1351 	}
1352 
1353 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
1354 }
1355 
1356 int
1357 cli_script_process(const char *file_name,
1358 	size_t msg_in_len_max,
1359 	size_t msg_out_len_max,
1360 	void *obj)
1361 {
1362 	char *msg_in = NULL, *msg_out = NULL;
1363 	FILE *f = NULL;
1364 
1365 	/* Check input arguments */
1366 	if ((file_name == NULL) ||
1367 		(strlen(file_name) == 0) ||
1368 		(msg_in_len_max == 0) ||
1369 		(msg_out_len_max == 0))
1370 		return -EINVAL;
1371 
1372 	msg_in = malloc(msg_in_len_max + 1);
1373 	msg_out = malloc(msg_out_len_max + 1);
1374 	if ((msg_in == NULL) ||
1375 		(msg_out == NULL)) {
1376 		free(msg_out);
1377 		free(msg_in);
1378 		return -ENOMEM;
1379 	}
1380 
1381 	/* Open input file */
1382 	f = fopen(file_name, "r");
1383 	if (f == NULL) {
1384 		free(msg_out);
1385 		free(msg_in);
1386 		return -EIO;
1387 	}
1388 
1389 	/* Read file */
1390 	for ( ; ; ) {
1391 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
1392 			break;
1393 
1394 		printf("%s", msg_in);
1395 		msg_out[0] = 0;
1396 
1397 		cli_process(msg_in,
1398 			msg_out,
1399 			msg_out_len_max,
1400 			obj);
1401 
1402 		if (strlen(msg_out))
1403 			printf("%s", msg_out);
1404 	}
1405 
1406 	/* Close file */
1407 	fclose(f);
1408 	free(msg_out);
1409 	free(msg_in);
1410 	return 0;
1411 }
1412