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
This commit is contained in:
Peter Marschall
2010-08-12 22:41:05 -05:00
committed by Dan Williams
parent 4397f4463a
commit a5b77939fb

View File

@@ -73,15 +73,30 @@ void add_data(const char *key,const char *data)
//printf("added data '%s' with key '%s'\n",data,key); //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) void ifparser_init (const char *eni_file)
{ {
FILE *inp = fopen (eni_file, "r"); FILE *inp = fopen (eni_file, "r");
int ret = 0; char line[255];
char *line; int skip_to_block = 1;
char *space; int skip_long_line = 0;
char rline[255]; int offs = 0;
if (inp == NULL) { if (inp == NULL) {
nm_warning ("Error: Can't open %s\n", eni_file); nm_warning ("Error: Can't open %s\n", eni_file);
@@ -89,69 +104,107 @@ void ifparser_init (const char *eni_file)
} }
first = last = NULL; first = last = NULL;
while(1) while (!feof(inp))
{ {
line = space = NULL; char *token[128]; // 255 chars can only be split into 127 tokens
ret = fscanf(inp,"%255[^\n]\n",rline); char value[255]; // large enough to join previously split tokens
if (ret == EOF) char *safeptr;
break; int toknum;
// If the line did not match, skip it int len = 0;
if (ret == 0) {
char *ignored;
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; continue;
} }
line = rline; // trailing '\n' found: remove it & reset offset to 0
while(line[0] == ' ') if (len > 0 && line[len-1] == '\n') {
line++; line[--len] = '\0';
if (line[0]=='#' || line[0]=='\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; continue;
SPACE_OR_TAB(line,space) if (toknum < 2) {
if (space == NULL) g_message ("Error: Can't parse interface line '%s'\n",
{ join_values_with_spaces(value, token));
nm_warning ("Error: Can't parse interface line '%s'\n",line); skip_to_block = 1;
continue; continue;
} }
space[0] = '\0';
// There are four different stanzas: // There are four different stanzas:
// iface, mapping, auto and allow-*. Create a block for each of them. // iface, mapping, auto and allow-*. Create a block for each of them.
if (strcmp(line,"iface")==0)
{ // iface stanza takes at least 3 parameters
char *space2 = strchr(space+1,' '); if (strcmp(token[0], "iface") == 0) {
if (space2 == NULL) if (toknum < 4) {
{ g_message ("Error: Can't parse iface line '%s'\n",
nm_warning ("Error: Can't parse iface line '%s'\n",space+1); join_values_with_spaces(value, token));
continue; continue;
} }
space2[0]='\0'; add_block(token[0], token[1]);
add_block(line,space+1); skip_to_block = 0;
add_data(token[2], join_values_with_spaces(value, token + 3));
if (space2[1]!='\0') }
{ // auto and allow-auto stanzas are equivalent,
space = strchr(space2+1,' '); // both can take multiple interfaces as parameters: add one block for each
if (space == NULL) else if (strcmp(token[0], "auto") == 0 ||
{ strcmp(token[0], "allow-auto") == 0) {
nm_warning ("Error: Can't parse data '%s'\n",space2+1); int i;
continue; for (i = 1; i < toknum; i++)
} add_block("auto", token[i]);
space[0] = '\0'; skip_to_block = 0;
add_data(space2+1,space+1); }
} 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); fclose(inp);
} }