*/
#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;
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)
{
}
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;
return 1;
/* add new sub keyword */
- return keyword_alloc(keyword->sub, string, handler, print);
+ return keyword_alloc(keyword->sub, string, handler, print, unique);
}
void
}
struct keyword *
-find_keyword(vector v, char * name)
+find_keyword(vector keywords, vector v, char * name)
{
struct keyword *keyword;
int i;
!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;
}
}
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;
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;
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)
{
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
*(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);
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 == '#')
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);