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