Modify configurations
[platform/core/system/resourced.git] / src / common / config-parser.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <assert.h>
23 #include <limits.h>
24
25 #include "util.h"
26 #include "trace.h"
27 #include "config-parser.h"
28
29 #define MAX_SECTION     64
30
31 int config_parse(const char *file_name, int cb(struct parse_result *result,
32                         void *user_data), void *user_data)
33 {
34         FILE *f = NULL;
35         struct parse_result result;
36         /* use stack for parsing */
37         char line[LINE_MAX];
38         char section[MAX_SECTION];
39         char *start, *end, *name, *value;
40         int lineno = 0, ret = 0;
41
42         if (!file_name || !cb) {
43                 ret = -EINVAL;
44                 goto error;
45         }
46
47         /* open conf file */
48         f = fopen(file_name, "r");
49         if (!f) {
50                 _E("[DEBUG] Failed to open file %s", file_name);
51                 ret = -EIO;
52                 goto error;
53         }
54
55         /* parsing line by line */
56         while (fgets(line, LINE_MAX, f) != NULL) {
57                 lineno++;
58
59                 start = line;
60                 truncate_nl(start);
61                 start = strstrip(start);
62
63                 if (*start == COMMENT) {
64                         continue;
65                 } else if (*start == '[') {
66                         /* parse section */
67                         end = strchr(start, ']');
68                         if (!end || *end != ']') {
69                                 ret = -EBADMSG;
70                                 goto error;
71                         }
72
73                         *end = '\0';
74                         strncpy(section, start + 1, sizeof(section)-1);
75                         section[MAX_SECTION-1] = '\0';
76                 } else if (*start) {
77                         /* parse name & value */
78                         end = strchr(start, '=');
79                         if (!end || *end != '=') {
80                                 ret = -EBADMSG;
81                                 goto error;
82                         }
83                         *end = '\0';
84                         name = strstrip(start);
85                         value = strstrip(end + 1);
86                         end = strchr(value, COMMENT);
87                         if (end && *end == COMMENT) {
88                                 *end = '\0';
89                                 value = strstrip(value);
90                         }
91
92                         result.section = section;
93                         result.name = name;
94                         result.value = value;
95                         /* callback with parse result */
96                         ret = cb(&result, user_data);
97                         if (ret < 0) {
98                                 ret = -EBADMSG;
99                                 goto error;
100                         }
101                 }
102         }
103         _D("[DEBUG] Success to load %s", file_name);
104         fclose(f);
105         return 0;
106
107 error:
108         if (f)
109                 fclose(f);
110         _E("[DEBUG] Failed to read %s:%d!", file_name, lineno);
111         return ret;
112 }
113
114 static int config_table_lookup(void *table,
115                                const char *section,
116                                const char *lvalue,
117                                ConfigParserCallback *func,
118                                int *ltype,
119                                void **data)
120 {
121         ConfigTableItem *t;
122
123         assert(table);
124         assert(lvalue);
125         assert(func);
126         assert(ltype);
127         assert(data);
128
129         for (t = table; t->lvalue; t++) {
130
131                 if (!streq(lvalue, t->lvalue))
132                         continue;
133
134                 if (!streq_ptr(section, t->section))
135                         continue;
136
137                 *func = t->cb;
138                 *ltype = t->ltype;
139                 *data = t->data;
140                 return 1;
141         }
142
143         return 0;
144 }
145
146 /* Run the user supplied parser for an assignment */
147 static int config_parse_table(const char *filename,
148                               unsigned line,
149                               void *table,
150                               const char *section,
151                               const char *lvalue,
152                               const char *rvalue)
153 {
154         ConfigParserCallback cb = NULL;
155         int ltype = 0;
156         void *data = NULL;
157         int r;
158
159         assert(filename);
160         assert(section);
161         assert(lvalue);
162         assert(rvalue);
163
164         r = config_table_lookup(table,
165                                 section,
166                                 lvalue,
167                                 &cb,
168                                 &ltype,
169                                 &data);
170         if (r <= 0)
171                 return r;
172
173         if (cb)
174                 return cb(filename,
175                           line,
176                           section,
177                           lvalue,
178                           ltype,
179                           rvalue,
180                           data);
181
182         return 0;
183 }
184
185 int config_parse_new(const char *filename, void *table)
186 {
187         _cleanup_fclose_ FILE *f = NULL;
188         char *sections[MAX_SECTION] = { 0 };
189         char *section = NULL, *n, *e, l[LINE_MAX];
190         size_t len;
191         int i, r, num_section = 0;
192         bool already;
193         unsigned line = 0;
194
195         assert(filename);
196
197         f = fopen(filename, "r");
198         if (!f) {
199                 const int saved_errno = errno;
200                 _E("Failed to open file %s", filename); // can modify errno
201                 return -saved_errno;
202         }
203
204         while (!feof(f)) {
205                 _cleanup_free_ char *lvalue = NULL, *rvalue = NULL;
206                 char *rs = NULL;
207
208                 if (fgets(l, LINE_MAX, f) == NULL) {
209                         if (feof(f))
210                                 break;
211
212                         _E("Failed to parse configuration file '%s': %m", filename);
213                         r = -errno;
214                         goto finish;
215                 }
216
217                 line++;
218                 truncate_nl(l);
219
220                 if (strchr(COMMENTS NEWLINE, *l))
221                         continue;
222
223                 if (*l == '[') {
224                         len = strlen(l);
225                         if (l[len-1] != ']') {
226                                 _E("Error: Invalid section header: %s", l);
227                                 r = -EBADMSG;
228                                 goto finish;
229                         }
230
231                         n = strndup(l+1, len-2);
232                         if (!n) {
233                                 r = -ENOMEM;
234                                 goto finish;
235                         }
236
237                         already = false;
238                         for (i = 0; i < num_section; i++) {
239                                 if (streq(n, sections[i])) {
240                                         section = sections[i];
241                                         already = true;
242                                         free(n);
243                                         break;
244                                 }
245                         }
246
247                         if (already)
248                                 continue;
249
250                         section = n;
251                         sections[num_section] = n;
252                         num_section++;
253                         if (num_section > MAX_SECTION) {
254                                 _E("Error: max number of section reached: %d", num_section);
255                                 r = -EOVERFLOW;
256                                 goto finish;
257                         }
258
259                         continue;
260                 }
261
262                 if (!section)
263                         continue;
264
265                 e = strchr(l, '=');
266                 if (e == NULL) {
267                         _D("Warning: config: no '=' character in line '%s'.", l);
268                         continue;
269                 }
270
271                 lvalue = strndup(l, e-l);
272                 if (!lvalue) {
273                         r = -ENOMEM;
274                         goto finish;
275                 }
276
277                 strstrip(lvalue);
278
279                 rs = strstrip(e+1);
280                 rvalue = strdup(rs);
281                 if (!rvalue) {
282                         r = -ENOMEM;
283                         goto finish;
284                 }
285
286                 strstrip(rvalue);
287
288                 r = config_parse_table(filename,
289                                        line,
290                                        table,
291                                        section,
292                                        lvalue,
293                                        rvalue);
294                 if (r < 0)
295                         goto finish;
296         }
297
298         r = 0;
299
300 finish:
301         for (i = 0; i < num_section; i++)
302                 if (sections[i])
303                         free(sections[i]);
304
305         return r;
306 }
307
308 int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data)
309 {
310         _cleanup_closedir_ DIR *d = NULL;
311         struct dirent *de;
312
313         d = opendir(dir);
314         if (!d) {
315                 _E("Failed to open dir: %s", dir);
316                 return errno;
317         }
318
319         for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) {
320                 if (!de) {
321                         if (errno > 0)
322                                 return -errno;
323                         break;
324                 } else if (streq(de->d_name, ".") || streq(de->d_name, "..")) {
325                         continue;
326                 }
327
328                 _cleanup_free_ char *path = NULL;
329                 int r;
330
331                 if (de->d_type != DT_REG)
332                         continue;
333
334                 r = asprintf(&path, "%s/%s", dir, de->d_name);
335                 if (r < 0)
336                         return -ENOMEM;
337
338                 r = fp(path, data);
339                 /* Do not just break loop until parse all file of
340                  * dir. Just only put log */
341                 if (r < 0)
342                         _D("Failed to parse config: %s", de->d_name);
343         }
344
345         return 0;
346 }
347
348 int config_parse_bool(const char *filename,
349                       unsigned line,
350                       const char *section,
351                       const char *lvalue,
352                       int ltype,
353                       const char *rvalue,
354                       void *data)
355 {
356         int k;
357         bool *b = data;
358
359         assert(filename);
360         assert(lvalue);
361         assert(rvalue);
362         assert(data);
363
364         k = parse_boolean(rvalue);
365         if (k < 0) {
366                 _E("Failed to parse boolean value, ignoring: %s", rvalue);
367                 return 0;
368         }
369
370         *b = !!k;
371
372         return 0;
373 }
374
375 #define DEFINE_PARSER(type, vartype, conv_func)          \
376         int config_parse_##type(                                        \
377                                 const char *filename,                   \
378                                 unsigned line,                          \
379                                 const char *section,                    \
380                                 const char *lvalue,                     \
381                                 int ltype,                              \
382                                 const char *rvalue,                     \
383                                 void *data) {                           \
384                                                                                                                 \
385                 vartype *i = data;                                      \
386                                                                         \
387                 assert(filename);                                       \
388                 assert(lvalue);                                         \
389                 assert(rvalue);                                         \
390                 assert(data);                                           \
391                                                                         \
392                 *i = conv_func(rvalue);                          \
393                 return 0;                                               \
394                 }
395
396 DEFINE_PARSER(int, int, atoi)
397 DEFINE_PARSER(float, float, atof)
398 DEFINE_PARSER(long, long, atol)
399
400 int config_parse_string(const char *filename,
401                         unsigned line,
402                         const char *section,
403                         const char *lvalue,
404                         int ltype,
405                         const char *rvalue,
406                         void *data)
407 {
408         char **s = data, *n;
409
410         assert(filename);
411         assert(lvalue);
412         assert(rvalue);
413         assert(data);
414
415         if (is_empty(rvalue))
416                 n = NULL;
417         else {
418                 n = strdup(rvalue);
419                 if (!n)
420                         return -ENOMEM;
421         }
422
423         free(*s);
424         *s = n;
425
426         return 0;
427 }
428
429 int config_parse_bytes(const char *filename,
430                        unsigned line,
431                        const char *section,
432                        const char *lvalue,
433                        int ltype,
434                        const char *rvalue,
435                        void *data)
436 {
437         size_t *s = data;
438         int r;
439
440         assert(filename);
441         assert(lvalue);
442         assert(rvalue);
443         assert(data);
444
445         if (is_empty(rvalue))
446                 *s = 0;
447         else {
448                 r = parse_bytes(rvalue, s);
449                 if (r < 0)
450                         return r;
451         }
452
453         return 0;
454 }
455
456 int config_parse_strv(const char *filename,
457                       unsigned line,
458                       const char *section,
459                       const char *lvalue,
460                       int ltype,
461                       const char *rvalue,
462                       void *data)
463 {
464         char ***strv = data;
465         char **o = NULL, **v = NULL, **vv = NULL;
466         int r;
467
468         assert(filename);
469         assert(lvalue);
470         assert(rvalue);
471         assert(data);
472
473         if (is_empty(rvalue))
474                 return 0;
475
476         r = str_to_strv(rvalue, &v, WHITESPACE);
477         if (r < 0)
478                 return r;
479
480         o = *strv;
481
482         r = strv_attach(o, v, &vv, true);
483         if (r < 0)
484                 return r;
485
486         *strv = vv;
487
488         return 0;
489 }