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