xref: /dpdk/examples/pipeline/cli.c (revision fd5f9fb95ff5fb2543ad1cd8b4206f531d443347)
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_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 		return;
1168 	}
1169 
1170 	if (strcmp(tokens[0], "mempool") == 0) {
1171 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1172 		return;
1173 	}
1174 
1175 	if (strcmp(tokens[0], "link") == 0) {
1176 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
1177 		return;
1178 	}
1179 
1180 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1181 		((n_tokens == 1) && (strcmp(tokens[2], "create")) == 0)) {
1182 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1183 		return;
1184 	}
1185 
1186 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1187 		(strcmp(tokens[1], "port") == 0)) {
1188 		if ((n_tokens == 3) && (strcmp(tokens[2], "in")) == 0) {
1189 			snprintf(out, out_size, "\n%s\n",
1190 				cmd_pipeline_port_in_help);
1191 			return;
1192 		}
1193 
1194 		if ((n_tokens == 3) && (strcmp(tokens[2], "out")) == 0) {
1195 			snprintf(out, out_size, "\n%s\n",
1196 				cmd_pipeline_port_out_help);
1197 			return;
1198 		}
1199 	}
1200 
1201 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1202 		((n_tokens >= 2) && (strcmp(tokens[2], "build")) == 0)) {
1203 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
1204 		return;
1205 	}
1206 
1207 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1208 		((n_tokens >= 2) && (strcmp(tokens[2], "table")) == 0)) {
1209 		snprintf(out, out_size, "\n%s\n",
1210 			cmd_pipeline_table_update_help);
1211 		return;
1212 	}
1213 
1214 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1215 		((n_tokens >= 2) && (strcmp(tokens[2], "stats")) == 0)) {
1216 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
1217 		return;
1218 	}
1219 
1220 	if ((n_tokens == 3) &&
1221 		(strcmp(tokens[0], "thread") == 0) &&
1222 		(strcmp(tokens[1], "pipeline") == 0)) {
1223 		if (strcmp(tokens[2], "enable") == 0) {
1224 			snprintf(out, out_size, "\n%s\n",
1225 				cmd_thread_pipeline_enable_help);
1226 			return;
1227 		}
1228 
1229 		if (strcmp(tokens[2], "disable") == 0) {
1230 			snprintf(out, out_size, "\n%s\n",
1231 				cmd_thread_pipeline_disable_help);
1232 			return;
1233 		}
1234 	}
1235 
1236 	snprintf(out, out_size, "Invalid command\n");
1237 }
1238 
1239 void
1240 cli_process(char *in, char *out, size_t out_size, void *obj)
1241 {
1242 	char *tokens[CMD_MAX_TOKENS];
1243 	uint32_t n_tokens = RTE_DIM(tokens);
1244 	int status;
1245 
1246 	if (is_comment(in))
1247 		return;
1248 
1249 	status = parse_tokenize_string(in, tokens, &n_tokens);
1250 	if (status) {
1251 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
1252 		return;
1253 	}
1254 
1255 	if (n_tokens == 0)
1256 		return;
1257 
1258 	if (strcmp(tokens[0], "help") == 0) {
1259 		cmd_help(tokens, n_tokens, out, out_size, obj);
1260 		return;
1261 	}
1262 
1263 	if (strcmp(tokens[0], "mempool") == 0) {
1264 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
1265 		return;
1266 	}
1267 
1268 	if (strcmp(tokens[0], "link") == 0) {
1269 		if (strcmp(tokens[1], "show") == 0) {
1270 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
1271 			return;
1272 		}
1273 
1274 		cmd_link(tokens, n_tokens, out, out_size, obj);
1275 		return;
1276 	}
1277 
1278 	if (strcmp(tokens[0], "pipeline") == 0) {
1279 		if ((n_tokens >= 3) &&
1280 			(strcmp(tokens[2], "create") == 0)) {
1281 			cmd_pipeline_create(tokens, n_tokens, out, out_size,
1282 				obj);
1283 			return;
1284 		}
1285 
1286 		if ((n_tokens >= 4) &&
1287 			(strcmp(tokens[2], "port") == 0) &&
1288 			(strcmp(tokens[3], "in") == 0)) {
1289 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
1290 				obj);
1291 			return;
1292 		}
1293 
1294 		if ((n_tokens >= 4) &&
1295 			(strcmp(tokens[2], "port") == 0) &&
1296 			(strcmp(tokens[3], "out") == 0)) {
1297 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
1298 				obj);
1299 			return;
1300 		}
1301 
1302 		if ((n_tokens >= 3) &&
1303 			(strcmp(tokens[2], "build") == 0)) {
1304 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
1305 				obj);
1306 			return;
1307 		}
1308 
1309 		if ((n_tokens >= 3) &&
1310 			(strcmp(tokens[2], "table") == 0)) {
1311 			cmd_pipeline_table_update(tokens, n_tokens, out,
1312 				out_size, obj);
1313 			return;
1314 		}
1315 
1316 		if ((n_tokens >= 3) &&
1317 			(strcmp(tokens[2], "stats") == 0)) {
1318 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
1319 				obj);
1320 			return;
1321 		}
1322 	}
1323 
1324 	if (strcmp(tokens[0], "thread") == 0) {
1325 		if ((n_tokens >= 5) &&
1326 			(strcmp(tokens[4], "enable") == 0)) {
1327 			cmd_thread_pipeline_enable(tokens, n_tokens,
1328 				out, out_size, obj);
1329 			return;
1330 		}
1331 
1332 		if ((n_tokens >= 5) &&
1333 			(strcmp(tokens[4], "disable") == 0)) {
1334 			cmd_thread_pipeline_disable(tokens, n_tokens,
1335 				out, out_size, obj);
1336 			return;
1337 		}
1338 	}
1339 
1340 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
1341 }
1342 
1343 int
1344 cli_script_process(const char *file_name,
1345 	size_t msg_in_len_max,
1346 	size_t msg_out_len_max,
1347 	void *obj)
1348 {
1349 	char *msg_in = NULL, *msg_out = NULL;
1350 	FILE *f = NULL;
1351 
1352 	/* Check input arguments */
1353 	if ((file_name == NULL) ||
1354 		(strlen(file_name) == 0) ||
1355 		(msg_in_len_max == 0) ||
1356 		(msg_out_len_max == 0))
1357 		return -EINVAL;
1358 
1359 	msg_in = malloc(msg_in_len_max + 1);
1360 	msg_out = malloc(msg_out_len_max + 1);
1361 	if ((msg_in == NULL) ||
1362 		(msg_out == NULL)) {
1363 		free(msg_out);
1364 		free(msg_in);
1365 		return -ENOMEM;
1366 	}
1367 
1368 	/* Open input file */
1369 	f = fopen(file_name, "r");
1370 	if (f == NULL) {
1371 		free(msg_out);
1372 		free(msg_in);
1373 		return -EIO;
1374 	}
1375 
1376 	/* Read file */
1377 	for ( ; ; ) {
1378 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
1379 			break;
1380 
1381 		printf("%s", msg_in);
1382 		msg_out[0] = 0;
1383 
1384 		cli_process(msg_in,
1385 			msg_out,
1386 			msg_out_len_max,
1387 			obj);
1388 
1389 		if (strlen(msg_out))
1390 			printf("%s", msg_out);
1391 	}
1392 
1393 	/* Close file */
1394 	fclose(f);
1395 	free(msg_out);
1396 	free(msg_in);
1397 	return 0;
1398 }
1399