[Dibbler-devel] Reimplementation of linux lowlevel configuration option removal functions

petr.pisar at atlas.cz petr.pisar at atlas.cz
Tue Jul 1 22:42:57 CEST 2008


Hello,

there was bug #161 (search-line in /etc/resolv.conf is corrupted at client
stop) which has been fixed. However current implementation does not deal with
other cases which can corrupt configuration files.

E.g. having search domain equaled to one of resolv.conf keywords or editing
commented data.

Also current version doesn't prevent concurent file modification (e.g. by
DHCPv4 and v6 clients).

So I implemented better parser and I changed the way how the config files are
swapped. My changes are attached as diff against CVS HEAD on 2008-06-30.

One important change is config value removal deletes all occurances of the
value. I found such behaviour usefull, especially after system crash.
Otherwise my configuration files would grow and grow.

What's your opinion?

-- Petr
-------------- next part --------------
Index: Port-linux/lowlevel-options-linux.c
===================================================================
RCS file: /var/cvs/dibbler/Port-linux/lowlevel-options-linux.c,v
retrieving revision 1.14
diff -r1.14 lowlevel-options-linux.c
15a16,17
> #include <ctype.h>
> #include <errno.h>
20a23,24
> #define MAX_LINE_LEN 511
> 
26a31,146
> /* Remove value of keyword from opened file in and the result is printed into
>  * opened file out. If removed_empty and keyword remains without argument, it
>  * will be removed too. Comments (starting with comment char) are respected.
>  * All values following keyword are removed on all lines (global remove).
>  * Returns LOWLEVEL_NO_ERROR by default, LOWLEVEL_ERROR_FILE on I/O error (and
>  * errno is set up), LOWLEVEL_ERROR_UNSPEC if value or keyword is too long.
>  * */
> int cfg_value_del(FILE *in, FILE *out, const char *keyword, const char *value,
> 	const char comment, int remove_empty) {
>     char buf[MAX_LINE_LEN+1];
> 
>     if (strlen(keyword) > MAX_LINE_LEN || strlen(value) > MAX_LINE_LEN)
> 	return(LOWLEVEL_ERROR_UNSPEC);
> 
>     errno = 0;
>     while (fgets(buf, MAX_LINE_LEN, in)) {
> 
> 	char *head;
> 	/* Skip leading white space */
> 	for (head=buf; *head!='\0' && isspace((int)(*head)); head++);
> 	/* Skip comment */
> 	if (*head!='\0' && *head!=comment) {
> 	    /* Find keyword */
> 	    if (strstr(head, keyword)==head) {
> 		head += strlen(keyword);
> 
> 		/* Skip alone keyword */
> 		if (*head!='\0' && *head!=comment && isspace((int)(*head))) {
> 		    char *keyword_end=head;
> 
> 		    /* Locate each argument */
> 		    while (*head!='\0' && *head!=comment) {
> 		    char *argument_begin=head;
> 		    /* Skip spaces before argument */
> 		    for (; *head!='\0' && isspace((int)(*head)); head++);
> 		    if (*head!='\0' && *head!=comment) {
> 			/* Compare argument to value */
> 			if (strstr(head, value)==head) {
> 			    head += strlen(value);
> 			    if (head=='\0' || *head==comment ||
> 				    isspace((int)(*head))) {
> 				/* remove this argument */
> 				/* sprinf() moves data and appends '\0' */
> 				sprintf(argument_begin, "%s", head);
> 				head=argument_begin;
> 			    }
> 			}
> 			/* Skip rest of the argument */
> 			for (; *head!='\0' && !isspace((int)(*head)); head++);
> 		    }
> 		}
> 
> 		/* Remove whole line if keyword remains without any arguments.*/
> 		if (remove_empty) {
> 		    for (head=keyword_end; *head!='\0' && isspace((int)(*head));
> 			    head++);
> 		    if (*head=='\0' || *head==comment) *buf='\0';
> 		    }
> 		}
> 	    }
> 	}
> 
> 	/* print output */
> 	if (-1 == fprintf(out, "%s", buf)) return(LOWLEVEL_ERROR_FILE);
>     }
> 
>     return((errno)?LOWLEVEL_ERROR_FILE:LOWLEVEL_NO_ERROR);
> }
> 
> 
> /* Removes value of keyword from file.
>  * It tries to do its best not to corrupt the file.
>  * Returns LOWLEVEL ERROR codes */
> int cfg_file_del(const char *file, const char *keyword, const char *value) {
>     FILE *fold, *ftmp;
>     int tmpfd;
>     int error=LOWLEVEL_NO_ERROR;
>     struct stat st;
>     char template[]="/etc/dibbler.XXXXXX";
> 
>     /* Create temporary FILE */
>     if (-1 == (tmpfd=mkstemp(template)))
> 	return(LOWLEVEL_ERROR_FILE);
> 
>     if (NULL == (ftmp=fdopen(tmpfd, "w"))) {
> 	unlink(template);
> 	close(tmpfd);
> 	return(LOWLEVEL_ERROR_FILE);
>     }
> 
>     /* Open original file */
>     if (!(fold = fopen(file, "r"))) {
> 	unlink(template);
> 	fclose(ftmp);
> 	return(LOWLEVEL_ERROR_FILE);
>     }
> 
>     /* modify configuration */
>     error = cfg_value_del(fold, ftmp, keyword, value, '#', 1);
> 
>     /* close the files */
>     if (EOF==fclose(fold)) error=LOWLEVEL_ERROR_FILE;
>     if (EOF==fclose(ftmp)) error=LOWLEVEL_ERROR_FILE;
> 
>     /* move temp file into place of the old one */
>     if (error==LOWLEVEL_NO_ERROR) {
> 	memset(&st,0,sizeof(st));
> 	if (stat(file, &st) || rename(template, file) ||
> 		chmod(file, st.st_mode))
> 	    error=LOWLEVEL_ERROR_FILE;
>     }
> 
>     return(error);
> }
> 
> 
52,74c172
<     FILE * f, *f2;
<     char buf[512];
<     int found=0;
<     struct stat st;
<     memset(&st,0,sizeof(st));
<     stat(RESOLVCONF_FILE, &st);
< 
<     unlink(RESOLVCONF_FILE".old");
<     rename(RESOLVCONF_FILE,RESOLVCONF_FILE".old");
<     f = fopen(RESOLVCONF_FILE".old","r");
<     f2 = fopen(RESOLVCONF_FILE,"w"); 
<     while (fgets(buf,511,f)) {
< 	if ( (!found) && (strstr(buf, addrPlain)) ) {
< 	    found = 1;
< 	    continue;
< 	}
< 	fprintf(f2,"%s",buf);
<     }
<     fclose(f);
<     fclose(f2);
< 
<     chmod(RESOLVCONF_FILE, st.st_mode);
<     return LOWLEVEL_NO_ERROR;
---
>     return cfg_file_del(RESOLVCONF_FILE, "nameserver", addrPlain);
122,152c220
<     FILE * f, *f2;
<     char buf[512], searchbuf[512], *ptr;
<     int found=0;
<     struct stat st;
<     memset(&st,0,sizeof(st));
<     stat(RESOLVCONF_FILE, &st);
< 
<     if (strlen(domain) >= sizeof(searchbuf)-1 )
< 	return LOWLEVEL_ERROR_UNSPEC;
<     searchbuf[0] = ' ';
<     strcpy(&(searchbuf[1]), domain);
<     unlink(RESOLVCONF_FILE".old");
<     rename(RESOLVCONF_FILE,RESOLVCONF_FILE".old");
<     if ( !(f = fopen(RESOLVCONF_FILE".old","r")) )
< 	return LOWLEVEL_ERROR_FILE;
<     if ( !(f2= fopen(RESOLVCONF_FILE,"w+")))
< 	return LOWLEVEL_ERROR_FILE;
<     while (fgets(buf,511,f)) {
< 	if ( (!found) && (ptr=strstr(buf, searchbuf)) ) {
< 	    found = 1;
< 	    strcpy(ptr, ptr+strlen(searchbuf));
< 	    if (strlen(buf)<11) /* 11=minimum length (one letter domain in 2letter top domain, e.g. "search x.pl") */
< 		continue;
< 	}
< 	fprintf(f2,"%s",buf);
<     }
<     fclose(f);
<     fclose(f2);
< 
<     chmod(RESOLVCONF_FILE,st.st_mode);
<     return LOWLEVEL_NO_ERROR;
---
>     return cfg_file_del(RESOLVCONF_FILE, "search", domain);
175,197c243
<     FILE * f, *f2;
<     char buf[512];
<     int found=0;
<     struct stat st;
<     memset(&st,0,sizeof(st));
<     stat(NTPCONF_FILE, &st);
< 
<     unlink(NTPCONF_FILE".old");
<     rename(NTPCONF_FILE, NTPCONF_FILE".old");
<     f = fopen(NTPCONF_FILE".old","r");
<     f2 = fopen(NTPCONF_FILE,"w"); 
<     while (fgets(buf,511,f)) {
< 	if ( (!found) && (strstr(buf, addrPlain)) ) {
< 	    found = 1;
< 	    continue;
< 	}
< 	fprintf(f2,"%s",buf);
<     }
<     fclose(f);
<     fclose(f2);
< 
<     chmod(NTPCONF_FILE, st.st_mode);
<     return LOWLEVEL_NO_ERROR;
---
>     return cfg_file_del(NTPCONF_FILE, "server", addrPlain);
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://klub.com.pl/pipermail/dibbler-devel/attachments/20080701/dada3fa8/attachment.pgp 


More information about the Dibbler-devel mailing list