From a5b77939fbedc7f1c8b0734dc677c30f62a635f7 Mon Sep 17 00:00:00 2001 From: Peter Marschall Date: Thu, 12 Aug 2010 22:41:05 -0500 Subject: [PATCH] ifupdown: make parser for /etc/network/interfaces more robust The previous implementation of the parser for /etc/network/interfaces had quite a few drawbacks: - it expected the lines to be terminated with "\n", even the last line - it ignored line wraps with "\\" followed by "\n" - it expected over-long lines to be shorter than 510 characters - it ignored line wraps on over-long lines - it treated spaces and tabs differently - it did not make sure to really tokenize on word boundaries - it treated the equivalent stanzas "auto" and "allow-auto" differently - it ignored the fact that the "allow-*" stanzas can take multiple arguments that need to be separated to be recognized NetworkManager's processing later - it allowed "non-block" stanzas to appear before a block This patch is a rewrite of the parser to fix the issues mentioned: - it accepts the last line even if it is not terminated by "\n" - it skips over-long lines, emits a warning and even takes into account that over-long lines may be wrapped to next lines - it un-wraps wrapped lines - it uses spaces and tabs equivalently to tokenize the input - it treats "allow-auto" as a synonym to "auto" - it splits multi-argument "auto"/"allow-*" into multiple single-argument stanzas of the same type - it warns on data stanzas before the first block stanza --- .../plugins/ifupdown/interface_parser.c | 163 ++++++++++++------ 1 file changed, 108 insertions(+), 55 deletions(-) diff --git a/system-settings/plugins/ifupdown/interface_parser.c b/system-settings/plugins/ifupdown/interface_parser.c index ec9c4144a..bb00314be 100644 --- a/system-settings/plugins/ifupdown/interface_parser.c +++ b/system-settings/plugins/ifupdown/interface_parser.c @@ -73,15 +73,30 @@ void add_data(const char *key,const char *data) //printf("added data '%s' with key '%s'\n",data,key); } -#define SPACE_OR_TAB(string,ret) {ret = strchr(string,' ');ret=(ret == NULL?strchr(string,'\t'):ret);} +// join values in src with spaces into dst; dst needs to be large enough +static char *join_values_with_spaces(char *dst, char **src) +{ + if (dst != NULL) { + *dst = '\0'; + if (src != NULL && *src != NULL) { + strcat(dst, *src); + + for (src++; *src != NULL; src++) { + strcat(dst, " "); + strcat(dst, *src); + } + } + } + return(dst); +} void ifparser_init (const char *eni_file) { FILE *inp = fopen (eni_file, "r"); - int ret = 0; - char *line; - char *space; - char rline[255]; + char line[255]; + int skip_to_block = 1; + int skip_long_line = 0; + int offs = 0; if (inp == NULL) { nm_warning ("Error: Can't open %s\n", eni_file); @@ -89,69 +104,107 @@ void ifparser_init (const char *eni_file) } first = last = NULL; - while(1) + while (!feof(inp)) { - line = space = NULL; - ret = fscanf(inp,"%255[^\n]\n",rline); - if (ret == EOF) - break; - // If the line did not match, skip it - if (ret == 0) { - char *ignored; + char *token[128]; // 255 chars can only be split into 127 tokens + char value[255]; // large enough to join previously split tokens + char *safeptr; + int toknum; + int len = 0; - ignored = fgets(rline, 255, inp); + char *ptr = fgets(line+offs, 255-offs, inp); + if (ptr == NULL) + break; + + len = strlen(line); + // skip over-long lines + if (!feof(inp) && len > 0 && line[len-1] != '\n') { + if (!skip_long_line) + g_message ("Error: Skipping over-long-line '%s...'\n", line); + skip_long_line = 1; continue; } - line = rline; - while(line[0] == ' ') - line++; - if (line[0]=='#' || line[0]=='\0') + // trailing '\n' found: remove it & reset offset to 0 + if (len > 0 && line[len-1] == '\n') { + line[--len] = '\0'; + offs = 0; + } + + // if we're in long_line_skip mode, terminate it for real next line + if (skip_long_line) { + if (len == 0 || line[len-1] != '\\') + skip_long_line = 0; + continue; + } + + // unwrap wrapped lines + if (len > 0 && line[len-1] == '\\') { + offs = len - 1; + continue; + } + + //printf(">>%s<<\n", line); + +#define SPACES " \t" + // tokenize input; + for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr); + token[toknum] != NULL; + toknum++, token[toknum] = strtok_r(NULL, SPACES, &safeptr)) + ; + + // ignore comments and empty lines + if (toknum == 0 || *token[0]=='#') continue; - SPACE_OR_TAB(line,space) - if (space == NULL) - { - nm_warning ("Error: Can't parse interface line '%s'\n",line); - continue; - } - space[0] = '\0'; + if (toknum < 2) { + g_message ("Error: Can't parse interface line '%s'\n", + join_values_with_spaces(value, token)); + skip_to_block = 1; + continue; + } // There are four different stanzas: // iface, mapping, auto and allow-*. Create a block for each of them. - if (strcmp(line,"iface")==0) - { - char *space2 = strchr(space+1,' '); - if (space2 == NULL) - { - nm_warning ("Error: Can't parse iface line '%s'\n",space+1); + + // iface stanza takes at least 3 parameters + if (strcmp(token[0], "iface") == 0) { + if (toknum < 4) { + g_message ("Error: Can't parse iface line '%s'\n", + join_values_with_spaces(value, token)); continue; } - space2[0]='\0'; - add_block(line,space+1); - - if (space2[1]!='\0') - { - space = strchr(space2+1,' '); - if (space == NULL) - { - nm_warning ("Error: Can't parse data '%s'\n",space2+1); - continue; - } - space[0] = '\0'; - add_data(space2+1,space+1); - } + add_block(token[0], token[1]); + skip_to_block = 0; + add_data(token[2], join_values_with_spaces(value, token + 3)); + } + // auto and allow-auto stanzas are equivalent, + // both can take multiple interfaces as parameters: add one block for each + else if (strcmp(token[0], "auto") == 0 || + strcmp(token[0], "allow-auto") == 0) { + int i; + for (i = 1; i < toknum; i++) + add_block("auto", token[i]); + skip_to_block = 0; + } + else if (strcmp(token[0], "mapping") == 0) { + add_block(token[0], join_values_with_spaces(value, token + 1)); + skip_to_block = 0; + } + // allow-* can take multiple interfaces as parameters: add one block for each + else if (strncmp(token[0],"allow-",6) == 0) { + int i; + for (i = 1; i < toknum; i++) + add_block(token[0], token[i]); + skip_to_block = 0; + } + else { + if (skip_to_block) + g_message ("Error: ignoring out-of-block data '%s'\n", + join_values_with_spaces(value, token)); + else + add_data(token[0], join_values_with_spaces(value, token + 1)); } - else if (strcmp(line,"auto")==0) - add_block(line,space+1); - else if (strcmp(line,"mapping")==0) - add_block(line,space+1); - else if (strncmp(line,"allow-",6)==0) - add_block(line,space+1); - else - add_data(line,space+1); - - //printf("line: '%s' ret=%d\n",rline,ret); } fclose(inp); }