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);
|
//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);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user