Imported Upstream version 0.7.6
[platform/upstream/multipath-tools.git] / libmultipath / parser.c
index f1fdeb8..2f9ab6e 100644 (file)
  */
 
 #include <syslog.h>
+#include <errno.h>
 
+#include "vector.h"
+#include "config.h"
 #include "parser.h"
 #include "memory.h"
+#include "debug.h"
 
 /* local vars */
 static int sublevel = 0;
-vector keywords = NULL;
-vector *keywords_addr = NULL;
-
-void set_current_keywords (vector *k)
-{
-       keywords_addr = k;
-       keywords = NULL;
-}
+static int line_nr;
 
 int
-keyword_alloc(vector keywords, char *string, int (*handler) (vector),
-               int (*print) (char *, int, void *))
+keyword_alloc(vector keywords, char *string,
+             int (*handler) (struct config *, vector),
+             int (*print) (struct config *, char *, int, const void*),
+             int unique)
 {
        struct keyword *keyword;
 
@@ -51,21 +50,13 @@ keyword_alloc(vector keywords, char *string, int (*handler) (vector),
        keyword->string = string;
        keyword->handler = handler;
        keyword->print = print;
+       keyword->unique = unique;
 
        vector_set_slot(keywords, keyword);
 
        return 0;
 }
 
-int
-install_keyword_root(char *string, int (*handler) (vector))
-{
-       int r = keyword_alloc(keywords, string, handler, NULL);
-       if (!r)
-               *keywords_addr = keywords;
-       return r;
-}
-
 void
 install_sublevel(void)
 {
@@ -79,8 +70,10 @@ install_sublevel_end(void)
 }
 
 int
-install_keyword(char *string, int (*handler) (vector),
-               int (*print) (char *, int, void *))
+_install_keyword(vector keywords, char *string,
+                int (*handler) (struct config *, vector),
+                int (*print) (struct config *, char *, int, const void*),
+                int unique)
 {
        int i = 0;
        struct keyword *keyword;
@@ -101,7 +94,7 @@ install_keyword(char *string, int (*handler) (vector),
                return 1;
 
        /* add new sub keyword */
-       return keyword_alloc(keyword->sub, string, handler, print);
+       return keyword_alloc(keyword->sub, string, handler, print, unique);
 }
 
 void
@@ -123,7 +116,7 @@ free_keywords(vector keywords)
 }
 
 struct keyword *
-find_keyword(vector v, char * name)
+find_keyword(vector keywords, vector v, char * name)
 {
        struct keyword *keyword;
        int i;
@@ -143,7 +136,7 @@ find_keyword(vector v, char * name)
                    !strcmp(keyword->string, name))
                        return keyword;
                if (keyword->sub) {
-                       keyword = find_keyword(keyword->sub, name);
+                       keyword = find_keyword(keywords, keyword->sub, name);
                        if (keyword)
                                return keyword;
                }
@@ -152,11 +145,13 @@ find_keyword(vector v, char * name)
 }
 
 int
-snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data)
+snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
+               const void *data)
 {
        int r;
        int fwd = 0;
        char *f = fmt;
+       struct config *conf;
 
        if (!kw || !kw->print)
                return 0;
@@ -175,9 +170,11 @@ snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data)
                        fwd += snprintf(buff + fwd, len - fwd, "%s", kw->string);
                        break;
                case 'v':
-                       r = kw->print(buff + fwd, len - fwd, data);
+                       conf = get_multipath_config();
+                       r = kw->print(conf, buff + fwd, len - fwd, data);
+                       put_multipath_config(conf);
                        if (!r) { /* no output if no value */
-                               buff = '\0';
+                               buff[0] = '\0';
                                return 0;
                        }
                        fwd += r;
@@ -189,6 +186,12 @@ snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data)
        return fwd;
 }
 
