examples/ip_pipeline: add traffic manager object
[dpdk.git] / examples / ip_pipeline / cli.c
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
10 #include <rte_common.h>
11
12 #include "cli.h"
13 #include "link.h"
14 #include "mempool.h"
15 #include "parser.h"
16 #include "swq.h"
17 #include "tmgr.h"
18
19 #ifndef CMD_MAX_TOKENS
20 #define CMD_MAX_TOKENS     256
21 #endif
22
23 #define MSG_OUT_OF_MEMORY  "Not enough memory.\n"
24 #define MSG_CMD_UNKNOWN    "Unknown command \"%s\".\n"
25 #define MSG_CMD_UNIMPLEM   "Command \"%s\" not implemented.\n"
26 #define MSG_ARG_NOT_ENOUGH "Not enough arguments for command \"%s\".\n"
27 #define MSG_ARG_TOO_MANY   "Too many arguments for command \"%s\".\n"
28 #define MSG_ARG_MISMATCH   "Wrong number of arguments for command \"%s\".\n"
29 #define MSG_ARG_NOT_FOUND  "Argument \"%s\" not found.\n"
30 #define MSG_ARG_INVALID    "Invalid value for argument \"%s\".\n"
31 #define MSG_FILE_ERR       "Error in file \"%s\" at line %u.\n"
32 #define MSG_CMD_FAIL       "Command \"%s\" failed.\n"
33
34 static int
35 is_comment(char *in)
36 {
37         if ((strlen(in) && index("!#%;", in[0])) ||
38                 (strncmp(in, "//", 2) == 0) ||
39                 (strncmp(in, "--", 2) == 0))
40                 return 1;
41
42         return 0;
43 }
44
45 /**
46  * mempool <mempool_name>
47  *  buffer <buffer_size>
48  *  pool <pool_size>
49  *  cache <cache_size>
50  *  cpu <cpu_id>
51  */
52 static void
53 cmd_mempool(char **tokens,
54         uint32_t n_tokens,
55         char *out,
56         size_t out_size)
57 {
58         struct mempool_params p;
59         char *name;
60         struct mempool *mempool;
61
62         if (n_tokens != 10) {
63                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
64                 return;
65         }
66
67         name = tokens[1];
68
69         if (strcmp(tokens[2], "buffer") != 0) {
70                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
71                 return;
72         }
73
74         if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
75                 snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
76                 return;
77         }
78
79         if (strcmp(tokens[4], "pool") != 0) {
80                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
81                 return;
82         }
83
84         if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
85                 snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
86                 return;
87         }
88
89         if (strcmp(tokens[6], "cache") != 0) {
90                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
91                 return;
92         }
93
94         if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
95                 snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
96                 return;
97         }
98
99         if (strcmp(tokens[8], "cpu") != 0) {
100                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
101                 return;
102         }
103
104         if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
105                 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
106                 return;
107         }
108
109         mempool = mempool_create(name, &p);
110         if (mempool == NULL) {
111                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
112                 return;
113         }
114 }
115
116 /**
117  * link <link_name>
118  *  dev <device_name> | port <port_id>
119  *  rxq <n_queues> <queue_size> <mempool_name>
120  *  txq <n_queues> <queue_size>
121  *  promiscuous on | off
122  *  [rss <qid_0> ... <qid_n>]
123  */
124 static void
125 cmd_link(char **tokens,
126         uint32_t n_tokens,
127         char *out,
128         size_t out_size)
129 {
130         struct link_params p;
131         struct link_params_rss rss;
132         struct link *link;
133         char *name;
134
135         if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
136                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
137                 return;
138         }
139         name = tokens[1];
140
141         if (strcmp(tokens[2], "dev") == 0)
142                 p.dev_name = tokens[3];
143         else if (strcmp(tokens[2], "port") == 0) {
144                 p.dev_name = NULL;
145
146                 if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
147                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
148                         return;
149                 }
150         } else {
151                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
152                 return;
153         }
154
155         if (strcmp(tokens[4], "rxq") != 0) {
156                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
157                 return;
158         }
159
160         if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
161                 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
162                 return;
163         }
164         if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
165                 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
166                 return;
167         }
168
169         p.rx.mempool_name = tokens[7];
170
171         if (strcmp(tokens[8], "txq") != 0) {
172                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
173                 return;
174         }
175
176         if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
177                 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
178                 return;
179         }
180
181         if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
182                 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
183                 return;
184         }
185
186         if (strcmp(tokens[11], "promiscuous") != 0) {
187                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
188                 return;
189         }
190
191         if (strcmp(tokens[12], "on") == 0)
192                 p.promiscuous = 1;
193         else if (strcmp(tokens[12], "off") == 0)
194                 p.promiscuous = 0;
195         else {
196                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
197                 return;
198         }
199
200         /* RSS */
201         p.rx.rss = NULL;
202         if (n_tokens > 13) {
203                 uint32_t queue_id, i;
204
205                 if (strcmp(tokens[13], "rss") != 0) {
206                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
207                         return;
208                 }
209
210                 p.rx.rss = &rss;
211
212                 rss.n_queues = 0;
213                 for (i = 14; i < n_tokens; i++) {
214                         if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
215                                 snprintf(out, out_size, MSG_ARG_INVALID,
216                                         "queue_id");
217                                 return;
218                         }
219
220                         rss.queue_id[rss.n_queues] = queue_id;
221                         rss.n_queues++;
222                 }
223         }
224
225         link = link_create(name, &p);
226         if (link == NULL) {
227                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
228                 return;
229         }
230 }
231
232 /**
233  * swq <swq_name>
234  *  size <size>
235  *  cpu <cpu_id>
236  */
237 static void
238 cmd_swq(char **tokens,
239         uint32_t n_tokens,
240         char *out,
241         size_t out_size)
242 {
243         struct swq_params p;
244         char *name;
245         struct swq *swq;
246
247         if (n_tokens != 6) {
248                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
249                 return;
250         }
251
252         name = tokens[1];
253
254         if (strcmp(tokens[2], "size") != 0) {
255                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
256                 return;
257         }
258
259         if (parser_read_uint32(&p.size, tokens[3]) != 0) {
260                 snprintf(out, out_size, MSG_ARG_INVALID, "size");
261                 return;
262         }
263
264         if (strcmp(tokens[4], "cpu") != 0) {
265                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
266                 return;
267         }
268
269         if (parser_read_uint32(&p.cpu_id, tokens[5]) != 0) {
270                 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
271                 return;
272         }
273
274         swq = swq_create(name, &p);
275         if (swq == NULL) {
276                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
277                 return;
278         }
279 }
280
281 /**
282  * tmgr subport profile
283  *  <tb_rate> <tb_size>
284  *  <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate>
285  *  <tc_period>
286  */
287 static void
288 cmd_tmgr_subport_profile(char **tokens,
289         uint32_t n_tokens,
290         char *out,
291         size_t out_size)
292 {
293         struct rte_sched_subport_params p;
294         int status, i;
295
296         if (n_tokens != 10) {
297                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
298                 return;
299         }
300
301         if (parser_read_uint32(&p.tb_rate, tokens[3]) != 0) {
302                 snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
303                 return;
304         }
305
306         if (parser_read_uint32(&p.tb_size, tokens[4]) != 0) {
307                 snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
308                 return;
309         }
310
311         for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
312                 if (parser_read_uint32(&p.tc_rate[i], tokens[5 + i]) != 0) {
313                         snprintf(out, out_size, MSG_ARG_INVALID, "tc_rate");
314                         return;
315                 }
316
317         if (parser_read_uint32(&p.tc_period, tokens[9]) != 0) {
318                 snprintf(out, out_size, MSG_ARG_INVALID, "tc_period");
319                 return;
320         }
321
322         status = tmgr_subport_profile_add(&p);
323         if (status != 0) {
324                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
325                 return;
326         }
327 }
328
329 /**
330  * tmgr pipe profile
331  *  <tb_rate> <tb_size>
332  *  <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate>
333  *  <tc_period>
334  *  <tc_ov_weight>
335  *  <wrr_weight0..15>
336  */
337 static void
338 cmd_tmgr_pipe_profile(char **tokens,
339         uint32_t n_tokens,
340         char *out,
341         size_t out_size)
342 {
343         struct rte_sched_pipe_params p;
344         int status, i;
345
346         if (n_tokens != 27) {
347                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
348                 return;
349         }
350
351         if (parser_read_uint32(&p.tb_rate, tokens[3]) != 0) {
352                 snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
353                 return;
354         }
355
356         if (parser_read_uint32(&p.tb_size, tokens[4]) != 0) {
357                 snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
358                 return;
359         }
360
361         for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
362                 if (parser_read_uint32(&p.tc_rate[i], tokens[5 + i]) != 0) {
363                         snprintf(out, out_size, MSG_ARG_INVALID, "tc_rate");
364                         return;
365                 }
366
367         if (parser_read_uint32(&p.tc_period, tokens[9]) != 0) {
368                 snprintf(out, out_size, MSG_ARG_INVALID, "tc_period");
369                 return;
370         }
371
372 #ifdef RTE_SCHED_SUBPORT_TC_OV
373         if (parser_read_uint8(&p.tc_ov_weight, tokens[10]) != 0) {
374                 snprintf(out, out_size, MSG_ARG_INVALID, "tc_ov_weight");
375                 return;
376         }
377 #endif
378
379         for (i = 0; i < RTE_SCHED_QUEUES_PER_PIPE; i++)
380                 if (parser_read_uint8(&p.wrr_weights[i], tokens[11 + i]) != 0) {
381                         snprintf(out, out_size, MSG_ARG_INVALID, "wrr_weights");
382                         return;
383                 }
384
385         status = tmgr_pipe_profile_add(&p);
386         if (status != 0) {
387                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
388                 return;
389         }
390 }
391
392 /**
393  * tmgr <tmgr_name>
394  *  rate <rate>
395  *  spp <n_subports_per_port>
396  *  pps <n_pipes_per_subport>
397  *  qsize <qsize_tc0> <qsize_tc1> <qsize_tc2> <qsize_tc3>
398  *  fo <frame_overhead>
399  *  mtu <mtu>
400  *  cpu <cpu_id>
401  */
402 static void
403 cmd_tmgr(char **tokens,
404         uint32_t n_tokens,
405         char *out,
406         size_t out_size)
407 {
408         struct tmgr_port_params p;
409         char *name;
410         struct tmgr_port *tmgr_port;
411         int i;
412
413         if (n_tokens != 19) {
414                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
415                 return;
416         }
417
418         name = tokens[1];
419
420         if (strcmp(tokens[2], "rate") != 0) {
421                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rate");
422                 return;
423         }
424
425         if (parser_read_uint32(&p.rate, tokens[3]) != 0) {
426                 snprintf(out, out_size, MSG_ARG_INVALID, "rate");
427                 return;
428         }
429
430         if (strcmp(tokens[4], "spp") != 0) {
431                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
432                 return;
433         }
434
435         if (parser_read_uint32(&p.n_subports_per_port, tokens[5]) != 0) {
436                 snprintf(out, out_size, MSG_ARG_INVALID, "n_subports_per_port");
437                 return;
438         }
439
440         if (strcmp(tokens[6], "pps") != 0) {
441                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
442                 return;
443         }
444
445         if (parser_read_uint32(&p.n_pipes_per_subport, tokens[7]) != 0) {
446                 snprintf(out, out_size, MSG_ARG_INVALID, "n_pipes_per_subport");
447                 return;
448         }
449
450         if (strcmp(tokens[8], "qsize") != 0) {
451                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
452                 return;
453         }
454
455         for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
456                 if (parser_read_uint16(&p.qsize[i], tokens[9 + i]) != 0) {
457                         snprintf(out, out_size, MSG_ARG_INVALID, "qsize");
458                         return;
459                 }
460
461         if (strcmp(tokens[13], "fo") != 0) {
462                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fo");
463                 return;
464         }
465
466         if (parser_read_uint32(&p.frame_overhead, tokens[14]) != 0) {
467                 snprintf(out, out_size, MSG_ARG_INVALID, "frame_overhead");
468                 return;
469         }
470
471         if (strcmp(tokens[15], "mtu") != 0) {
472                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mtu");
473                 return;
474         }
475
476         if (parser_read_uint32(&p.mtu, tokens[16]) != 0) {
477                 snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
478                 return;
479         }
480
481         if (strcmp(tokens[17], "cpu") != 0) {
482                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
483                 return;
484         }
485
486         if (parser_read_uint32(&p.cpu_id, tokens[18]) != 0) {
487                 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
488                 return;
489         }
490
491         tmgr_port = tmgr_port_create(name, &p);
492         if (tmgr_port == NULL) {
493                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
494                 return;
495         }
496 }
497
498 /**
499  * tmgr <tmgr_name> subport <subport_id>
500  *  profile <subport_profile_id>
501  */
502 static void
503 cmd_tmgr_subport(char **tokens,
504         uint32_t n_tokens,
505         char *out,
506         size_t out_size)
507 {
508         uint32_t subport_id, subport_profile_id;
509         int status;
510         char *name;
511
512         if (n_tokens != 6) {
513                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
514                 return;
515         }
516
517         name = tokens[1];
518
519         if (parser_read_uint32(&subport_id, tokens[3]) != 0) {
520                 snprintf(out, out_size, MSG_ARG_INVALID, "subport_id");
521                 return;
522         }
523
524         if (parser_read_uint32(&subport_profile_id, tokens[5]) != 0) {
525                 snprintf(out, out_size, MSG_ARG_INVALID, "subport_profile_id");
526                 return;
527         }
528
529         status = tmgr_subport_config(name, subport_id, subport_profile_id);
530         if (status) {
531                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
532                 return;
533         }
534 }
535
536 /**
537  * tmgr <tmgr_name> subport <subport_id> pipe
538  *  from <pipe_id_first> to <pipe_id_last>
539  *  profile <pipe_profile_id>
540  */
541 static void
542 cmd_tmgr_subport_pipe(char **tokens,
543         uint32_t n_tokens,
544         char *out,
545         size_t out_size)
546 {
547         uint32_t subport_id, pipe_id_first, pipe_id_last, pipe_profile_id;
548         int status;
549         char *name;
550
551         if (n_tokens != 11) {
552                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
553                 return;
554         }
555
556         name = tokens[1];
557
558         if (parser_read_uint32(&subport_id, tokens[3]) != 0) {
559                 snprintf(out, out_size, MSG_ARG_INVALID, "subport_id");
560                 return;
561         }
562
563         if (strcmp(tokens[4], "pipe") != 0) {
564                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipe");
565                 return;
566         }
567
568         if (strcmp(tokens[5], "from") != 0) {
569                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
570                 return;
571         }
572
573         if (parser_read_uint32(&pipe_id_first, tokens[6]) != 0) {
574                 snprintf(out, out_size, MSG_ARG_INVALID, "pipe_id_first");
575                 return;
576         }
577
578         if (strcmp(tokens[7], "to") != 0) {
579                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
580                 return;
581         }
582
583         if (parser_read_uint32(&pipe_id_last, tokens[8]) != 0) {
584                 snprintf(out, out_size, MSG_ARG_INVALID, "pipe_id_last");
585                 return;
586         }
587
588         if (strcmp(tokens[9], "profile") != 0) {
589                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
590                 return;
591         }
592
593         if (parser_read_uint32(&pipe_profile_id, tokens[10]) != 0) {
594                 snprintf(out, out_size, MSG_ARG_INVALID, "pipe_profile_id");
595                 return;
596         }
597
598         status = tmgr_pipe_config(name, subport_id, pipe_id_first,
599                         pipe_id_last, pipe_profile_id);
600         if (status) {
601                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
602                 return;
603         }
604 }
605
606 void
607 cli_process(char *in, char *out, size_t out_size)
608 {
609         char *tokens[CMD_MAX_TOKENS];
610         uint32_t n_tokens = RTE_DIM(tokens);
611         int status;
612
613         if (is_comment(in))
614                 return;
615
616         status = parse_tokenize_string(in, tokens, &n_tokens);
617         if (status) {
618                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
619                 return;
620         }
621
622         if (n_tokens == 0)
623                 return;
624
625         if (strcmp(tokens[0], "mempool") == 0) {
626                 cmd_mempool(tokens, n_tokens, out, out_size);
627                 return;
628         }
629
630         if (strcmp(tokens[0], "link") == 0) {
631                 cmd_link(tokens, n_tokens, out, out_size);
632                 return;
633         }
634
635         if (strcmp(tokens[0], "swq") == 0) {
636                 cmd_swq(tokens, n_tokens, out, out_size);
637                 return;
638         }
639
640         if (strcmp(tokens[0], "tmgr") == 0) {
641                 if ((n_tokens >= 3) &&
642                         (strcmp(tokens[1], "subport") == 0) &&
643                         (strcmp(tokens[2], "profile") == 0)) {
644                         cmd_tmgr_subport_profile(tokens, n_tokens,
645                                 out, out_size);
646                         return;
647                 }
648
649                 if ((n_tokens >= 3) &&
650                         (strcmp(tokens[1], "pipe") == 0) &&
651                         (strcmp(tokens[2], "profile") == 0)) {
652                         cmd_tmgr_pipe_profile(tokens, n_tokens, out, out_size);
653                         return;
654                 }
655
656                 if ((n_tokens >= 5) &&
657                         (strcmp(tokens[2], "subport") == 0) &&
658                         (strcmp(tokens[4], "profile") == 0)) {
659                         cmd_tmgr_subport(tokens, n_tokens, out, out_size);
660                         return;
661                 }
662
663                 if ((n_tokens >= 5) &&
664                         (strcmp(tokens[2], "subport") == 0) &&
665                         (strcmp(tokens[4], "pipe") == 0)) {
666                         cmd_tmgr_subport_pipe(tokens, n_tokens, out, out_size);
667                         return;
668                 }
669
670                 cmd_tmgr(tokens, n_tokens, out, out_size);
671                 return;
672         }
673
674         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
675 }
676
677 int
678 cli_script_process(const char *file_name,
679         size_t msg_in_len_max,
680         size_t msg_out_len_max)
681 {
682         char *msg_in = NULL, *msg_out = NULL;
683         FILE *f = NULL;
684
685         /* Check input arguments */
686         if ((file_name == NULL) ||
687                 (strlen(file_name) == 0) ||
688                 (msg_in_len_max == 0) ||
689                 (msg_out_len_max == 0))
690                 return -EINVAL;
691
692         msg_in = malloc(msg_in_len_max + 1);
693         msg_out = malloc(msg_out_len_max + 1);
694         if ((msg_in == NULL) ||
695                 (msg_out == NULL)) {
696                 free(msg_out);
697                 free(msg_in);
698                 return -ENOMEM;
699         }
700
701         /* Open input file */
702         f = fopen(file_name, "r");
703         if (f == NULL) {
704                 free(msg_out);
705                 free(msg_in);
706                 return -EIO;
707         }
708
709         /* Read file */
710         for ( ; ; ) {
711                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
712                         break;
713
714                 printf("%s", msg_in);
715                 msg_out[0] = 0;
716
717                 cli_process(msg_in,
718                         msg_out,
719                         msg_out_len_max);
720
721                 if (strlen(msg_out))
722                         printf("%s", msg_out);
723         }
724
725         /* Close file */
726         fclose(f);
727         free(msg_out);
728         free(msg_in);
729         return 0;
730 }