e4296ee49155b8858e6ec2ce22622bdff8a811c3
[multipath-tools/.git] / libmultipath / parser.c
1 /*
2  * Part:        Configuration file parser/reader. Place into the dynamic
3  *              data structure representation the conf file
4  *
5  * Version:     $Id: parser.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
6  *
7  * Author:      Alexandre Cassen, <acassen@linux-vs.org>
8  *
9  *              This program is distributed in the hope that it will be useful,
10  *              but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *              See the GNU General Public License for more details.
13  *
14  *              This program is free software; you can redistribute it and/or
15  *              modify it under the terms of the GNU General Public License
16  *              as published by the Free Software Foundation; either version
17  *              2 of the License, or (at your option) any later version.
18  */
19
20 #include <syslog.h>
21 #include <errno.h>
22
23 #include "parser.h"
24 #include "memory.h"
25 #include "debug.h"
26
27 /* local vars */
28 static int sublevel = 0;
29 static vector keywords = NULL;
30 static vector *keywords_addr = NULL;
31 static int line_nr;
32
33 void set_current_keywords (vector *k)
34 {
35         keywords_addr = k;
36         keywords = NULL;
37 }
38
39 int
40 keyword_alloc(vector keywords, char *string, int (*handler) (vector),
41                 int (*print) (char *, int, void *), int unique)
42 {
43         struct keyword *keyword;
44
45         keyword = (struct keyword *) MALLOC(sizeof (struct keyword));
46
47         if (!keyword)
48                 return 1;
49
50         if (!vector_alloc_slot(keywords)) {
51                 FREE(keyword);
52                 return 1;
53         }
54         keyword->string = string;
55         keyword->handler = handler;
56         keyword->print = print;
57         keyword->unique = unique;
58
59         vector_set_slot(keywords, keyword);
60
61         return 0;
62 }
63
64 int
65 install_keyword_root(char *string, int (*handler) (vector))
66 {
67         int r = keyword_alloc(keywords, string, handler, NULL, 1);
68         if (!r)
69                 *keywords_addr = keywords;
70         return r;
71 }
72
73 void
74 install_sublevel(void)
75 {
76         sublevel++;
77 }
78
79 void
80 install_sublevel_end(void)
81 {
82         sublevel--;
83 }
84
85 int
86 _install_keyword(char *string, int (*handler) (vector),
87                 int (*print) (char *, int, void *), int unique)
88 {
89         int i = 0;
90         struct keyword *keyword;
91
92         /* fetch last keyword */
93         keyword = VECTOR_SLOT(keywords, VECTOR_SIZE(keywords) - 1);
94
95         /* position to last sub level */
96         for (i = 0; i < sublevel; i++)
97                 keyword =
98                     VECTOR_SLOT(keyword->sub, VECTOR_SIZE(keyword->sub) - 1);
99
100         /* First sub level allocation */
101         if (!keyword->sub)
102                 keyword->sub = vector_alloc();
103
104         if (!keyword->sub)
105                 return 1;
106
107         /* add new sub keyword */
108         return keyword_alloc(keyword->sub, string, handler, print, unique);
109 }
110
111 void
112 free_keywords(vector keywords)
113 {
114         struct keyword *keyword;
115         int i;
116
117         if (!keywords)
118                 return;
119
120         for (i = 0; i < VECTOR_SIZE(keywords); i++) {
121                 keyword = VECTOR_SLOT(keywords, i);
122                 if (keyword->sub)
123                         free_keywords(keyword->sub);
124                 FREE(keyword);
125         }
126         vector_free(keywords);
127 }
128
129 struct keyword *
130 find_keyword(vector v, char * name)
131 {
132         struct keyword *keyword;
133         int i;
134         int len;
135
136         if (!name || !keywords)
137                 return NULL;
138
139         if (!v)
140                 v = keywords;
141
142         len = strlen(name);
143
144         for (i = 0; i < VECTOR_SIZE(v); i++) {
145                 keyword = VECTOR_SLOT(v, i);
146                 if ((strlen(keyword->string) == len) &&
147                     !strcmp(keyword->string, name))
148                         return keyword;
149                 if (keyword->sub) {
150                         keyword = find_keyword(keyword->sub, name);
151                         if (keyword)
152                                 return keyword;
153                 }
154         }
155         return NULL;
156 }
157
158 int
159 snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data)
160 {
161         int r;
162         int fwd = 0;
163         char *f = fmt;
164
165         if (!kw || !kw->print)
166                 return 0;
167
168         do {
169                 if (fwd == len || *f == '\0')
170                         break;
171                 if (*f != '%') {
172                         *(buff + fwd) = *f;
173                         fwd++;
174                         continue;
175                 }
176                 f++;
177                 switch(*f) {
178                 case 'k':
179                         fwd += snprintf(buff + fwd, len - fwd, "%s", kw->string);
180                         break;
181                 case 'v':
182                         r = kw->print(buff + fwd, len - fwd, data);
183                         if (!r) { /* no output if no value */
184                                 buff = '\0';
185                                 return 0;
186                         }
187                         fwd += r;
188                         break;
189                 }
190                 if (fwd > len)
191                         fwd = len;
192         } while (*f++);
193         return fwd;
194 }
195
196 vector
197 alloc_strvec(char *string)
198 {
199         char *cp, *start, *token;
200         int strlen;
201         int in_string;
202         vector strvec;
203
204         if (!string)
205                 return NULL;
206
207         cp = string;
208
209         /* Skip white spaces */
210         while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0')
211                 cp++;
212
213         /* Return if there is only white spaces */
214         if (*cp == '\0')
215                 return NULL;
216
217         /* Return if string begin with a comment */
218         if (*cp == '!' || *cp == '#')
219                 return NULL;
220
221         /* Create a vector and alloc each command piece */
222         strvec = vector_alloc();
223
224         if (!strvec)
225                 return NULL;
226
227         in_string = 0;
228         while (1) {
229                 if (!vector_alloc_slot(strvec))
230                         goto out;
231
232                 start = cp;
233                 if (*cp == '"') {
234                         cp++;
235                         token = MALLOC(2);
236
237                         if (!token)
238                                 goto out;
239
240                         *(token) = '"';
241                         *(token + 1) = '\0';
242                         if (in_string)
243                                 in_string = 0;
244                         else
245                                 in_string = 1;
246                 } else if (!in_string && (*cp == '{' || *cp == '}')) {
247                         token = MALLOC(2);
248
249                         if (!token)
250                                 goto out;
251
252                         *(token) = *cp;
253                         *(token + 1) = '\0';
254                         cp++;
255                 } else {
256                         while ((in_string ||
257                                 (!isspace((int) *cp) && isascii((int) *cp) &&
258                                  *cp != '!' && *cp != '#' && *cp != '{' &&
259                                  *cp != '}')) && *cp != '\0' && *cp != '"')
260                                 cp++;
261                         strlen = cp - start;
262                         token = MALLOC(strlen + 1);
263
264                         if (!token)
265                                 goto out;
266
267                         memcpy(token, start, strlen);
268                         *(token + strlen) = '\0';
269                 }
270                 vector_set_slot(strvec, token);
271
272                 while ((isspace((int) *cp) || !isascii((int) *cp))
273                        && *cp != '\0')
274                         cp++;
275                 if (*cp == '\0' || *cp == '!' || *cp == '#')
276                         return strvec;
277         }
278 out:
279         vector_free(strvec);
280         return NULL;
281 }
282
283 static int
284 read_line(FILE *stream, char *buf, int size)
285 {
286         char *p;
287
288         if (fgets(buf, size, stream) == NULL)
289                 return 0;
290         strtok_r(buf, "\n\r", &p);
291         return 1;
292 }
293
294 void *
295 set_value(vector strvec)
296 {
297         char *str = VECTOR_SLOT(strvec, 1);
298         size_t size;
299         int i = 0;
300         int len = 0;
301         char *alloc = NULL;
302         char *tmp;
303
304         if (!str) {
305                 condlog(0, "option '%s' missing value",
306                         (char *)VECTOR_SLOT(strvec, 0));
307                 return NULL;
308         }
309         size = strlen(str);
310         if (size == 0) {
311                 condlog(0, "option '%s' has empty value",
312                         (char *)VECTOR_SLOT(strvec, 0));
313                 return NULL;
314         }
315         if (*str != '"') {
316                 alloc = MALLOC(sizeof (char) * (size + 1));
317                 if (alloc)
318                         memcpy(alloc, str, size);
319                 else
320                         condlog(0, "can't allocate memeory for option '%s'",
321                                 (char *)VECTOR_SLOT(strvec, 0));
322                 return alloc;
323         }
324         /* Even empty quotes counts as a value (An empty string) */
325         alloc = (char *) MALLOC(sizeof (char));
326         if (!alloc) {
327                 condlog(0, "can't allocate memeory for option '%s'",
328                         (char *)VECTOR_SLOT(strvec, 0));
329                 return NULL;
330         }
331         for (i = 2; i < VECTOR_SIZE(strvec); i++) {
332                 str = VECTOR_SLOT(strvec, i);
333                 if (!str) {
334                         free(alloc);
335                         condlog(0, "parse error for option '%s'",
336                                 (char *)VECTOR_SLOT(strvec, 0));
337                         return NULL;
338                 }
339                 if (*str == '"')
340                         break;
341                 tmp = alloc;
342                 /* The first +1 is for the NULL byte. The rest are for the
343                  * spaces between words */
344                 len += strlen(str) + 1;
345                 alloc = REALLOC(alloc, sizeof (char) * len);
346                 if (!alloc) {
347                         FREE(tmp);
348                         condlog(0, "can't allocate memeory for option '%s'",
349                                 (char *)VECTOR_SLOT(strvec, 0));
350                         return NULL;
351                 }
352                 if (*alloc != '\0')
353                         strncat(alloc, " ", 1);
354                 strncat(alloc, str, strlen(str));
355         }
356         return alloc;
357 }
358
359 /* non-recursive configuration stream handler */
360 static int kw_level = 0;
361
362 int warn_on_duplicates(vector uniques, char *str, char *file)
363 {
364         char *tmp;
365         int i;
366
367         vector_foreach_slot(uniques, tmp, i) {
368                 if (!strcmp(str, tmp)) {
369                         condlog(1, "%s line %d, duplicate keyword: %s",
370                                 file, line_nr, str);
371                         return 0;
372                 }
373         }
374         tmp = strdup(str);
375         if (!tmp)
376                 return 1;
377         if (!vector_alloc_slot(uniques)) {
378                 free(tmp);
379                 return 1;
380         }
381         vector_set_slot(uniques, tmp);
382         return 0;
383 }
384
385 void free_uniques(vector uniques)
386 {
387         char *tmp;
388         int i;
389
390         vector_foreach_slot(uniques, tmp, i)
391                 free(tmp);
392         vector_free(uniques);
393 }
394
395 int
396 is_sublevel_keyword(char *str)
397 {
398         return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 ||
399                 strcmp(str, "blacklist_exceptions") == 0 ||
400                 strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 ||
401                 strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 ||
402                 strcmp(str, "multipath") == 0);
403 }
404
405 int
406 validate_config_strvec(vector strvec, char *file)
407 {
408         char *str;
409         int i;
410
411         str = VECTOR_SLOT(strvec, 0);
412         if (str == NULL) {
413                 condlog(0, "can't parse option on line %d of %s",
414                         line_nr, file);
415         return -1;
416         }
417         if (*str == '}') {
418                 if (VECTOR_SIZE(strvec) > 1)
419                         condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file);
420                 return 0;
421         }
422         if (*str == '{') {
423                 condlog(0, "invalid keyword '%s' on line %d of %s",
424                         str, line_nr, file);
425                 return -1;
426         }
427         if (is_sublevel_keyword(str)) {
428                 str = VECTOR_SLOT(strvec, 1);
429                 if (str == NULL)
430                         condlog(0, "missing '{' on line %d of %s",
431                                 line_nr, file);
432                 else if (*str != '{')
433                         condlog(0, "expecting '{' on line %d of %s. found '%s'",
434                                 line_nr, file, str);
435                 else if (VECTOR_SIZE(strvec) > 2)
436                         condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
437                 return 0;
438         }
439         str = VECTOR_SLOT(strvec, 1);
440         if (str == NULL) {
441                 condlog(0, "missing value for option '%s' on line %d of %s",
442                         (char *)VECTOR_SLOT(strvec, 0), line_nr, file);
443                 return -1;
444         }
445         if (*str != '"') {
446                 if (VECTOR_SIZE(strvec) > 2)
447                         condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
448                 return 0;
449         }
450         for (i = 2; i < VECTOR_SIZE(strvec); i++) {
451                 str = VECTOR_SLOT(strvec, i);
452                 if (str == NULL) {
453                         condlog(0, "can't parse value on line %d of %s",
454                                 line_nr, file);
455                         return -1;
456                 }
457                 if (*str == '"') {
458                         if (VECTOR_SIZE(strvec) > i + 1)
459                                 condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file);
460                         return 0;
461                 }
462         }
463         condlog(0, "missing closing quotes on line %d of %s",
464                 line_nr, file);
465         return 0;
466 }
467
468 static int
469 process_stream(FILE *stream, vector keywords, char *file)
470 {
471         int i;
472         int r = 0, t;
473         struct keyword *keyword;
474         char *str;
475         char *buf;
476         vector strvec;
477         vector uniques;
478
479         uniques = vector_alloc();
480         if (!uniques)
481                 return 1;
482
483         buf = MALLOC(MAXBUF);
484
485         if (!buf) {
486                 vector_free(uniques);
487                 return 1;
488         }
489
490         while (read_line(stream, buf, MAXBUF)) {
491                 line_nr++;
492                 strvec = alloc_strvec(buf);
493                 if (!strvec)
494                         continue;
495
496                 if (validate_config_strvec(strvec, file) != 0) {
497                         free_strvec(strvec);
498                         continue;
499                 }
500
501                 str = VECTOR_SLOT(strvec, 0);
502
503                 if (!strcmp(str, EOB)) {
504                         if (kw_level > 0) {
505                                 free_strvec(strvec);
506                                 break;
507                         }
508                         condlog(0, "unmatched '%s' at line %d of %s",
509                                 EOB, line_nr, file);
510                 }
511
512                 for (i = 0; i < VECTOR_SIZE(keywords); i++) {
513                         keyword = VECTOR_SLOT(keywords, i);
514
515                         if (!strcmp(keyword->string, str)) {
516                                 if (keyword->unique &&
517                                     warn_on_duplicates(uniques, str, file)) {
518                                                 r = 1;
519                                                 free_strvec(strvec);
520                                                 goto out;
521                                 }
522                                 if (keyword->handler) {
523                                         t = (*keyword->handler) (strvec);
524                                         r += t;
525                                         if (t)
526                                                 condlog(1, "multipath.conf +%d, parsing failed: %s",
527                                                         line_nr, buf);
528                                 }
529
530                                 if (keyword->sub) {
531                                         kw_level++;
532                                         r += process_stream(stream,
533                                                             keyword->sub, file);
534                                         kw_level--;
535                                 }
536                                 break;
537                         }
538                 }
539                 if (i >= VECTOR_SIZE(keywords))
540                         condlog(1, "%s line %d, invalid keyword: %s",
541                                 file, line_nr, str);
542
543                 free_strvec(strvec);
544         }
545
546 out:
547         FREE(buf);
548         free_uniques(uniques);
549         return r;
550 }
551
552 int alloc_keywords(void)
553 {
554         if (!keywords)
555                 keywords = vector_alloc();
556
557         if (!keywords)
558                 return 1;
559
560         return 0;
561 }
562
563 /* Data initialization */
564 int
565 process_file(char *file)
566 {
567         int r;
568         FILE *stream;
569
570         if (!keywords) {
571                 condlog(0, "No keywords alocated");
572                 return 1;
573         }
574         stream = fopen(file, "r");
575         if (!stream) {
576                 condlog(0, "couldn't open configuration file '%s': %s",
577                         file, strerror(errno));
578                 return 1;
579         }
580
581         /* Stream handling */
582         line_nr = 0;
583         r = process_stream(stream, keywords, file);
584         fclose(stream);
585         //free_keywords(keywords);
586
587         return r;
588 }