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:

committed by
Dan Williams

parent
4397f4463a
commit
a5b77939fb
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user