Support for xtesting
[samplevnf.git] / VNFs / DPPD-PROX / cfgfile.c
1 /*
2 // Copyright (c) 2010-2017 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16
17 #include "cfgfile.h"
18
19 #include <rte_string_fns.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <unistd.h>
25
26 #include "parse_utils.h"
27 #include "log.h"
28 #include "quit.h"
29 #include "prox_compat.h"
30
31 #define UINT32_MAX_STR "4294967295"
32
33 /*
34  * Allocate cfg_file structure.
35  * Returns pointer to the allocated structure, NULL otherwise.
36  */
37 struct cfg_file *cfg_open(const char *cfg_name)
38 {
39         if (cfg_name == NULL) {
40                 plog_err("\tNo config file name provided\n");
41                 return NULL;
42         }
43         if (access(cfg_name, F_OK)) {
44                 plog_err("\tError opening config file '%s': %s\n", cfg_name, strerror(errno));
45                 return NULL;
46         }
47
48         FILE *pf = fopen(cfg_name, "rb");
49         if (pf == NULL) {
50                 plog_err("\tError opening config file '%s'\n", cfg_name);
51                 return NULL;
52         }
53
54         struct cfg_file *pcfg = calloc(1, sizeof(struct cfg_file));
55
56         if (pcfg == NULL) {
57                 fclose(pf);
58                 plog_err("\tCouldn't allocate memory for config file struct\n");
59                 return NULL;
60         }
61
62         pcfg->pfile = pf;
63         pcfg->name = strdup(cfg_name);
64
65         return pcfg;
66 }
67
68 /* Free memory allocated for cfg_file structure.
69  * Returns 0 on success, -1 if the pointer to the pcfg is invalid */
70 int cfg_close(struct cfg_file *pcfg)
71 {
72         if (pcfg == NULL) {
73                 return -1;
74         }
75
76         if (pcfg->name != NULL) {
77                 free(pcfg->name);
78         }
79         if (pcfg->err_section != NULL) {
80                 free(pcfg->err_section);
81         }
82         if (pcfg->pfile != NULL) {
83                 fclose(pcfg->pfile);
84         }
85
86         free(pcfg);
87         return 0;
88 }
89
90 static int cfg_get_pos(struct cfg_file *pcfg, fpos_t *pos)
91 {
92         pcfg->index_line = pcfg->line;
93         return fgetpos(pcfg->pfile, pos);
94 }
95
96 static int cfg_set_pos(struct cfg_file *pcfg, fpos_t *pos)
97 {
98         pcfg->line = pcfg->index_line;
99         return fsetpos(pcfg->pfile, pos);
100 }
101
102 /*
103  * Read a line from the configuration file.
104  * Returns: on success length of the line read from the file is returned,
105  *          0 to indicate End of File,
106  *         -1 in case of wrong function parameters
107  */
108 static int cfg_get_line(struct cfg_file *pcfg, char *buffer, unsigned len, int raw_lines)
109 {
110         char *ptr;
111
112         if (pcfg == NULL || pcfg->pfile == NULL || buffer == NULL || len == 0) {
113                 return -1;
114         }
115
116         do {
117                 ptr = fgets(buffer, len, pcfg->pfile);
118                 if (ptr == NULL) {
119                         return 0; /* end of file */
120                 }
121                 ++pcfg->line;
122
123                 if (raw_lines) {
124                         break;
125                 }
126
127                 /* remove comments */
128                 ptr = strchr(buffer, ';');
129                 if (ptr != NULL) {
130                         *ptr = '\0';
131                 }
132                 else {
133                         ptr = strchr(buffer, '\0');
134                 }
135
136                 /* remove trailing spaces */
137                 if (ptr != buffer) {
138                         ptr--;
139                         while (isspace(*ptr)) {
140                                 *ptr = '\0';
141                                 ptr--;
142                         }
143                 }
144
145                 ptr = buffer;
146                 /* remove leading spaces */
147                 while (*ptr && isspace(*ptr)) {
148                         ++ptr;
149                 }
150                 if (ptr != buffer) {
151                         strcpy(buffer, ptr);
152                         ptr = buffer;
153                 }
154         }
155         while (*ptr == '\0'); /* skip empty strings */
156
157         return strlen(buffer);
158 }
159
160 /*
161  * Checks if buffer contains section name specified by the cfg_section pointer.
162  * Returns NULL if section name does not match, cfg_section pointer otherwise
163  */
164 static struct cfg_section *cfg_check_section(char *buffer, struct cfg_section *psec)
165 {
166         char *pend;
167         unsigned len;
168         static const char *valid = "0123456789,hs- \t";
169
170         pend = strchr(buffer, ']');
171         if (pend == NULL) {
172                 return NULL; /* ']' not found: invalid section name */
173         }
174
175         *pend = '\0';
176
177         /* check if section is indexed */
178         pend = strchr(psec->name, '#');
179         if (pend == NULL) {
180                 return (strcmp(buffer, psec->name) == 0) ? psec : NULL;
181         }
182
183         /* get section index */
184         len = pend - psec->name;
185         if (strncmp(buffer, psec->name, len) != 0) {
186                 return NULL;
187         }
188         pend = buffer + len;
189         if (*pend == '\0') {
190                 return NULL;
191         }
192
193         /* only numeric characters are valid for section index */
194         char val[MAX_CFG_STRING_LEN];
195         if (pend[0] == '$') {
196                 if (parse_vars(val, sizeof(val), pend))
197                         return NULL;
198         } else
199                 prox_strncpy(val, pend, sizeof(val));
200
201         for (len = 0; val[len] != '\0'; ++len) {
202                 if (strchr(valid, val[len]) == NULL) {
203                         return NULL;
204                 }
205         }
206
207         psec->nbindex = parse_list_set(psec->indexp, pend, MAX_INDEX);
208         PROX_PANIC(psec->nbindex == -1, "\t\tError in cfg_check_section('%s'): %s\n", buffer, get_parse_err());
209
210         for (int i = 0; i < psec->nbindex; ++i) {
211                 psec->indexp[i] |= CFG_INDEXED;
212         }
213
214         return psec;
215 }
216
217 static char *cfg_get_section_name(struct cfg_section *psec)
218 {
219         char *name;
220
221         if (!(psec->indexp[0] & CFG_INDEXED)) {
222                 return strdup(psec->name);
223         }
224
225         name = malloc(strlen(psec->name) + strlen(UINT32_MAX_STR));
226         if (name != NULL) {
227                 strcpy(name, psec->name);
228                 char *pidx = strchr(name, '#');
229                 if (pidx != NULL) {
230                         sprintf(pidx, "%u", psec->indexp[0] & ~CFG_INDEXED);
231                 }
232         }
233         return name;
234 }
235
236 /*
237  * Reads configuration file and parses section specified by psec pointer.
238  * Returns 0 on success, -1 otherwise
239  */
240 int cfg_parse(struct cfg_file *pcfg, struct cfg_section *psec)
241 {
242         int error;
243         unsigned entry = 0;
244         fpos_t pos;
245         int index_count = 0;
246         struct cfg_section *section = NULL;
247         char buffer[sizeof(pcfg->cur_line)] = {0};
248
249         if (pcfg == NULL || psec == NULL) {
250                 return -1;
251         }
252
253         pcfg->line = 0;
254         fseek(pcfg->pfile, 0, SEEK_SET);
255
256         /* read configuration file and parse section specified by psec pointer */
257         while (1) {
258                 if (psec->raw_lines) {
259                         /* skip until section starts */
260                         char *lines = pcfg->cur_line;
261                         size_t max_len = sizeof(pcfg->cur_line);
262                         char *ret;
263
264                         do {
265                                 ret = fgets(lines, max_len, pcfg->pfile);
266                                 if (ret && *ret == '[') {
267                                         section = cfg_check_section(lines + 1, psec);
268                                 }
269                         } while (!section && ret);
270
271                         if (!ret)
272                                 return 0;
273
274                         do {
275
276                                 ret = fgets(buffer, sizeof(buffer), pcfg->pfile);
277                                 /* remove comments */
278                                 if (*ret == ';') {
279                                         *ret = '\0';
280                                 }
281
282                                 if (ret && *ret != '[') {
283                                         size_t l = strlen(buffer);
284                                         prox_strncpy(lines, buffer, max_len);
285                                         max_len -= l;
286                                         lines += l;
287                                 }
288                         } while ((ret && *ret != '['));
289
290                         if (section != NULL) {
291                                 error = section->parser(section->indexp[index_count], pcfg->cur_line, section->data);
292                                 if (error != 0) {
293                                         section->error = error;
294                                         /* log only the very first error */
295                                         if (!pcfg->err_section) {
296                                                 pcfg->err_line = pcfg->line;
297                                                 pcfg->err_entry = entry;
298                                                 pcfg->err_section = cfg_get_section_name(section);
299                                         }
300                                         return 0;
301                                 }
302                                 ++entry;
303                         }
304                         return 0;
305                 }
306
307                 while (cfg_get_line(pcfg, buffer, MAX_CFG_STRING_LEN, psec->raw_lines) > 0) {
308                         prox_strncpy(pcfg->cur_line, buffer, sizeof(pcfg->cur_line));
309                         if (*buffer == '[') {
310                                 if (index_count + 1 < psec->nbindex) {
311                                         // Need to loop - go back to recorded postion in file
312                                         cfg_set_pos(pcfg, &pos);
313                                         ++index_count;
314                                         continue;
315                                 }
316                                 else {
317                                         section = cfg_check_section(buffer + 1, psec);
318                                         entry = 0;
319                                         index_count = 0;
320                                         cfg_get_pos(pcfg, &pos);
321                                         continue;
322                                 }
323                         }
324                         /* call parser procedure for each line in the section */
325                         if (section != NULL) {
326                                 error = section->parser(section->indexp[index_count], buffer, section->data);
327                                 if (error != 0) {
328                                         section->error = error;
329                                         /* log only the very first error */
330                                         if (!pcfg->err_section) {
331                                                 pcfg->err_line = pcfg->line;
332                                                 pcfg->err_entry = entry;
333                                                 pcfg->err_section = cfg_get_section_name(section);
334                                         }
335                                         return 0;
336                                 }
337                                 ++entry;
338                         }
339                 }
340                 if (index_count + 1 < psec->nbindex) {
341                         // Last core config contained multiple cores - loop back
342                         cfg_set_pos(pcfg, &pos);
343                         ++index_count;
344                 }
345                 else {
346                         break;
347                 }
348         }
349         return 0;
350 }