X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fconf-parser.c;h=a9b01135e6a1dfc7a3bcbd335b6d59c1a21cb7be;hp=438b4cc3d57a7e4e7b59671dd283fdf03a952f45;hb=34a35eced40860181757abe5ec00ac0e5d8d0225;hpb=d6c9574fb558d9e304699b1cc7522c3b133adfc9 diff --git a/src/conf-parser.c b/src/conf-parser.c index 438b4cc3d..a9b01135e 100644 --- a/src/conf-parser.c +++ b/src/conf-parser.c @@ -30,53 +30,142 @@ #include "macro.h" #include "strv.h" #include "log.h" +#include "utf8.h" -#define COMMENTS "#;\n" +int config_item_table_lookup( + void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + ConfigTableItem *t; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + for (t = table; t->lvalue; t++) { + + if (!streq(lvalue, t->lvalue)) + continue; + + if (!streq_ptr(section, t->section)) + continue; + + *func = t->parse; + *ltype = t->ltype; + *data = t->data; + return 1; + } + + return 0; +} + +int config_item_perf_lookup( + void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; + const ConfigPerfItem *p; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + if (!section) + p = lookup(lvalue, strlen(lvalue)); + else { + char *key; + + key = join(section, ".", lvalue, NULL); + if (!key) + return -ENOMEM; + + p = lookup(key, strlen(key)); + free(key); + } + + if (!p) + return 0; + + *func = p->parse; + *ltype = p->ltype; + *data = (uint8_t*) userdata + p->offset; + return 1; +} /* Run the user supplied parser for an assignment */ static int next_assignment( const char *filename, unsigned line, + ConfigItemLookup lookup, + void *table, const char *section, - const ConfigItem *t, - bool relaxed, const char *lvalue, const char *rvalue, + bool relaxed, void *userdata) { + ConfigParserCallback func = NULL; + int ltype = 0; + void *data = NULL; + int r; + assert(filename); - assert(t); + assert(line > 0); + assert(lookup); assert(lvalue); assert(rvalue); - for (; t->parse || t->lvalue; t++) { - - if (t->lvalue && !streq(lvalue, t->lvalue)) - continue; - - if (t->section && !section) - continue; - - if (t->section && !streq(section, t->section)) - continue; + r = lookup(table, section, lvalue, &func, <ype, &data, userdata); + if (r < 0) + return r; - if (!t->parse) - return 0; + if (r > 0) { + if (func) + return func(filename, line, section, lvalue, ltype, rvalue, data, userdata); - return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata); + return 0; } /* Warn about unknown non-extension fields. */ if (!relaxed && !startswith(lvalue, "X-")) - log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section)); + log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section); return 0; } /* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, bool relaxed, char *l, void *userdata) { +static int parse_line( + const char *filename, + unsigned line, + const char *sections, + ConfigItemLookup lookup, + void *table, + bool relaxed, + char **section, + char *l, + void *userdata) { + char *e; + assert(filename); + assert(line > 0); + assert(lookup); + assert(l); + l = strstrip(l); if (!*l) @@ -89,10 +178,11 @@ static int parse_line(const char *filename, unsigned line, char **section, const char *fn; int r; - if (!(fn = file_in_same_dir(filename, strstrip(l+9)))) + fn = file_in_same_dir(filename, strstrip(l+9)); + if (!fn) return -ENOMEM; - r = config_parse(fn, NULL, sections, t, relaxed, userdata); + r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata); free(fn); return r; @@ -110,22 +200,35 @@ static int parse_line(const char *filename, unsigned line, char **section, const return -EBADMSG; } - if (!(n = strndup(l+1, k-2))) + n = strndup(l+1, k-2); + if (!n) return -ENOMEM; - if (!relaxed && sections && !strv_contains((char**) sections, n)) - log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n); + if (sections && !nulstr_contains(sections, n)) { - free(*section); - *section = n; + if (!relaxed) + log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n); + + free(n); + *section = NULL; + } else { + free(*section); + *section = n; + } return 0; } - if (sections && (!*section || !strv_contains((char**) sections, *section))) + if (sections && !*section) { + + if (!relaxed) + log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line); + return 0; + } - if (!(e = strchr(l, '='))) { + e = strchr(l, '='); + if (!e) { log_error("[%s:%u] Missing '='.", filename, line); return -EBADMSG; } @@ -133,11 +236,28 @@ static int parse_line(const char *filename, unsigned line, char **section, const *e = 0; e++; - return next_assignment(filename, line, *section, t, relaxed, strstrip(l), strstrip(e), userdata); + return next_assignment( + filename, + line, + lookup, + table, + *section, + strstrip(l), + strstrip(e), + relaxed, + userdata); } /* Go through the file and parse each line */ -int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, bool relaxed, void *userdata) { +int config_parse( + const char *filename, + FILE *f, + const char *sections, + ConfigItemLookup lookup, + void *table, + bool relaxed, + void *userdata) { + unsigned line = 0; char *section = NULL; int r; @@ -145,10 +265,11 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co char *continuation = NULL; assert(filename); - assert(t); + assert(lookup); if (!f) { - if (!(f = fopen(filename, "re"))) { + f = fopen(filename, "re"); + if (!f) { r = -errno; log_error("Failed to open configuration file '%s': %s", filename, strerror(-r)); goto finish; @@ -173,7 +294,8 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co truncate_nl(l); if (continuation) { - if (!(c = strappend(continuation, l))) { + c = strappend(continuation, l); + if (!c) { r = -ENOMEM; goto finish; } @@ -196,15 +318,26 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co if (c) continuation = c; - else if (!(continuation = strdup(l))) { - r = -ENOMEM; - goto finish; + else { + continuation = strdup(l); + if (!continuation) { + r = -ENOMEM; + goto finish; + } } continue; } - r = parse_line(filename, ++line, §ion, sections, t, relaxed, p, userdata); + r = parse_line(filename, + ++line, + sections, + lookup, + table, + relaxed, + §ion, + p, + userdata); free(c); if (r < 0) @@ -228,6 +361,7 @@ int config_parse_int( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -241,8 +375,60 @@ int config_parse_int( assert(data); if ((r = safe_atoi(rvalue, i)) < 0) { - log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); - return r; + log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_long( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + long *i = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if ((r = safe_atoli(rvalue, i)) < 0) { + log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_uint64( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint64_t *u = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if ((r = safe_atou64(rvalue, u)) < 0) { + log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue); + return 0; } return 0; @@ -253,6 +439,7 @@ int config_parse_unsigned( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -273,30 +460,58 @@ int config_parse_unsigned( return 0; } -int config_parse_size( +int config_parse_bytes_size( const char *filename, unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { size_t *sz = data; - unsigned u; - int r; + off_t o; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if ((r = safe_atou(rvalue, &u)) < 0) { - log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); - return r; + if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) { + log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue); + return 0; + } + + *sz = (size_t) o; + return 0; +} + + +int config_parse_bytes_off( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + off_t *bytes = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + assert_cc(sizeof(off_t) == sizeof(uint64_t)); + + if (parse_bytes(rvalue, bytes) < 0) { + log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue); + return 0; } - *sz = (size_t) u; return 0; } @@ -305,6 +520,7 @@ int config_parse_bool( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -318,8 +534,38 @@ int config_parse_bool( assert(data); if ((k = parse_boolean(rvalue)) < 0) { - log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); - return k; + log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue); + return 0; + } + + *b = !!k; + return 0; +} + +int config_parse_tristate( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + int k; + int *b = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */ + + k = parse_boolean(rvalue); + if (k < 0) { + log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue); + return 0; } *b = !!k; @@ -331,6 +577,7 @@ int config_parse_string( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -343,14 +590,23 @@ int config_parse_string( assert(rvalue); assert(data); - if (*rvalue) { - if (!(n = strdup(rvalue))) - return -ENOMEM; - } else - n = NULL; + n = cunescape(rvalue); + if (!n) + return -ENOMEM; + + if (!utf8_is_valid(n)) { + log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); + free(n); + return 0; + } free(*s); - *s = n; + if (*n) + *s = n; + else { + free(n); + *s = NULL; + } return 0; } @@ -360,6 +616,7 @@ int config_parse_path( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -372,12 +629,18 @@ int config_parse_path( assert(rvalue); assert(data); + if (!utf8_is_valid(rvalue)) { + log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); + return 0; + } + if (!path_is_absolute(rvalue)) { - log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); - return -EINVAL; + log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue); + return 0; } - if (!(n = strdup(rvalue))) + n = strdup(rvalue); + if (!n) return -ENOMEM; path_kill_slashes(n); @@ -393,6 +656,7 @@ int config_parse_strv( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -403,6 +667,7 @@ int config_parse_strv( unsigned k; size_t l; char *state; + int r; assert(filename); assert(lvalue); @@ -413,7 +678,8 @@ int config_parse_strv( FOREACH_WORD_QUOTED(w, l, rvalue, state) k++; - if (!(n = new(char*, k+1))) + n = new(char*, k+1); + if (!n) return -ENOMEM; if (*sv) @@ -422,9 +688,21 @@ int config_parse_strv( else k = 0; - FOREACH_WORD_QUOTED(w, l, rvalue, state) - if (!(n[k++] = cunescape_length(w, l))) + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + n[k] = cunescape_length(w, l); + if (!n[k]) { + r = -ENOMEM; goto fail; + } + + if (!utf8_is_valid(n[k])) { + log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); + free(n[k]); + continue; + } + + k++; + } n[k] = NULL; free(*sv); @@ -437,7 +715,7 @@ fail: free(n[k-1]); free(n); - return -ENOMEM; + return r; } int config_parse_path_strv( @@ -445,6 +723,7 @@ int config_parse_path_strv( unsigned line, const char *section, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata) { @@ -466,7 +745,8 @@ int config_parse_path_strv( FOREACH_WORD_QUOTED(w, l, rvalue, state) k++; - if (!(n = new(char*, k+1))) + n = new(char*, k+1); + if (!n) return -ENOMEM; k = 0; @@ -475,19 +755,25 @@ int config_parse_path_strv( n[k] = (*sv)[k]; FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (!(n[k] = cunescape_length(w, l))) { + n[k] = strndup(w, l); + if (!n[k]) { r = -ENOMEM; goto fail; } + if (!utf8_is_valid(n[k])) { + log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); + free(n[k]); + continue; + } + if (!path_is_absolute(n[k])) { - log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); - r = -EINVAL; - goto fail; + log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue); + free(n[k]); + continue; } path_kill_slashes(n[k]); - k++; } @@ -498,10 +784,69 @@ int config_parse_path_strv( return 0; fail: - free(n[k]); for (; k > 0; k--) free(n[k-1]); free(n); return r; } + +int config_parse_usec( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + usec_t *usec = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (parse_usec(rvalue, usec) < 0) { + log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue); + return 0; + } + + return 0; +} + +int config_parse_mode( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + mode_t *m = data; + long l; + char *x = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + errno = 0; + l = strtol(rvalue, &x, 8); + if (!x || *x || errno) { + log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue); + return 0; + } + + if (l < 0000 || l > 07777) { + log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue); + return 0; + } + + *m = (mode_t) l; + return 0; +}