+static const char quote_marker[] = { '\0', '"', '\0' };
+bool is_quote(const char* token)
+{
+       return !memcmp(token, quote_marker, sizeof(quote_marker));
+}
+
 vector
 alloc_strvec(char *string)
 {
@@ -222,19 +225,20 @@ alloc_strvec(char *string)
 
        in_string = 0;
        while (1) {
+               int two_quotes = 0;
+
                if (!vector_alloc_slot(strvec))
                        goto out;
 
                start = cp;
-               if (*cp == '"') {
+               if (*cp == '"' && !(in_string && *(cp + 1) == '"')) {
                        cp++;
-                       token = MALLOC(2);
+                       token = MALLOC(sizeof(quote_marker));
 
                        if (!token)
                                goto out;
 
-                       *(token) = '"';
-                       *(token + 1) = '\0';
+                       memcpy(token, quote_marker, sizeof(quote_marker));
                        if (in_string)
                                in_string = 0;
                        else
@@ -249,11 +253,23 @@ alloc_strvec(char *string)
                        *(token + 1) = '\0';
                        cp++;
                } else {
+
+               move_on:
                        while ((in_string ||
                                (!isspace((int) *cp) && isascii((int) *cp) &&
                                 *cp != '!' && *cp != '#' && *cp != '{' &&
                                 *cp != '}')) && *cp != '\0' && *cp != '"')
                                cp++;
+
+                       /* Two consecutive double quotes - don't end string */
+                       if (in_string && *cp == '"') {
+                               if (*(cp + 1) == '"') {
+                                       two_quotes = 1;
+                                       cp += 2;
+                                       goto move_on;
+                               }
+                       }
+
                        strlen = cp - start;
                        token = MALLOC(strlen + 1);
 
@@ -262,10 +278,21 @@ alloc_strvec(char *string)
 
                        memcpy(token, start, strlen);
                        *(token + strlen) = '\0';
+
+                       /* Replace "" by " */
+                       if (two_quotes) {
+                               char *qq = strstr(token, "\"\"");
+                               while (qq != NULL) {
+                                       memmove(qq + 1, qq + 2,
+                                               strlen + 1 - (qq + 2 - token));
+                                       qq = strstr(qq + 1, "\"\"");
+                               }
+                       }
                }
                vector_set_slot(strvec, token);
 
-               while ((isspace((int) *cp) || !isascii((int) *cp))
+               while ((!in_string &&
+                       (isspace((int) *cp) || !isascii((int) *cp)))
                       && *cp != '\0')
                        cp++;
                if (*cp == '\0' || *cp == '!' || *cp == '#')
@@ -276,222 +303,296 @@ out:
        return NULL;
 }
 
-int
-read_line(char *buf, int size)
+static int
+read_line(FILE *stream, char *buf, int size)
 {
-       int ch;
-       int count = 0;
+       char *p;
 
-       while ((ch = fgetc(stream)) != EOF && (int) ch != '\n'
-              && (int) ch != '\r') {
-               if (count < size)
-                       buf[count] = (int) ch;
-               else
-                       break;
-               count++;
-       }
-       return (ch == EOF) ? 0 : 1;
+       if (fgets(buf, size, stream) == NULL)
+               return 0;
+       strtok_r(buf, "\n\r", &p);
+       return 1;
 }
 
-vector
-read_value_block(void)
+void *
+set_value(vector strvec)
 {
-       char *buf;
-       int i;
-       char *str = NULL;
-       char *dup;
-       vector vec = NULL;
-       vector elements = vector_alloc();
-
-       buf = (char *) MALLOC(MAXBUF);
+       char *str = VECTOR_SLOT(strvec, 1);
+       size_t size;
+       int i = 0;
+       int len = 0;
+       char *alloc = NULL;
+       char *tmp;
 
-       if (!buf)
+       if (!str) {
+               condlog(0, "option '%s' missing value",
+                       (char *)VECTOR_SLOT(strvec, 0));
                return NULL;
-
-       if (!elements)
-               goto out;
-
-       while (read_line(buf, MAXBUF)) {
-               vec = alloc_strvec(buf);
-               if (vec) {
-                       str = VECTOR_SLOT(vec, 0);
-                       if (!strcmp(str, EOB)) {
-                               free_strvec(vec);
-                               break;
-                       }
-
-                       if (VECTOR_SIZE(vec))
-                               for (i = 0; i < VECTOR_SIZE(vec); i++) {
-                                       str = VECTOR_SLOT(vec, i);
-                                       dup = (char *) MALLOC(strlen(str) + 1);
-                                       memcpy(dup, str, strlen(str));
-
-                                       if (!vector_alloc_slot(elements))
-                                               goto out1;
-
-                                       vector_set_slot(elements, dup);
-                               }
-                       free_strvec(vec);
+       }
+       if (!is_quote(str)) {
+               size = strlen(str);
+               if (size == 0) {
+                       condlog(0, "option '%s' has empty value",
+                               (char *)VECTOR_SLOT(strvec, 0));
+                       return NULL;
                }
-               memset(buf, 0, MAXBUF);
+               alloc = MALLOC(sizeof (char) * (size + 1));
+               if (alloc)
+                       memcpy(alloc, str, size);
+               else
+                       condlog(0, "can't allocate memeory for option '%s'",
+                               (char *)VECTOR_SLOT(strvec, 0));
+               return alloc;
        }
-       FREE(buf);
-       return elements;
-out1:
-       FREE(dup);
-out:
-       FREE(buf);
-       return NULL;
+       /* Even empty quotes counts as a value (An empty string) */
+       alloc = (char *) MALLOC(sizeof (char));
+       if (!alloc) {
+               condlog(0, "can't allocate memeory for option '%s'",
+                       (char *)VECTOR_SLOT(strvec, 0));
+               return NULL;
+       }
+       for (i = 2; i < VECTOR_SIZE(strvec); i++) {
+               str = VECTOR_SLOT(strvec, i);
+               if (!str) {
+                       free(alloc);
+                       condlog(0, "parse error for option '%s'",
+                               (char *)VECTOR_SLOT(strvec, 0));
+                       return NULL;
+               }
+               if (is_quote(str))
+                       break;
+               tmp = alloc;
+               /* The first +1 is for the NULL byte. The rest are for the
+                * spaces between words */
+               len += strlen(str) + 1;
+               alloc = REALLOC(alloc, sizeof (char) * len);
+               if (!alloc) {
+                       FREE(tmp);
+                       condlog(0, "can't allocate memeory for option '%s'",
+                               (char *)VECTOR_SLOT(strvec, 0));
+                       return NULL;
+               }
+               if (*alloc != '\0')
+                       strncat(alloc, " ", 1);
+               strncat(alloc, str, strlen(str));
+       }
+       return alloc;
 }
 
-int
-alloc_value_block(vector strvec, void (*alloc_func) (vector))
-{
-       char *buf;
-       char *str = NULL;
-       vector vec = NULL;
-
-       buf = (char *) MALLOC(MAXBUF);
-
-       if (!buf)
-               return 1;
-
-       while (read_line(buf, MAXBUF)) {
-               vec = alloc_strvec(buf);
-               if (vec) {
-                       str = VECTOR_SLOT(vec, 0);
-                       if (!strcmp(str, EOB)) {
-                               free_strvec(vec);
-                               break;
-                       }
+/* non-recursive configuration stream handler */
+static int kw_level = 0;
 
-                       if (VECTOR_SIZE(vec))
-                               (*alloc_func) (vec);
+int warn_on_duplicates(vector uniques, char *str, char *file)
+{
+       char *tmp;
+       int i;
 
-                       free_strvec(vec);
+       vector_foreach_slot(uniques, tmp, i) {
+               if (!strcmp(str, tmp)) {
+                       condlog(1, "%s line %d, duplicate keyword: %s",
+                               file, line_nr, str);
+                       return 0;
                }
-               memset(buf, 0, MAXBUF);
        }
-       FREE(buf);
+       tmp = strdup(str);
+       if (!tmp)
+               return 1;
+       if (!vector_alloc_slot(uniques)) {
+               free(tmp);
+               return 1;
+       }
+       vector_set_slot(uniques, tmp);
        return 0;
 }
 
-void *
-set_value(vector strvec)
+void free_uniques(vector uniques)
 {
-       char *str = VECTOR_SLOT(strvec, 1);
-       int size = strlen(str);
-       int i = 0;
-       int len = 0;
-       char *alloc = NULL;
        char *tmp;
+       int i;
 
-       if (*str == '"') {
-               for (i = 2; i < VECTOR_SIZE(strvec); i++) {
-                       str = VECTOR_SLOT(strvec, i);
-                       len += strlen(str);
-                       if (!alloc)
-                               alloc =
-                                   (char *) MALLOC(sizeof (char *) *
-                                                   (len + 1));
-                       else {
-                               alloc =
-                                   REALLOC(alloc, sizeof (char *) * (len + 1));
-                               tmp = VECTOR_SLOT(strvec, i-1);
-                               if (*str != '"' && *tmp != '"')
-                                       strncat(alloc, " ", 1);
-                       }
+       vector_foreach_slot(uniques, tmp, i)
+               free(tmp);
+       vector_free(uniques);
+}
+
+int
+is_sublevel_keyword(char *str)
+{
+       return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 ||
+               strcmp(str, "blacklist_exceptions") == 0 ||
+               strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 ||
+               strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 ||
+               strcmp(str, "multipath") == 0);
+}
 
-                       if (i != VECTOR_SIZE(strvec)-1)
-                               strncat(alloc, str, strlen(str));
+int
+validate_config_strvec(vector strvec, char *file)
+{
+       char *str;
+       int i;
+
+       str = VECTOR_SLOT(strvec, 0);
+       if (str == NULL) {
+               condlog(0, "can't parse option on line %d of %s",
+                       line_nr, file);
+       return -1;
+       }
+       if (*str == '}') {
+               if (VECTOR_SIZE(strvec) > 1)
+                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file);
+               return 0;
+       }
+       if (*str == '{') {
+               condlog(0, "invalid keyword '%s' on line %d of %s",
+                       str, line_nr, file);
+               return -1;
+       }
+       if (is_sublevel_keyword(str)) {
+               str = VECTOR_SLOT(strvec, 1);
+               if (str == NULL)
+                       condlog(0, "missing '{' on line %d of %s",
+                               line_nr, file);
+               else if (*str != '{')
+                       condlog(0, "expecting '{' on line %d of %s. found '%s'",
+                               line_nr, file, str);
+               else if (VECTOR_SIZE(strvec) > 2)
+                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
+               return 0;
+       }
+       str = VECTOR_SLOT(strvec, 1);
+       if (str == NULL) {
+               condlog(0, "missing value for option '%s' on line %d of %s",
+                       (char *)VECTOR_SLOT(strvec, 0), line_nr, file);
+               return -1;
+       }
+       if (!is_quote(str)) {
+               if (VECTOR_SIZE(strvec) > 2)
+                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
+               return 0;
+       }
+       for (i = 2; i < VECTOR_SIZE(strvec); i++) {
+               str = VECTOR_SLOT(strvec, i);
+               if (str == NULL) {
+                       condlog(0, "can't parse value on line %d of %s",
+                               line_nr, file);
+                       return -1;
+               }
+               if (is_quote(str)) {
+                       if (VECTOR_SIZE(strvec) > i + 1)
+                               condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file);
+                       return 0;
                }
-       } else {
-               alloc = MALLOC(sizeof (char *) * (size + 1));
-               memcpy(alloc, str, size);
        }
-       return alloc;
+       condlog(0, "missing closing quotes on line %d of %s",
+               line_nr, file);
+       return 0;
 }
 
-/* non-recursive configuration stream handler */
-static int kw_level = 0;
-int
-process_stream(vector keywords)
+static int
+process_stream(struct config *conf, FILE *stream, vector keywords, char *file)
 {
        int i;
-       int r = 0;
+       int r = 0, t;
        struct keyword *keyword;
        char *str;
        char *buf;
        vector strvec;
+       vector uniques;
+
+       uniques = vector_alloc();
+       if (!uniques)
+               return 1;
 
        buf = MALLOC(MAXBUF);
 
-       if (!buf)
+       if (!buf) {
+               vector_free(uniques);
                return 1;
+       }
 
-       while (read_line(buf, MAXBUF)) {
+       while (read_line(stream, buf, MAXBUF)) {
+               line_nr++;
                strvec = alloc_strvec(buf);
-               memset(buf,0, MAXBUF);
-
                if (!strvec)
                        continue;
 
+               if (validate_config_strvec(strvec, file) != 0) {
+                       free_strvec(strvec);
+                       continue;
+               }
+
                str = VECTOR_SLOT(strvec, 0);
 
-               if (!strcmp(str, EOB) && kw_level > 0) {
-                       free_strvec(strvec);
-                       break;
+               if (!strcmp(str, EOB)) {
+                       if (kw_level > 0) {
+                               free_strvec(strvec);
+                               break;
+                       }
+                       condlog(0, "unmatched '%s' at line %d of %s",
+                               EOB, line_nr, file);
                }
 
                for (i = 0; i < VECTOR_SIZE(keywords); i++) {
                        keyword = VECTOR_SLOT(keywords, i);
 
                        if (!strcmp(keyword->string, str)) {
-                               if (keyword->handler)
-                                       r += (*keyword->handler) (strvec);
+                               if (keyword->unique &&
+                                   warn_on_duplicates(uniques, str, file)) {
+                                               r = 1;
+                                               free_strvec(strvec);
+                                               goto out;
+                               }
+                               if (keyword->handler) {
+                                   t = (*keyword->handler) (conf, strvec);
+                                       r += t;
+                                       if (t)
+                                               condlog(1, "multipath.conf +%d, parsing failed: %s",
+                                                       line_nr, buf);
+                               }
 
                                if (keyword->sub) {
                                        kw_level++;
-                                       r += process_stream(keyword->sub);
+                                       r += process_stream(conf, stream,
+                                                           keyword->sub, file);
                                        kw_level--;
                                }
                                break;
                        }
                }
+               if (i >= VECTOR_SIZE(keywords))
+                       condlog(1, "%s line %d, invalid keyword: %s",
+                               file, line_nr, str);
 
                free_strvec(strvec);
        }
 
+out:
        FREE(buf);
+       free_uniques(uniques);
        return r;
 }
 
 /* Data initialization */
 int
-init_data(char *conf_file, void (*init_keywords) (void))
+process_file(struct config *conf, char *file)
 {
        int r;
+       FILE *stream;
 
-       if (!keywords)
-               keywords = vector_alloc();
-       if (!keywords)
+       if (!conf->keywords) {
+               condlog(0, "No keywords allocated");
                return 1;
-       stream = fopen(conf_file, "r");
+       }
+       stream = fopen(file, "r");
        if (!stream) {
-               syslog(LOG_WARNING, "Configuration file open problem");
+               condlog(0, "couldn't open configuration file '%s': %s",
+                       file, strerror(errno));
                return 1;
        }
 
-       /* Init Keywords structure */
-       (*init_keywords) ();
-
-/* Dump configuration *
-  vector_dump(keywords);
-  dump_keywords(keywords, 0);
-*/
-
        /* Stream handling */
-       r = process_stream(keywords);
+       line_nr = 0;
+       r = process_stream(conf, stream, conf->keywords, file);
        fclose(stream);
        //free_keywords(keywords);