xref: /dpdk/drivers/net/softnic/rte_eth_softnic_cli.c (revision d2384afb4776b4f775bfe238aaa7912423335ebd)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2018 Intel Corporation
3  */
4 
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <rte_common.h>
12 #include <rte_cycles.h>
13 #include <rte_string_fns.h>
14 
15 #include "rte_eth_softnic_internals.h"
16 
17 #ifndef CMD_MAX_TOKENS
18 #define CMD_MAX_TOKENS     256
19 #endif
20 
21 #ifndef MAX_LINE_SIZE
22 #define MAX_LINE_SIZE 2048
23 #endif
24 
25 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
26 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
27 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
28 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
29 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
30 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
31 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
32 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
33 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
34 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
35 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
36 
37 static int
38 parser_read_uint64(uint64_t *value, char *p)
39 {
40 	uint64_t val = 0;
41 
42 	if (!value || !p || !p[0])
43 		return -EINVAL;
44 
45 	val = strtoull(p, &p, 0);
46 	if (p[0])
47 		return -EINVAL;
48 
49 	*value = val;
50 	return 0;
51 }
52 
53 static int
54 parser_read_uint32(uint32_t *value, char *p)
55 {
56 	uint32_t val = 0;
57 
58 	if (!value || !p || !p[0])
59 		return -EINVAL;
60 
61 	val = strtoul(p, &p, 0);
62 	if (p[0])
63 		return -EINVAL;
64 
65 	*value = val;
66 	return 0;
67 }
68 
69 #define PARSE_DELIMITER " \f\n\r\t\v"
70 
71 static int
72 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
73 {
74 	uint32_t i;
75 
76 	if (!string || !tokens || !n_tokens || !*n_tokens)
77 		return -EINVAL;
78 
79 	for (i = 0; i < *n_tokens; i++) {
80 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
81 		if (!tokens[i])
82 			break;
83 	}
84 
85 	if (i == *n_tokens && strtok_r(string, PARSE_DELIMITER, &string))
86 		return -E2BIG;
87 
88 	*n_tokens = i;
89 	return 0;
90 }
91 
92 static int
93 is_comment(char *in)
94 {
95 	if ((strlen(in) && index("!#%;", in[0])) ||
96 		(strncmp(in, "//", 2) == 0) ||
97 		(strncmp(in, "--", 2) == 0))
98 		return 1;
99 
100 	return 0;
101 }
102 
103 /**
104  * mempool <mempool_name>
105  *  buffer <buffer_size>
106  *  pool <pool_size>
107  *  cache <cache_size>
108  */
109 static void
110 cmd_mempool(struct pmd_internals *softnic,
111 	char **tokens,
112 	uint32_t n_tokens,
113 	char *out,
114 	size_t out_size)
115 {
116 	struct softnic_mempool_params p;
117 	char *name;
118 	struct softnic_mempool *mempool;
119 
120 	if (n_tokens != 8) {
121 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
122 		return;
123 	}
124 
125 	name = tokens[1];
126 
127 	if (strcmp(tokens[2], "buffer") != 0) {
128 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
129 		return;
130 	}
131 
132 	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
133 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
134 		return;
135 	}
136 
137 	if (strcmp(tokens[4], "pool") != 0) {
138 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
139 		return;
140 	}
141 
142 	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
143 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
144 		return;
145 	}
146 
147 	if (strcmp(tokens[6], "cache") != 0) {
148 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
149 		return;
150 	}
151 
152 	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
153 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
154 		return;
155 	}
156 
157 	mempool = softnic_mempool_create(softnic, name, &p);
158 	if (mempool == NULL) {
159 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
160 		return;
161 	}
162 }
163 
164 /**
165  * swq <swq_name>
166  *  size <size>
167  */
168 static void
169 cmd_swq(struct pmd_internals *softnic,
170 	char **tokens,
171 	uint32_t n_tokens,
172 	char *out,
173 	size_t out_size)
174 {
175 	struct softnic_swq_params p;
176 	char *name;
177 	struct softnic_swq *swq;
178 
179 	if (n_tokens != 4) {
180 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
181 		return;
182 	}
183 
184 	name = tokens[1];
185 
186 	if (strcmp(tokens[2], "size") != 0) {
187 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
188 		return;
189 	}
190 
191 	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
192 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
193 		return;
194 	}
195 
196 	swq = softnic_swq_create(softnic, name, &p);
197 	if (swq == NULL) {
198 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
199 		return;
200 	}
201 }
202 
203 /**
204  * pipeline codegen <spec_file> <code_file>
205  */
206 static void
207 cmd_softnic_pipeline_codegen(struct pmd_internals *softnic __rte_unused,
208 	char **tokens,
209 	uint32_t n_tokens,
210 	char *out,
211 	size_t out_size)
212 {
213 	FILE *spec_file = NULL;
214 	FILE *code_file = NULL;
215 	uint32_t err_line;
216 	const char *err_msg;
217 	int status;
218 
219 	if (n_tokens != 4) {
220 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
221 		return;
222 	}
223 
224 	spec_file = fopen(tokens[2], "r");
225 	if (!spec_file) {
226 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
227 		return;
228 	}
229 
230 	code_file = fopen(tokens[3], "w");
231 	if (!code_file) {
232 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
233 		return;
234 	}
235 
236 	status = rte_swx_pipeline_codegen(spec_file,
237 					  code_file,
238 					  &err_line,
239 					  &err_msg);
240 
241 	fclose(spec_file);
242 	fclose(code_file);
243 
244 	if (status) {
245 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
246 			status, err_line, err_msg);
247 		return;
248 	}
249 }
250 
251 /**
252  * pipeline libbuild <code_file> <lib_file>
253  */
254 static void
255 cmd_softnic_pipeline_libbuild(struct pmd_internals *softnic __rte_unused,
256 	char **tokens,
257 	uint32_t n_tokens,
258 	char *out,
259 	size_t out_size)
260 {
261 	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
262 	char *install_dir, *cwd = NULL, *buffer = NULL;
263 	size_t length;
264 	int status = 0;
265 
266 	if (n_tokens != 4) {
267 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
268 		goto free;
269 	}
270 
271 	install_dir = getenv("RTE_INSTALL_DIR");
272 	if (!install_dir) {
273 		cwd = malloc(MAX_LINE_SIZE);
274 		if (!cwd) {
275 			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
276 			goto free;
277 		}
278 
279 		install_dir = getcwd(cwd, MAX_LINE_SIZE);
280 		if (!install_dir) {
281 			snprintf(out, out_size, "Error: Path too long.\n");
282 			goto free;
283 		}
284 	}
285 
286 	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
287 	out_size -= strlen(out);
288 	out += strlen(out);
289 
290 	code_file = tokens[2];
291 	length = strnlen(code_file, MAX_LINE_SIZE);
292 	if (length < 3 ||
293 	    code_file[length - 2] != '.' ||
294 	    code_file[length - 1] != 'c') {
295 		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
296 		goto free;
297 	}
298 
299 	lib_file = tokens[3];
300 	length = strnlen(lib_file, MAX_LINE_SIZE);
301 	if (length < 4 ||
302 	    lib_file[length - 3] != '.' ||
303 	    lib_file[length - 2] != 's' ||
304 	    lib_file[length - 1] != 'o') {
305 		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
306 		goto free;
307 	}
308 
309 	obj_file = malloc(length);
310 	log_file = malloc(length + 2);
311 	if (!obj_file || !log_file) {
312 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
313 		goto free;
314 	}
315 
316 	memcpy(obj_file, lib_file, length - 2);
317 	obj_file[length - 2] = 'o';
318 	obj_file[length - 1] = 0;
319 
320 	memcpy(log_file, lib_file, length - 2);
321 	log_file[length - 2] = 'l';
322 	log_file[length - 1] = 'o';
323 	log_file[length] = 'g';
324 	log_file[length + 1] = 0;
325 
326 	buffer = malloc(MAX_LINE_SIZE);
327 	if (!buffer) {
328 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
329 		return;
330 	}
331 
332 	snprintf(buffer,
333 		 MAX_LINE_SIZE,
334 		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
335 		 "-I %s/lib/pipeline "
336 		 "-I %s/lib/eal/include "
337 		 "-I %s/lib/eal/x86/include "
338 		 "-I %s/lib/eal/include/generic "
339 		 "-I %s/lib/meter "
340 		 "-I %s/lib/port "
341 		 "-I %s/lib/table "
342 		 "-I %s/lib/pipeline "
343 		 "-I %s/config "
344 		 "-I %s/build "
345 		 "-I %s/lib/eal/linux/include "
346 		 ">%s 2>&1 "
347 		 "&& "
348 		 "gcc -shared %s -o %s "
349 		 ">>%s 2>&1",
350 		 obj_file,
351 		 code_file,
352 		 install_dir,
353 		 install_dir,
354 		 install_dir,
355 		 install_dir,
356 		 install_dir,
357 		 install_dir,
358 		 install_dir,
359 		 install_dir,
360 		 install_dir,
361 		 install_dir,
362 		 install_dir,
363 		 log_file,
364 		 obj_file,
365 		 lib_file,
366 		 log_file);
367 
368 	status = system(buffer);
369 	if (status) {
370 		snprintf(out,
371 			 out_size,
372 			 "Library build failed, see file \"%s\" for details.\n",
373 			 log_file);
374 		goto free;
375 	}
376 
377 free:
378 	free(cwd);
379 	free(obj_file);
380 	free(log_file);
381 	free(buffer);
382 }
383 
384 /**
385  * pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>
386  */
387 static void
388 cmd_softnic_pipeline_build(struct pmd_internals *softnic,
389 	char **tokens,
390 	uint32_t n_tokens,
391 	char *out,
392 	size_t out_size)
393 {
394 	struct pipeline *p = NULL;
395 	char *pipeline_name, *lib_file_name, *iospec_file_name;
396 	uint32_t numa_node = 0;
397 
398 	/* Parsing. */
399 	if (n_tokens != 9) {
400 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
401 		return;
402 	}
403 
404 	pipeline_name = tokens[1];
405 
406 	if (strcmp(tokens[2], "build")) {
407 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
408 		return;
409 	}
410 
411 	if (strcmp(tokens[3], "lib")) {
412 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
413 		return;
414 	}
415 
416 	lib_file_name = tokens[4];
417 
418 	if (strcmp(tokens[5], "io")) {
419 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
420 		return;
421 	}
422 
423 	iospec_file_name = tokens[6];
424 
425 	if (strcmp(tokens[7], "numa")) {
426 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
427 		return;
428 	}
429 
430 	if (parser_read_uint32(&numa_node, tokens[8])) {
431 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
432 		return;
433 	}
434 
435 	/* Pipeline create. */
436 	p = softnic_pipeline_create(softnic,
437 				    pipeline_name,
438 				    lib_file_name,
439 				    iospec_file_name,
440 				    (int)numa_node);
441 	if (!p)
442 		snprintf(out, out_size, "Pipeline creation failed.\n");
443 }
444 
445 static void
446 table_entry_free(struct rte_swx_table_entry *entry)
447 {
448 	if (!entry)
449 		return;
450 
451 	free(entry->key);
452 	free(entry->key_mask);
453 	free(entry->action_data);
454 	free(entry);
455 }
456 
457 static int
458 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
459 			   const char *table_name,
460 			   FILE *file,
461 			   uint32_t *file_line_number)
462 {
463 	char *line = NULL;
464 	uint32_t line_id = 0;
465 	int status = 0;
466 
467 	/* Buffer allocation. */
468 	line = malloc(MAX_LINE_SIZE);
469 	if (!line)
470 		return -ENOMEM;
471 
472 	/* File read. */
473 	for (line_id = 1; ; line_id++) {
474 		struct rte_swx_table_entry *entry;
475 		int is_blank_or_comment;
476 
477 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
478 			break;
479 
480 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
481 							      table_name,
482 							      line,
483 							      &is_blank_or_comment);
484 		if (!entry) {
485 			if (is_blank_or_comment)
486 				continue;
487 
488 			status = -EINVAL;
489 			goto error;
490 		}
491 
492 		status = rte_swx_ctl_pipeline_table_entry_add(p,
493 							      table_name,
494 							      entry);
495 		table_entry_free(entry);
496 		if (status)
497 			goto error;
498 	}
499 
500 error:
501 	free(line);
502 	*file_line_number = line_id;
503 	return status;
504 }
505 
506 /**
507  * pipeline <pipeline_name> table <table_name> add <file_name>
508  */
509 static void
510 cmd_softnic_pipeline_table_add(struct pmd_internals *softnic,
511 	char **tokens,
512 	uint32_t n_tokens,
513 	char *out,
514 	size_t out_size)
515 {
516 	struct pipeline *p;
517 	char *pipeline_name, *table_name, *file_name;
518 	FILE *file = NULL;
519 	uint32_t file_line_number = 0;
520 	int status;
521 
522 	if (n_tokens != 6) {
523 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
524 		return;
525 	}
526 
527 	pipeline_name = tokens[1];
528 	p = softnic_pipeline_find(softnic, pipeline_name);
529 	if (!p) {
530 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
531 		return;
532 	}
533 
534 	table_name = tokens[3];
535 
536 	file_name = tokens[5];
537 	file = fopen(file_name, "r");
538 	if (!file) {
539 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
540 		return;
541 	}
542 
543 	status = pipeline_table_entries_add(p->ctl,
544 					    table_name,
545 					    file,
546 					    &file_line_number);
547 	if (status)
548 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
549 			 file_name,
550 			 file_line_number);
551 
552 	fclose(file);
553 }
554 
555 static int
556 pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
557 			      const char *table_name,
558 			      FILE *file,
559 			      uint32_t *file_line_number)
560 {
561 	char *line = NULL;
562 	uint32_t line_id = 0;
563 	int status = 0;
564 
565 	/* Buffer allocation. */
566 	line = malloc(MAX_LINE_SIZE);
567 	if (!line)
568 		return -ENOMEM;
569 
570 	/* File read. */
571 	for (line_id = 1; ; line_id++) {
572 		struct rte_swx_table_entry *entry;
573 		int is_blank_or_comment;
574 
575 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
576 			break;
577 
578 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
579 							      table_name,
580 							      line,
581 							      &is_blank_or_comment);
582 		if (!entry) {
583 			if (is_blank_or_comment)
584 				continue;
585 
586 			status = -EINVAL;
587 			goto error;
588 		}
589 
590 		status = rte_swx_ctl_pipeline_table_entry_delete(p,
591 								 table_name,
592 								 entry);
593 		table_entry_free(entry);
594 		if (status)
595 			goto error;
596 	}
597 
598 error:
599 	*file_line_number = line_id;
600 	free(line);
601 	return status;
602 }
603 
604 /**
605  * pipeline <pipeline_name> table <table_name> delete <file_name>
606  */
607 static void
608 cmd_softnic_pipeline_table_delete(struct pmd_internals *softnic,
609 	char **tokens,
610 	uint32_t n_tokens,
611 	char *out,
612 	size_t out_size)
613 {
614 	struct pipeline *p;
615 	char *pipeline_name, *table_name, *file_name;
616 	FILE *file = NULL;
617 	uint32_t file_line_number = 0;
618 	int status;
619 
620 	if (n_tokens != 6) {
621 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
622 		return;
623 	}
624 
625 	pipeline_name = tokens[1];
626 	p = softnic_pipeline_find(softnic, pipeline_name);
627 	if (!p) {
628 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
629 		return;
630 	}
631 
632 	table_name = tokens[3];
633 
634 	file_name = tokens[5];
635 	file = fopen(file_name, "r");
636 	if (!file) {
637 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
638 		return;
639 	}
640 
641 	status = pipeline_table_entries_delete(p->ctl,
642 					       table_name,
643 					       file,
644 					       &file_line_number);
645 	if (status)
646 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
647 			 file_name,
648 			 file_line_number);
649 
650 	fclose(file);
651 }
652 
653 static int
654 pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
655 				 const char *table_name,
656 				 FILE *file,
657 				 uint32_t *file_line_number)
658 {
659 	char *line = NULL;
660 	uint32_t line_id = 0;
661 	int status = 0;
662 
663 	/* Buffer allocation. */
664 	line = malloc(MAX_LINE_SIZE);
665 	if (!line)
666 		return -ENOMEM;
667 
668 	/* File read. */
669 	for (line_id = 1; ; line_id++) {
670 		struct rte_swx_table_entry *entry;
671 		int is_blank_or_comment;
672 
673 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
674 			break;
675 
676 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
677 							      table_name,
678 							      line,
679 							      &is_blank_or_comment);
680 		if (!entry) {
681 			if (is_blank_or_comment)
682 				continue;
683 
684 			status = -EINVAL;
685 			goto error;
686 		}
687 
688 		status = rte_swx_ctl_pipeline_table_default_entry_add(p,
689 								      table_name,
690 								      entry);
691 		table_entry_free(entry);
692 		if (status)
693 			goto error;
694 	}
695 
696 error:
697 	*file_line_number = line_id;
698 	free(line);
699 	return status;
700 }
701 
702 /**
703  * pipeline <pipeline_name> table <table_name> default <file_name>
704  */
705 static void
706 cmd_softnic_pipeline_table_default(struct pmd_internals *softnic,
707 	char **tokens,
708 	uint32_t n_tokens,
709 	char *out,
710 	size_t out_size)
711 {
712 	struct pipeline *p;
713 	char *pipeline_name, *table_name, *file_name;
714 	FILE *file = NULL;
715 	uint32_t file_line_number = 0;
716 	int status;
717 
718 	if (n_tokens != 6) {
719 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
720 		return;
721 	}
722 
723 	pipeline_name = tokens[1];
724 	p = softnic_pipeline_find(softnic, pipeline_name);
725 	if (!p) {
726 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
727 		return;
728 	}
729 
730 	table_name = tokens[3];
731 
732 	file_name = tokens[5];
733 	file = fopen(file_name, "r");
734 	if (!file) {
735 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
736 		return;
737 	}
738 
739 	status = pipeline_table_default_entry_add(p->ctl,
740 						  table_name,
741 						  file,
742 						  &file_line_number);
743 	if (status)
744 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
745 			 file_name,
746 			 file_line_number);
747 
748 	fclose(file);
749 }
750 
751 /**
752  * pipeline <pipeline_name> table <table_name> show [filename]
753  */
754 static void
755 cmd_softnic_pipeline_table_show(struct pmd_internals *softnic __rte_unused,
756 	char **tokens,
757 	uint32_t n_tokens,
758 	char *out,
759 	size_t out_size)
760 {
761 	struct pipeline *p;
762 	char *pipeline_name, *table_name;
763 	FILE *file = NULL;
764 	int status;
765 
766 	if (n_tokens != 5 && n_tokens != 6) {
767 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
768 		return;
769 	}
770 
771 	pipeline_name = tokens[1];
772 	p = softnic_pipeline_find(softnic, pipeline_name);
773 	if (!p) {
774 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
775 		return;
776 	}
777 
778 	table_name = tokens[3];
779 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
780 	if (!file) {
781 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
782 		return;
783 	}
784 
785 	status = rte_swx_ctl_pipeline_table_fprintf(file, p->ctl, table_name);
786 	if (status)
787 		snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
788 
789 	if (file)
790 		fclose(file);
791 }
792 
793 /**
794  * pipeline <pipeline_name> selector <selector_name> group add
795  */
796 static void
797 cmd_softnic_pipeline_selector_group_add(struct pmd_internals *softnic,
798 	char **tokens,
799 	uint32_t n_tokens,
800 	char *out,
801 	size_t out_size)
802 {
803 	struct pipeline *p;
804 	char *pipeline_name, *selector_name;
805 	uint32_t group_id;
806 	int status;
807 
808 	if (n_tokens != 6) {
809 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
810 		return;
811 	}
812 
813 	pipeline_name = tokens[1];
814 	p = softnic_pipeline_find(softnic, pipeline_name);
815 	if (!p) {
816 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
817 		return;
818 	}
819 
820 	if (strcmp(tokens[2], "selector") != 0) {
821 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
822 		return;
823 	}
824 
825 	selector_name = tokens[3];
826 
827 	if (strcmp(tokens[4], "group") ||
828 		strcmp(tokens[5], "add")) {
829 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
830 		return;
831 	}
832 
833 	status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
834 		selector_name,
835 		&group_id);
836 	if (status)
837 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
838 	else
839 		snprintf(out, out_size, "Group ID: %u\n", group_id);
840 }
841 
842 /**
843  * pipeline <pipeline_name> selector <selector_name> group delete <group_id>
844  */
845 static void
846 cmd_softnic_pipeline_selector_group_delete(struct pmd_internals *softnic,
847 	char **tokens,
848 	uint32_t n_tokens,
849 	char *out,
850 	size_t out_size)
851 {
852 	struct pipeline *p;
853 	char *pipeline_name, *selector_name;
854 	uint32_t group_id;
855 	int status;
856 
857 	if (n_tokens != 7) {
858 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
859 		return;
860 	}
861 
862 	pipeline_name = tokens[1];
863 	p = softnic_pipeline_find(softnic, pipeline_name);
864 	if (!p) {
865 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
866 		return;
867 	}
868 
869 	if (strcmp(tokens[2], "selector") != 0) {
870 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
871 		return;
872 	}
873 
874 	selector_name = tokens[3];
875 
876 	if (strcmp(tokens[4], "group") ||
877 		strcmp(tokens[5], "delete")) {
878 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
879 		return;
880 	}
881 
882 	if (parser_read_uint32(&group_id, tokens[6]) != 0) {
883 		snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
884 		return;
885 	}
886 
887 	status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
888 		selector_name,
889 		group_id);
890 	if (status)
891 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
892 }
893 
894 #define GROUP_MEMBER_INFO_TOKENS_MAX 6
895 
896 static int
897 token_is_comment(const char *token)
898 {
899 	if ((token[0] == '#') ||
900 	    (token[0] == ';') ||
901 	    ((token[0] == '/') && (token[1] == '/')))
902 		return 1; /* TRUE. */
903 
904 	return 0; /* FALSE. */
905 }
906 
907 static int
908 pipeline_selector_group_member_read(const char *string,
909 				    uint32_t *group_id,
910 				    uint32_t *member_id,
911 				    uint32_t *weight,
912 				    int *is_blank_or_comment)
913 {
914 	char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
915 	char *s0 = NULL, *s;
916 	uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
917 	int blank_or_comment = 0;
918 
919 	/* Check input arguments. */
920 	if (!string || !string[0])
921 		goto error;
922 
923 	/* Memory allocation. */
924 	s0 = strdup(string);
925 	if (!s0)
926 		goto error;
927 
928 	/* Parse the string into tokens. */
929 	for (s = s0; ; ) {
930 		char *token;
931 
932 		token = strtok_r(s, " \f\n\r\t\v", &s);
933 		if (!token || token_is_comment(token))
934 			break;
935 
936 		if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
937 			goto error;
938 
939 		token_array[n_tokens] = token;
940 		n_tokens++;
941 	}
942 
943 	if (!n_tokens) {
944 		blank_or_comment = 1;
945 		goto error;
946 	}
947 
948 	tokens = token_array;
949 
950 	if (n_tokens < 4 ||
951 		strcmp(tokens[0], "group") ||
952 		strcmp(tokens[2], "member"))
953 		goto error;
954 
955 	/*
956 	 * Group ID.
957 	 */
958 	if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
959 		goto error;
960 	*group_id = group_id_val;
961 
962 	/*
963 	 * Member ID.
964 	 */
965 	if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
966 		goto error;
967 	*member_id = member_id_val;
968 
969 	tokens += 4;
970 	n_tokens -= 4;
971 
972 	/*
973 	 * Weight.
974 	 */
975 	if (n_tokens && !strcmp(tokens[0], "weight")) {
976 		if (n_tokens < 2)
977 			goto error;
978 
979 		if (parser_read_uint32(&weight_val, tokens[1]) != 0)
980 			goto error;
981 		*weight = weight_val;
982 
983 		tokens += 2;
984 		n_tokens -= 2;
985 	}
986 
987 	if (n_tokens)
988 		goto error;
989 
990 	free(s0);
991 	return 0;
992 
993 error:
994 	free(s0);
995 	if (is_blank_or_comment)
996 		*is_blank_or_comment = blank_or_comment;
997 	return -EINVAL;
998 }
999 
1000 static int
1001 pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
1002 				    const char *selector_name,
1003 				    FILE *file,
1004 				    uint32_t *file_line_number)
1005 {
1006 	char *line = NULL;
1007 	uint32_t line_id = 0;
1008 	int status = 0;
1009 
1010 	/* Buffer allocation. */
1011 	line = malloc(MAX_LINE_SIZE);
1012 	if (!line)
1013 		return -ENOMEM;
1014 
1015 	/* File read. */
1016 	for (line_id = 1; ; line_id++) {
1017 		uint32_t group_id, member_id, weight;
1018 		int is_blank_or_comment;
1019 
1020 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1021 			break;
1022 
1023 		status = pipeline_selector_group_member_read(line,
1024 							     &group_id,
1025 							     &member_id,
1026 							     &weight,
1027 							     &is_blank_or_comment);
1028 		if (status) {
1029 			if (is_blank_or_comment)
1030 				continue;
1031 
1032 			goto error;
1033 		}
1034 
1035 		status = rte_swx_ctl_pipeline_selector_group_member_add(p,
1036 			selector_name,
1037 			group_id,
1038 			member_id,
1039 			weight);
1040 		if (status)
1041 			goto error;
1042 	}
1043 
1044 error:
1045 	free(line);
1046 	*file_line_number = line_id;
1047 	return status;
1048 }
1049 
1050 /**
1051  * pipeline <pipeline_name> selector <selector_name> group member add <file_name>
1052  */
1053 static void
1054 cmd_softnic_pipeline_selector_group_member_add(struct pmd_internals *softnic,
1055 	char **tokens,
1056 	uint32_t n_tokens,
1057 	char *out,
1058 	size_t out_size)
1059 {
1060 	struct pipeline *p;
1061 	char *pipeline_name, *selector_name, *file_name;
1062 	FILE *file = NULL;
1063 	uint32_t file_line_number = 0;
1064 	int status;
1065 
1066 	if (n_tokens != 8) {
1067 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1068 		return;
1069 	}
1070 
1071 	pipeline_name = tokens[1];
1072 	p = softnic_pipeline_find(softnic, pipeline_name);
1073 	if (!p) {
1074 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1075 		return;
1076 	}
1077 
1078 	if (strcmp(tokens[2], "selector") != 0) {
1079 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1080 		return;
1081 	}
1082 
1083 	selector_name = tokens[3];
1084 
1085 	if (strcmp(tokens[4], "group") ||
1086 		strcmp(tokens[5], "member") ||
1087 		strcmp(tokens[6], "add")) {
1088 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
1089 		return;
1090 	}
1091 
1092 	file_name = tokens[7];
1093 	file = fopen(file_name, "r");
1094 	if (!file) {
1095 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1096 		return;
1097 	}
1098 
1099 	status = pipeline_selector_group_members_add(p->ctl,
1100 		selector_name,
1101 		file,
1102 		&file_line_number);
1103 	if (status)
1104 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1105 			 file_name,
1106 			 file_line_number);
1107 
1108 	fclose(file);
1109 }
1110 
1111 static int
1112 pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
1113 				       const char *selector_name,
1114 				       FILE *file,
1115 				       uint32_t *file_line_number)
1116 {
1117 	char *line = NULL;
1118 	uint32_t line_id = 0;
1119 	int status = 0;
1120 
1121 	/* Buffer allocation. */
1122 	line = malloc(MAX_LINE_SIZE);
1123 	if (!line)
1124 		return -ENOMEM;
1125 
1126 	/* File read. */
1127 	for (line_id = 1; ; line_id++) {
1128 		uint32_t group_id, member_id, weight;
1129 		int is_blank_or_comment;
1130 
1131 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1132 			break;
1133 
1134 		status = pipeline_selector_group_member_read(line,
1135 							     &group_id,
1136 							     &member_id,
1137 							     &weight,
1138 							     &is_blank_or_comment);
1139 		if (status) {
1140 			if (is_blank_or_comment)
1141 				continue;
1142 
1143 			goto error;
1144 		}
1145 
1146 		status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
1147 			selector_name,
1148 			group_id,
1149 			member_id);
1150 		if (status)
1151 			goto error;
1152 	}
1153 
1154 error:
1155 	free(line);
1156 	*file_line_number = line_id;
1157 	return status;
1158 }
1159 
1160 /**
1161  * pipeline <pipeline_name> selector <selector_name> group member delete <file_name>
1162  */
1163 static void
1164 cmd_softnic_pipeline_selector_group_member_delete(struct pmd_internals *softnic,
1165 	char **tokens,
1166 	uint32_t n_tokens,
1167 	char *out,
1168 	size_t out_size)
1169 {
1170 	struct pipeline *p;
1171 	char *pipeline_name, *selector_name, *file_name;
1172 	FILE *file = NULL;
1173 	uint32_t file_line_number = 0;
1174 	int status;
1175 
1176 	if (n_tokens != 8) {
1177 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1178 		return;
1179 	}
1180 
1181 	pipeline_name = tokens[1];
1182 	p = softnic_pipeline_find(softnic, pipeline_name);
1183 	if (!p) {
1184 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1185 		return;
1186 	}
1187 
1188 	if (strcmp(tokens[2], "selector") != 0) {
1189 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1190 		return;
1191 	}
1192 
1193 	selector_name = tokens[3];
1194 
1195 	if (strcmp(tokens[4], "group") ||
1196 		strcmp(tokens[5], "member") ||
1197 		strcmp(tokens[6], "delete")) {
1198 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
1199 		return;
1200 	}
1201 
1202 	file_name = tokens[7];
1203 	file = fopen(file_name, "r");
1204 	if (!file) {
1205 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1206 		return;
1207 	}
1208 
1209 	status = pipeline_selector_group_members_delete(p->ctl,
1210 							selector_name,
1211 							file,
1212 							&file_line_number);
1213 	if (status)
1214 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1215 			 file_name,
1216 			 file_line_number);
1217 
1218 	fclose(file);
1219 }
1220 
1221 /**
1222  * pipeline <pipeline_name> selector <selector_name> show [filename]
1223  */
1224 static void
1225 cmd_softnic_pipeline_selector_show(struct pmd_internals *softnic,
1226 	char **tokens,
1227 	uint32_t n_tokens,
1228 	char *out,
1229 	size_t out_size)
1230 {
1231 	struct pipeline *p;
1232 	char *pipeline_name, *selector_name;
1233 	FILE *file = NULL;
1234 	int status;
1235 
1236 	if (n_tokens != 5 && n_tokens != 6) {
1237 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1238 		return;
1239 	}
1240 
1241 	pipeline_name = tokens[1];
1242 	p = softnic_pipeline_find(softnic, pipeline_name);
1243 	if (!p) {
1244 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1245 		return;
1246 	}
1247 
1248 	selector_name = tokens[3];
1249 
1250 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1251 	if (!file) {
1252 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1253 		return;
1254 	}
1255 
1256 	status = rte_swx_ctl_pipeline_selector_fprintf(file, p->ctl, selector_name);
1257 	if (status)
1258 		snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
1259 
1260 	if (file)
1261 		fclose(file);
1262 }
1263 
1264 static int
1265 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1266 				   const char *learner_name,
1267 				   FILE *file,
1268 				   uint32_t *file_line_number)
1269 {
1270 	char *line = NULL;
1271 	uint32_t line_id = 0;
1272 	int status = 0;
1273 
1274 	/* Buffer allocation. */
1275 	line = malloc(MAX_LINE_SIZE);
1276 	if (!line)
1277 		return -ENOMEM;
1278 
1279 	/* File read. */
1280 	for (line_id = 1; ; line_id++) {
1281 		struct rte_swx_table_entry *entry;
1282 		int is_blank_or_comment;
1283 
1284 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1285 			break;
1286 
1287 		entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1288 									learner_name,
1289 									line,
1290 									&is_blank_or_comment);
1291 		if (!entry) {
1292 			if (is_blank_or_comment)
1293 				continue;
1294 
1295 			status = -EINVAL;
1296 			goto error;
1297 		}
1298 
1299 		status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1300 									learner_name,
1301 									entry);
1302 		table_entry_free(entry);
1303 		if (status)
1304 			goto error;
1305 	}
1306 
1307 error:
1308 	*file_line_number = line_id;
1309 	free(line);
1310 	return status;
1311 }
1312 
1313 /**
1314  * pipeline <pipeline_name> learner <learner_name> default <file_name>
1315  */
1316 static void
1317 cmd_softnic_pipeline_learner_default(struct pmd_internals *softnic,
1318 	char **tokens,
1319 	uint32_t n_tokens,
1320 	char *out,
1321 	size_t out_size)
1322 {
1323 	struct pipeline *p;
1324 	char *pipeline_name, *learner_name, *file_name;
1325 	FILE *file = NULL;
1326 	uint32_t file_line_number = 0;
1327 	int status;
1328 
1329 	if (n_tokens != 6) {
1330 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1331 		return;
1332 	}
1333 
1334 	pipeline_name = tokens[1];
1335 	p = softnic_pipeline_find(softnic, pipeline_name);
1336 	if (!p) {
1337 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1338 		return;
1339 	}
1340 
1341 	learner_name = tokens[3];
1342 
1343 	file_name = tokens[5];
1344 	file = fopen(file_name, "r");
1345 	if (!file) {
1346 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1347 		return;
1348 	}
1349 
1350 	status = pipeline_learner_default_entry_add(p->ctl,
1351 						    learner_name,
1352 						    file,
1353 						    &file_line_number);
1354 	if (status)
1355 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1356 			 file_name,
1357 			 file_line_number);
1358 
1359 	fclose(file);
1360 }
1361 
1362 /**
1363  * pipeline <pipeline_name> commit
1364  */
1365 static void
1366 cmd_softnic_pipeline_commit(struct pmd_internals *softnic,
1367 	char **tokens,
1368 	uint32_t n_tokens,
1369 	char *out,
1370 	size_t out_size)
1371 {
1372 	struct pipeline *p;
1373 	char *pipeline_name;
1374 	int status;
1375 
1376 	if (n_tokens != 3) {
1377 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1378 		return;
1379 	}
1380 
1381 	pipeline_name = tokens[1];
1382 	p = softnic_pipeline_find(softnic, pipeline_name);
1383 	if (!p) {
1384 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1385 		return;
1386 	}
1387 
1388 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1389 	if (status)
1390 		snprintf(out, out_size, "Commit failed. "
1391 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1392 }
1393 
1394 /**
1395  * pipeline <pipeline_name> abort
1396  */
1397 static void
1398 cmd_softnic_pipeline_abort(struct pmd_internals *softnic,
1399 	char **tokens,
1400 	uint32_t n_tokens,
1401 	char *out,
1402 	size_t out_size)
1403 {
1404 	struct pipeline *p;
1405 	char *pipeline_name;
1406 
1407 	if (n_tokens != 3) {
1408 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1409 		return;
1410 	}
1411 
1412 	pipeline_name = tokens[1];
1413 	p = softnic_pipeline_find(softnic, pipeline_name);
1414 	if (!p) {
1415 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1416 		return;
1417 	}
1418 
1419 	rte_swx_ctl_pipeline_abort(p->ctl);
1420 }
1421 
1422 /**
1423  * pipeline <pipeline_name> regrd <register_array_name> <index>
1424  */
1425 static void
1426 cmd_softnic_pipeline_regrd(struct pmd_internals *softnic,
1427 	char **tokens,
1428 	uint32_t n_tokens,
1429 	char *out,
1430 	size_t out_size)
1431 {
1432 	struct pipeline *p;
1433 	const char *pipeline_name, *name;
1434 	uint64_t value;
1435 	uint32_t idx;
1436 	int status;
1437 
1438 	if (n_tokens != 5) {
1439 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1440 		return;
1441 	}
1442 
1443 	pipeline_name = tokens[1];
1444 	p = softnic_pipeline_find(softnic, pipeline_name);
1445 	if (!p) {
1446 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1447 		return;
1448 	}
1449 
1450 	if (strcmp(tokens[2], "regrd")) {
1451 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
1452 		return;
1453 	}
1454 
1455 	name = tokens[3];
1456 
1457 	if (parser_read_uint32(&idx, tokens[4])) {
1458 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
1459 		return;
1460 	}
1461 
1462 	status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
1463 	if (status) {
1464 		snprintf(out, out_size, "Command failed.\n");
1465 		return;
1466 	}
1467 
1468 	snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1469 }
1470 
1471 /**
1472  * pipeline <pipeline_name> regwr <register_array_name> <index> <value>
1473  */
1474 static void
1475 cmd_softnic_pipeline_regwr(struct pmd_internals *softnic,
1476 	char **tokens,
1477 	uint32_t n_tokens,
1478 	char *out,
1479 	size_t out_size)
1480 {
1481 	struct pipeline *p;
1482 	const char *pipeline_name, *name;
1483 	uint64_t value;
1484 	uint32_t idx;
1485 	int status;
1486 
1487 	if (n_tokens != 6) {
1488 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1489 		return;
1490 	}
1491 
1492 	pipeline_name = tokens[1];
1493 	p = softnic_pipeline_find(softnic, pipeline_name);
1494 	if (!p) {
1495 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1496 		return;
1497 	}
1498 
1499 	if (strcmp(tokens[2], "regwr")) {
1500 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
1501 		return;
1502 	}
1503 
1504 	name = tokens[3];
1505 
1506 	if (parser_read_uint32(&idx, tokens[4])) {
1507 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
1508 		return;
1509 	}
1510 
1511 	if (parser_read_uint64(&value, tokens[5])) {
1512 		snprintf(out, out_size, MSG_ARG_INVALID, "value");
1513 		return;
1514 	}
1515 
1516 	status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
1517 	if (status) {
1518 		snprintf(out, out_size, "Command failed.\n");
1519 		return;
1520 	}
1521 }
1522 
1523 /**
1524  * pipeline <pipeline_name> meter profile <profile_name> add cir <cir> pir <pir> cbs <cbs> pbs <pbs>
1525  */
1526 static void
1527 cmd_softnic_pipeline_meter_profile_add(struct pmd_internals *softnic,
1528 	char **tokens,
1529 	uint32_t n_tokens,
1530 	char *out,
1531 	size_t out_size)
1532 {
1533 	struct rte_meter_trtcm_params params;
1534 	struct pipeline *p;
1535 	const char *profile_name;
1536 	int status;
1537 
1538 	if (n_tokens != 14) {
1539 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1540 		return;
1541 	}
1542 
1543 	p = softnic_pipeline_find(softnic, tokens[1]);
1544 	if (!p) {
1545 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1546 		return;
1547 	}
1548 
1549 	if (strcmp(tokens[2], "meter")) {
1550 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1551 		return;
1552 	}
1553 
1554 	if (strcmp(tokens[3], "profile")) {
1555 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1556 		return;
1557 	}
1558 
1559 	profile_name = tokens[4];
1560 
1561 	if (strcmp(tokens[5], "add")) {
1562 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
1563 		return;
1564 	}
1565 
1566 	if (strcmp(tokens[6], "cir")) {
1567 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
1568 		return;
1569 	}
1570 
1571 	if (parser_read_uint64(&params.cir, tokens[7])) {
1572 		snprintf(out, out_size, MSG_ARG_INVALID, "cir");
1573 		return;
1574 	}
1575 
1576 	if (strcmp(tokens[8], "pir")) {
1577 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
1578 		return;
1579 	}
1580 
1581 	if (parser_read_uint64(&params.pir, tokens[9])) {
1582 		snprintf(out, out_size, MSG_ARG_INVALID, "pir");
1583 		return;
1584 	}
1585 
1586 	if (strcmp(tokens[10], "cbs")) {
1587 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
1588 		return;
1589 	}
1590 
1591 	if (parser_read_uint64(&params.cbs, tokens[11])) {
1592 		snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
1593 		return;
1594 	}
1595 
1596 	if (strcmp(tokens[12], "pbs")) {
1597 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
1598 		return;
1599 	}
1600 
1601 	if (parser_read_uint64(&params.pbs, tokens[13])) {
1602 		snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
1603 		return;
1604 	}
1605 
1606 	status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
1607 	if (status) {
1608 		snprintf(out, out_size, "Command failed.\n");
1609 		return;
1610 	}
1611 }
1612 
1613 /**
1614  * pipeline <pipeline_name> meter profile <profile_name> delete
1615  */
1616 static void
1617 cmd_softnic_pipeline_meter_profile_delete(struct pmd_internals *softnic,
1618 	char **tokens,
1619 	uint32_t n_tokens,
1620 	char *out,
1621 	size_t out_size)
1622 {
1623 	struct pipeline *p;
1624 	const char *profile_name;
1625 	int status;
1626 
1627 	if (n_tokens != 6) {
1628 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1629 		return;
1630 	}
1631 
1632 	p = softnic_pipeline_find(softnic, tokens[1]);
1633 	if (!p) {
1634 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1635 		return;
1636 	}
1637 
1638 	if (strcmp(tokens[2], "meter")) {
1639 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1640 		return;
1641 	}
1642 
1643 	if (strcmp(tokens[3], "profile")) {
1644 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1645 		return;
1646 	}
1647 
1648 	profile_name = tokens[4];
1649 
1650 	if (strcmp(tokens[5], "delete")) {
1651 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
1652 		return;
1653 	}
1654 
1655 	status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
1656 	if (status) {
1657 		snprintf(out, out_size, "Command failed.\n");
1658 		return;
1659 	}
1660 }
1661 
1662 /**
1663  * pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> reset
1664  */
1665 static void
1666 cmd_softnic_pipeline_meter_reset(struct pmd_internals *softnic,
1667 	char **tokens,
1668 	uint32_t n_tokens,
1669 	char *out,
1670 	size_t out_size)
1671 {
1672 	struct pipeline *p;
1673 	const char *name;
1674 	uint32_t idx0 = 0, idx1 = 0;
1675 
1676 	if (n_tokens != 9) {
1677 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1678 		return;
1679 	}
1680 
1681 	p = softnic_pipeline_find(softnic, tokens[1]);
1682 	if (!p) {
1683 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1684 		return;
1685 	}
1686 
1687 	if (strcmp(tokens[2], "meter")) {
1688 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1689 		return;
1690 	}
1691 
1692 	name = tokens[3];
1693 
1694 	if (strcmp(tokens[4], "from")) {
1695 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
1696 		return;
1697 	}
1698 
1699 	if (parser_read_uint32(&idx0, tokens[5])) {
1700 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
1701 		return;
1702 	}
1703 
1704 	if (strcmp(tokens[6], "to")) {
1705 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
1706 		return;
1707 	}
1708 
1709 	if (parser_read_uint32(&idx1, tokens[7]) || idx1 < idx0) {
1710 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
1711 		return;
1712 	}
1713 
1714 	if (strcmp(tokens[8], "reset")) {
1715 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
1716 		return;
1717 	}
1718 
1719 	for ( ; idx0 <= idx1; idx0++) {
1720 		int status;
1721 
1722 		status = rte_swx_ctl_meter_reset(p->p, name, idx0);
1723 		if (status) {
1724 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
1725 			return;
1726 		}
1727 	}
1728 }
1729 
1730 /**
1731  * pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> set
1732  *	profile <profile_name>
1733  */
1734 static void
1735 cmd_softnic_pipeline_meter_set(struct pmd_internals *softnic,
1736 	char **tokens,
1737 	uint32_t n_tokens,
1738 	char *out,
1739 	size_t out_size)
1740 {
1741 	struct pipeline *p;
1742 	const char *name, *profile_name;
1743 	uint32_t idx0 = 0, idx1 = 0;
1744 
1745 	if (n_tokens != 11) {
1746 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1747 		return;
1748 	}
1749 
1750 	p = softnic_pipeline_find(softnic, tokens[1]);
1751 	if (!p) {
1752 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1753 		return;
1754 	}
1755 
1756 	if (strcmp(tokens[2], "meter")) {
1757 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1758 		return;
1759 	}
1760 
1761 	name = tokens[3];
1762 
1763 	if (strcmp(tokens[4], "from")) {
1764 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
1765 		return;
1766 	}
1767 
1768 	if (parser_read_uint32(&idx0, tokens[5])) {
1769 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
1770 		return;
1771 	}
1772 
1773 	if (strcmp(tokens[6], "to")) {
1774 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
1775 		return;
1776 	}
1777 
1778 	if (parser_read_uint32(&idx1, tokens[7]) || idx1 < idx0) {
1779 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
1780 		return;
1781 	}
1782 
1783 	if (strcmp(tokens[8], "set")) {
1784 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
1785 		return;
1786 	}
1787 
1788 	if (strcmp(tokens[9], "profile")) {
1789 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1790 		return;
1791 	}
1792 
1793 	profile_name = tokens[10];
1794 
1795 	for ( ; idx0 <= idx1; idx0++) {
1796 		int status;
1797 
1798 		status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
1799 		if (status) {
1800 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
1801 			return;
1802 		}
1803 	}
1804 }
1805 
1806 /**
1807  * pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> stats
1808  */
1809 static void
1810 cmd_softnic_pipeline_meter_stats(struct pmd_internals *softnic,
1811 	char **tokens,
1812 	uint32_t n_tokens,
1813 	char *out,
1814 	size_t out_size)
1815 {
1816 	struct rte_swx_ctl_meter_stats stats;
1817 	struct pipeline *p;
1818 	const char *name;
1819 	uint32_t idx0 = 0, idx1 = 0;
1820 
1821 	if (n_tokens != 9) {
1822 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1823 		return;
1824 	}
1825 
1826 	p = softnic_pipeline_find(softnic, tokens[1]);
1827 	if (!p) {
1828 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1829 		return;
1830 	}
1831 
1832 	if (strcmp(tokens[2], "meter")) {
1833 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1834 		return;
1835 	}
1836 
1837 	name = tokens[3];
1838 
1839 	if (strcmp(tokens[4], "from")) {
1840 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
1841 		return;
1842 	}
1843 
1844 	if (parser_read_uint32(&idx0, tokens[5])) {
1845 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
1846 		return;
1847 	}
1848 
1849 	if (strcmp(tokens[6], "to")) {
1850 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
1851 		return;
1852 	}
1853 
1854 	if (parser_read_uint32(&idx1, tokens[7]) || idx1 < idx0) {
1855 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
1856 		return;
1857 	}
1858 
1859 	if (strcmp(tokens[8], "stats")) {
1860 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1861 		return;
1862 	}
1863 
1864 	/* Table header. */
1865 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
1866 		 "-------",
1867 		 "----------------", "----------------", "----------------",
1868 		 "----------------", "----------------", "----------------");
1869 	out_size -= strlen(out);
1870 	out += strlen(out);
1871 
1872 	snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
1873 		 "METER #",
1874 		 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
1875 		 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
1876 	out_size -= strlen(out);
1877 	out += strlen(out);
1878 
1879 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
1880 		 "-------",
1881 		 "----------------", "----------------", "----------------",
1882 		 "----------------", "----------------", "----------------");
1883 	out_size -= strlen(out);
1884 	out += strlen(out);
1885 
1886 	/* Table rows. */
1887 	for ( ; idx0 <= idx1; idx0++) {
1888 		int status;
1889 
1890 		status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
1891 		if (status) {
1892 			snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
1893 			out_size -= strlen(out);
1894 			out += strlen(out);
1895 			return;
1896 		}
1897 
1898 		snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
1899 			 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
1900 			 idx0,
1901 			 stats.n_pkts[RTE_COLOR_GREEN],
1902 			 stats.n_pkts[RTE_COLOR_YELLOW],
1903 			 stats.n_pkts[RTE_COLOR_RED],
1904 			 stats.n_bytes[RTE_COLOR_GREEN],
1905 			 stats.n_bytes[RTE_COLOR_YELLOW],
1906 			 stats.n_bytes[RTE_COLOR_RED]);
1907 		out_size -= strlen(out);
1908 		out += strlen(out);
1909 	}
1910 }
1911 
1912 /**
1913  * pipeline <pipeline_name> stats
1914  */
1915 static void
1916 cmd_softnic_pipeline_stats(struct pmd_internals *softnic,
1917 	char **tokens,
1918 	uint32_t n_tokens,
1919 	char *out,
1920 	size_t out_size)
1921 {
1922 	struct rte_swx_ctl_pipeline_info info;
1923 	struct pipeline *p;
1924 	uint32_t i;
1925 	int status;
1926 
1927 	if (n_tokens != 3) {
1928 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1929 		return;
1930 	}
1931 
1932 	p = softnic_pipeline_find(softnic, tokens[1]);
1933 	if (!p) {
1934 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1935 		return;
1936 	}
1937 
1938 	if (strcmp(tokens[2], "stats")) {
1939 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1940 		return;
1941 	}
1942 
1943 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1944 	if (status) {
1945 		snprintf(out, out_size, "Pipeline info get error.");
1946 		return;
1947 	}
1948 
1949 	snprintf(out, out_size, "Input ports:\n");
1950 	out_size -= strlen(out);
1951 	out += strlen(out);
1952 
1953 	for (i = 0; i < info.n_ports_in; i++) {
1954 		struct rte_swx_port_in_stats stats;
1955 
1956 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1957 
1958 		snprintf(out, out_size, "\tPort %u:"
1959 			" packets %" PRIu64
1960 			" bytes %" PRIu64
1961 			" empty %" PRIu64 "\n",
1962 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1963 		out_size -= strlen(out);
1964 		out += strlen(out);
1965 	}
1966 
1967 	snprintf(out, out_size, "\nOutput ports:\n");
1968 	out_size -= strlen(out);
1969 	out += strlen(out);
1970 
1971 	for (i = 0; i < info.n_ports_out; i++) {
1972 		struct rte_swx_port_out_stats stats;
1973 
1974 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1975 
1976 		if (i != info.n_ports_out - 1)
1977 			snprintf(out, out_size, "\tPort %u:", i);
1978 		else
1979 			snprintf(out, out_size, "\tDROP:");
1980 
1981 		out_size -= strlen(out);
1982 		out += strlen(out);
1983 
1984 		snprintf(out,
1985 			out_size,
1986 			" packets %" PRIu64
1987 			" bytes %" PRIu64
1988 			" packets dropped %" PRIu64
1989 			" bytes dropped %" PRIu64
1990 			" clone %" PRIu64
1991 			" clonerr %" PRIu64 "\n",
1992 			stats.n_pkts,
1993 			stats.n_bytes,
1994 			stats.n_pkts_drop,
1995 			stats.n_bytes_drop,
1996 			stats.n_pkts_clone,
1997 			stats.n_pkts_clone_err);
1998 
1999 		out_size -= strlen(out);
2000 		out += strlen(out);
2001 	}
2002 
2003 	snprintf(out, out_size, "\nTables:\n");
2004 	out_size -= strlen(out);
2005 	out += strlen(out);
2006 
2007 	for (i = 0; i < info.n_tables; i++) {
2008 		struct rte_swx_ctl_table_info table_info;
2009 		uint64_t n_pkts_action[info.n_actions];
2010 		struct rte_swx_table_stats stats = {
2011 			.n_pkts_hit = 0,
2012 			.n_pkts_miss = 0,
2013 			.n_pkts_action = n_pkts_action,
2014 		};
2015 		uint32_t j;
2016 
2017 		status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
2018 		if (status) {
2019 			snprintf(out, out_size, "Table info get error.");
2020 			return;
2021 		}
2022 
2023 		status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
2024 		if (status) {
2025 			snprintf(out, out_size, "Table stats read error.");
2026 			return;
2027 		}
2028 
2029 		snprintf(out, out_size, "\tTable %s:\n"
2030 			"\t\tHit (packets): %" PRIu64 "\n"
2031 			"\t\tMiss (packets): %" PRIu64 "\n",
2032 			table_info.name,
2033 			stats.n_pkts_hit,
2034 			stats.n_pkts_miss);
2035 		out_size -= strlen(out);
2036 		out += strlen(out);
2037 
2038 		for (j = 0; j < info.n_actions; j++) {
2039 			struct rte_swx_ctl_action_info action_info;
2040 
2041 			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2042 			if (status) {
2043 				snprintf(out, out_size, "Action info get error.");
2044 				return;
2045 			}
2046 
2047 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2048 				action_info.name,
2049 				stats.n_pkts_action[j]);
2050 			out_size -= strlen(out);
2051 			out += strlen(out);
2052 		}
2053 	}
2054 
2055 	snprintf(out, out_size, "\nLearner tables:\n");
2056 	out_size -= strlen(out);
2057 	out += strlen(out);
2058 
2059 	for (i = 0; i < info.n_learners; i++) {
2060 		struct rte_swx_ctl_learner_info learner_info;
2061 		uint64_t n_pkts_action[info.n_actions];
2062 		struct rte_swx_learner_stats stats = {
2063 			.n_pkts_hit = 0,
2064 			.n_pkts_miss = 0,
2065 			.n_pkts_action = n_pkts_action,
2066 		};
2067 		uint32_t j;
2068 
2069 		status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
2070 		if (status) {
2071 			snprintf(out, out_size, "Learner table info get error.");
2072 			return;
2073 		}
2074 
2075 		status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
2076 		if (status) {
2077 			snprintf(out, out_size, "Learner table stats read error.");
2078 			return;
2079 		}
2080 
2081 		snprintf(out, out_size, "\tLearner table %s:\n"
2082 			"\t\tHit (packets): %" PRIu64 "\n"
2083 			"\t\tMiss (packets): %" PRIu64 "\n"
2084 			"\t\tLearn OK (packets): %" PRIu64 "\n"
2085 			"\t\tLearn error (packets): %" PRIu64 "\n"
2086 			"\t\tRearm (packets): %" PRIu64 "\n"
2087 			"\t\tForget (packets): %" PRIu64 "\n",
2088 			learner_info.name,
2089 			stats.n_pkts_hit,
2090 			stats.n_pkts_miss,
2091 			stats.n_pkts_learn_ok,
2092 			stats.n_pkts_learn_err,
2093 			stats.n_pkts_rearm,
2094 			stats.n_pkts_forget);
2095 		out_size -= strlen(out);
2096 		out += strlen(out);
2097 
2098 		for (j = 0; j < info.n_actions; j++) {
2099 			struct rte_swx_ctl_action_info action_info;
2100 
2101 			status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2102 			if (status) {
2103 				snprintf(out, out_size, "Action info get error.");
2104 				return;
2105 			}
2106 
2107 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2108 				action_info.name,
2109 				stats.n_pkts_action[j]);
2110 			out_size -= strlen(out);
2111 			out += strlen(out);
2112 		}
2113 	}
2114 }
2115 
2116 /**
2117  * pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow
2118  *	truncate <truncation_length>
2119  */
2120 static void
2121 cmd_softnic_pipeline_mirror_session(struct pmd_internals *softnic,
2122 	char **tokens,
2123 	uint32_t n_tokens,
2124 	char *out,
2125 	size_t out_size)
2126 {
2127 	struct rte_swx_pipeline_mirroring_session_params params;
2128 	struct pipeline *p;
2129 	uint32_t session_id = 0;
2130 	int status;
2131 
2132 	if (n_tokens != 11) {
2133 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2134 		return;
2135 	}
2136 
2137 	if (strcmp(tokens[0], "pipeline")) {
2138 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2139 		return;
2140 	}
2141 
2142 	p = softnic_pipeline_find(softnic, tokens[1]);
2143 	if (!p) {
2144 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2145 		return;
2146 	}
2147 
2148 	if (strcmp(tokens[2], "mirror")) {
2149 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2150 		return;
2151 	}
2152 
2153 	if (strcmp(tokens[3], "session")) {
2154 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "session");
2155 		return;
2156 	}
2157 
2158 	if (parser_read_uint32(&session_id, tokens[4])) {
2159 		snprintf(out, out_size, MSG_ARG_INVALID, "session_id");
2160 		return;
2161 	}
2162 
2163 	if (strcmp(tokens[5], "port")) {
2164 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2165 		return;
2166 	}
2167 
2168 	if (parser_read_uint32(&params.port_id, tokens[6])) {
2169 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2170 		return;
2171 	}
2172 
2173 	if (strcmp(tokens[7], "clone")) {
2174 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "clone");
2175 		return;
2176 	}
2177 
2178 	if (!strcmp(tokens[8], "fast")) {
2179 		params.fast_clone = 1;
2180 	} else if (!strcmp(tokens[8], "slow")) {
2181 		params.fast_clone = 0;
2182 	} else {
2183 		snprintf(out, out_size, MSG_ARG_INVALID, "clone");
2184 		return;
2185 	}
2186 
2187 	if (strcmp(tokens[9], "truncate")) {
2188 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "truncate");
2189 		return;
2190 	}
2191 
2192 	if (parser_read_uint32(&params.truncation_length, tokens[10])) {
2193 		snprintf(out, out_size, MSG_ARG_INVALID, "truncation_length");
2194 		return;
2195 	}
2196 
2197 	status = rte_swx_ctl_pipeline_mirroring_session_set(p->p, session_id, &params);
2198 	if (status) {
2199 		snprintf(out, out_size, "Command failed!\n");
2200 		return;
2201 	}
2202 }
2203 
2204 /**
2205  * thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]
2206  */
2207 static void
2208 cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic,
2209 	char **tokens,
2210 	uint32_t n_tokens,
2211 	char *out,
2212 	size_t out_size)
2213 {
2214 	char *pipeline_name;
2215 	struct pipeline *p;
2216 	uint32_t thread_id;
2217 	int status;
2218 
2219 	if (n_tokens != 5) {
2220 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2221 		return;
2222 	}
2223 
2224 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2225 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2226 		return;
2227 	}
2228 
2229 	if (strcmp(tokens[2], "pipeline") != 0) {
2230 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2231 		return;
2232 	}
2233 
2234 	pipeline_name = tokens[3];
2235 	p = softnic_pipeline_find(softnic, pipeline_name);
2236 	if (!p) {
2237 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2238 		return;
2239 	}
2240 
2241 	if (strcmp(tokens[4], "enable") != 0) {
2242 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2243 		return;
2244 	}
2245 
2246 	status = softnic_thread_pipeline_enable(softnic, thread_id, p);
2247 	if (status) {
2248 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
2249 		return;
2250 	}
2251 }
2252 
2253 /**
2254  * thread <thread_id> pipeline <pipeline_name> disable
2255  */
2256 static void
2257 cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic,
2258 	char **tokens,
2259 	uint32_t n_tokens,
2260 	char *out,
2261 	size_t out_size)
2262 {
2263 	char *pipeline_name;
2264 	struct pipeline *p;
2265 	uint32_t thread_id;
2266 	int status;
2267 
2268 	if (n_tokens != 5) {
2269 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2270 		return;
2271 	}
2272 
2273 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2274 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2275 		return;
2276 	}
2277 
2278 	if (strcmp(tokens[2], "pipeline") != 0) {
2279 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2280 		return;
2281 	}
2282 
2283 	pipeline_name = tokens[3];
2284 	p = softnic_pipeline_find(softnic, pipeline_name);
2285 	if (!p) {
2286 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2287 		return;
2288 	}
2289 
2290 	if (strcmp(tokens[4], "disable") != 0) {
2291 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2292 		return;
2293 	}
2294 
2295 	status = softnic_thread_pipeline_disable(softnic, thread_id, p);
2296 	if (status) {
2297 		snprintf(out, out_size, MSG_CMD_FAIL,
2298 			"thread pipeline disable");
2299 		return;
2300 	}
2301 }
2302 
2303 void
2304 softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
2305 {
2306 	char *tokens[CMD_MAX_TOKENS];
2307 	uint32_t n_tokens = RTE_DIM(tokens);
2308 	struct pmd_internals *softnic = arg;
2309 	int status;
2310 
2311 	if (is_comment(in))
2312 		return;
2313 
2314 	status = parse_tokenize_string(in, tokens, &n_tokens);
2315 	if (status) {
2316 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
2317 		return;
2318 	}
2319 
2320 	if (n_tokens == 0)
2321 		return;
2322 
2323 	if (strcmp(tokens[0], "mempool") == 0) {
2324 		cmd_mempool(softnic, tokens, n_tokens, out, out_size);
2325 		return;
2326 	}
2327 
2328 	if (strcmp(tokens[0], "swq") == 0) {
2329 		cmd_swq(softnic, tokens, n_tokens, out, out_size);
2330 		return;
2331 	}
2332 
2333 	if (!strcmp(tokens[0], "pipeline")) {
2334 		if (n_tokens >= 2 && !strcmp(tokens[1], "codegen")) {
2335 			cmd_softnic_pipeline_codegen(softnic, tokens, n_tokens, out, out_size);
2336 			return;
2337 		}
2338 
2339 		if (n_tokens >= 3 && !strcmp(tokens[1], "libbuild")) {
2340 			cmd_softnic_pipeline_libbuild(softnic, tokens, n_tokens, out, out_size);
2341 			return;
2342 		}
2343 
2344 		if (n_tokens >= 3 && !strcmp(tokens[2], "build")) {
2345 			cmd_softnic_pipeline_build(softnic, tokens, n_tokens, out, out_size);
2346 			return;
2347 		}
2348 
2349 		if (n_tokens >= 5 && !strcmp(tokens[2], "table") && !strcmp(tokens[4], "add")) {
2350 			cmd_softnic_pipeline_table_add(softnic, tokens, n_tokens, out, out_size);
2351 			return;
2352 		}
2353 
2354 		if (n_tokens >= 5 && !strcmp(tokens[2], "table") && !strcmp(tokens[4], "delete")) {
2355 			cmd_softnic_pipeline_table_delete(softnic, tokens, n_tokens,
2356 				out, out_size);
2357 			return;
2358 		}
2359 
2360 		if (n_tokens >= 5 && !strcmp(tokens[2], "table") && !strcmp(tokens[4], "default")) {
2361 			cmd_softnic_pipeline_table_default(softnic, tokens, n_tokens,
2362 				out, out_size);
2363 			return;
2364 		}
2365 
2366 		if (n_tokens >= 5 && !strcmp(tokens[2], "table") && !strcmp(tokens[4], "show")) {
2367 			cmd_softnic_pipeline_table_show(softnic, tokens, n_tokens, out, out_size);
2368 			return;
2369 		}
2370 
2371 		if (n_tokens >= 6 &&
2372 			!strcmp(tokens[2], "selector") &&
2373 			!strcmp(tokens[4], "group") &&
2374 			!strcmp(tokens[5], "add")) {
2375 			cmd_softnic_pipeline_selector_group_add(softnic, tokens, n_tokens,
2376 				out, out_size);
2377 			return;
2378 		}
2379 
2380 		if (n_tokens >= 6 &&
2381 			!strcmp(tokens[2], "selector") &&
2382 			!strcmp(tokens[4], "group") &&
2383 			!strcmp(tokens[5], "delete")) {
2384 			cmd_softnic_pipeline_selector_group_delete(softnic, tokens, n_tokens,
2385 				out, out_size);
2386 			return;
2387 		}
2388 
2389 		if (n_tokens >= 7 &&
2390 			!strcmp(tokens[2], "selector") &&
2391 			!strcmp(tokens[4], "group") &&
2392 			!strcmp(tokens[5], "member") &&
2393 			!strcmp(tokens[6], "add")) {
2394 			cmd_softnic_pipeline_selector_group_member_add(softnic, tokens, n_tokens,
2395 				out, out_size);
2396 			return;
2397 		}
2398 
2399 		if (n_tokens >= 7 &&
2400 			!strcmp(tokens[2], "selector") &&
2401 			!strcmp(tokens[4], "group") &&
2402 			!strcmp(tokens[5], "member") &&
2403 			!strcmp(tokens[6], "delete")) {
2404 			cmd_softnic_pipeline_selector_group_member_delete(softnic, tokens, n_tokens,
2405 				out, out_size);
2406 			return;
2407 		}
2408 
2409 		if (n_tokens >= 5 &&
2410 			!strcmp(tokens[2], "selector") &&
2411 			!strcmp(tokens[4], "show")) {
2412 			cmd_softnic_pipeline_selector_show(softnic, tokens, n_tokens,
2413 				out, out_size);
2414 			return;
2415 		}
2416 
2417 		if (n_tokens >= 5 &&
2418 			!strcmp(tokens[2], "learner") &&
2419 			!strcmp(tokens[4], "default")) {
2420 			cmd_softnic_pipeline_learner_default(softnic, tokens, n_tokens,
2421 				out, out_size);
2422 			return;
2423 		}
2424 
2425 		if (n_tokens >= 3 && !strcmp(tokens[2], "commit")) {
2426 			cmd_softnic_pipeline_commit(softnic, tokens, n_tokens, out, out_size);
2427 			return;
2428 		}
2429 
2430 		if (n_tokens >= 3 && !strcmp(tokens[2], "abort")) {
2431 			cmd_softnic_pipeline_abort(softnic, tokens, n_tokens, out, out_size);
2432 			return;
2433 		}
2434 
2435 		if (n_tokens >= 3 && !strcmp(tokens[2], "regrd")) {
2436 			cmd_softnic_pipeline_regrd(softnic, tokens, n_tokens, out, out_size);
2437 			return;
2438 		}
2439 
2440 		if (n_tokens >= 3 && !strcmp(tokens[2], "regwr")) {
2441 			cmd_softnic_pipeline_regwr(softnic, tokens, n_tokens, out, out_size);
2442 			return;
2443 		}
2444 
2445 		if (n_tokens >= 6 &&
2446 			!strcmp(tokens[2], "meter") &&
2447 			!strcmp(tokens[3], "profile") &&
2448 			!strcmp(tokens[5], "add")) {
2449 			cmd_softnic_pipeline_meter_profile_add(softnic, tokens, n_tokens,
2450 				out, out_size);
2451 			return;
2452 		}
2453 
2454 		if (n_tokens >= 6 &&
2455 			!strcmp(tokens[2], "meter") &&
2456 			!strcmp(tokens[3], "profile") &&
2457 			!strcmp(tokens[5], "delete")) {
2458 			cmd_softnic_pipeline_meter_profile_delete(softnic, tokens, n_tokens,
2459 				out, out_size);
2460 			return;
2461 		}
2462 
2463 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[8], "reset")) {
2464 			cmd_softnic_pipeline_meter_reset(softnic, tokens, n_tokens, out, out_size);
2465 			return;
2466 		}
2467 
2468 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[8], "set")) {
2469 			cmd_softnic_pipeline_meter_set(softnic, tokens, n_tokens, out, out_size);
2470 			return;
2471 		}
2472 
2473 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[8], "stats")) {
2474 			cmd_softnic_pipeline_meter_stats(softnic, tokens, n_tokens, out, out_size);
2475 			return;
2476 		}
2477 
2478 		if (n_tokens >= 3 && !strcmp(tokens[2], "stats")) {
2479 			cmd_softnic_pipeline_stats(softnic, tokens, n_tokens, out, out_size);
2480 			return;
2481 		}
2482 
2483 		if (n_tokens >= 4 &&
2484 			!strcmp(tokens[2], "mirror") &&
2485 			!strcmp(tokens[3], "session")) {
2486 			cmd_softnic_pipeline_mirror_session(softnic, tokens, n_tokens,
2487 				out, out_size);
2488 			return;
2489 		}
2490 	}
2491 
2492 	if (strcmp(tokens[0], "thread") == 0) {
2493 		if (n_tokens >= 5 &&
2494 			(strcmp(tokens[4], "enable") == 0)) {
2495 			cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens,
2496 				out, out_size);
2497 			return;
2498 		}
2499 
2500 		if (n_tokens >= 5 &&
2501 			(strcmp(tokens[4], "disable") == 0)) {
2502 			cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens,
2503 				out, out_size);
2504 			return;
2505 		}
2506 	}
2507 
2508 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
2509 }
2510 
2511 int
2512 softnic_cli_script_process(struct pmd_internals *softnic,
2513 	const char *file_name,
2514 	size_t msg_in_len_max,
2515 	size_t msg_out_len_max)
2516 {
2517 	char *msg_in = NULL, *msg_out = NULL;
2518 	FILE *f = NULL;
2519 
2520 	/* Check input arguments */
2521 	if (file_name == NULL ||
2522 		(strlen(file_name) == 0) ||
2523 		msg_in_len_max == 0 ||
2524 		msg_out_len_max == 0)
2525 		return -EINVAL;
2526 
2527 	msg_in = malloc(msg_in_len_max + 1);
2528 	msg_out = malloc(msg_out_len_max + 1);
2529 	if (msg_in == NULL ||
2530 		msg_out == NULL) {
2531 		free(msg_out);
2532 		free(msg_in);
2533 		return -ENOMEM;
2534 	}
2535 
2536 	/* Open input file */
2537 	f = fopen(file_name, "r");
2538 	if (f == NULL) {
2539 		free(msg_out);
2540 		free(msg_in);
2541 		return -EIO;
2542 	}
2543 
2544 	/* Read file */
2545 	for ( ; ; ) {
2546 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
2547 			break;
2548 
2549 		printf("%s", msg_in);
2550 		msg_out[0] = 0;
2551 
2552 		softnic_cli_process(msg_in,
2553 			msg_out,
2554 			msg_out_len_max,
2555 			softnic);
2556 
2557 		if (strlen(msg_out))
2558 			printf("%s", msg_out);
2559 	}
2560 
2561 	/* Close file */
2562 	fclose(f);
2563 	free(msg_out);
2564 	free(msg_in);
2565 	return 0;
2566 }
2567