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