Re: [PATCH] nfs-utils: Enable adding of comments and date modified to nfs.conf files

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 5/3/19 6:57 AM, Alice J Mitchell wrote:
> Extend the nfs.conf editing code to support the inserting of comment
> lines, as well as file modified information so that automated setting
> adjustments and imports can be appropriately flagged.
> 
> Signed-off-by: Alice J Mitchell <ajmitchell@xxxxxxxxxx>
Committed... 

steved.
> ---
>  support/include/conffile.h |   2 +
>  support/nfs/conffile.c     | 195 ++++++++++++++++++++++++++++++++++++++++++++-
>  tools/nfsconf/nfsconf.man  |   7 +-
>  tools/nfsconf/nfsconfcli.c |  12 ++-
>  4 files changed, 211 insertions(+), 5 deletions(-)
> 
> diff --git a/support/include/conffile.h b/support/include/conffile.h
> index a3340f9..7d974fe 100644
> --- a/support/include/conffile.h
> +++ b/support/include/conffile.h
> @@ -69,6 +69,8 @@ extern int      conf_remove_section(int, const char *);
>  extern void     conf_report(FILE *);
>  extern int      conf_write(const char *, const char *, const char *, const char *, const char *);
>  
> +extern const char *modified_by;
> +
>  /*
>   * Convert letter from upper case to lower case
>   */
> diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
> index d8f2e8e..66d4215 100644
> --- a/support/nfs/conffile.c
> +++ b/support/nfs/conffile.c
> @@ -51,6 +51,7 @@
>  #include <syslog.h>
>  #include <libgen.h>
>  #include <sys/file.h>
> +#include <time.h>
>  
>  #include "conffile.h"
>  #include "xlog.h"
> @@ -113,6 +114,8 @@ struct conf_binding {
>  
>  LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
>  
> +const char *modified_by = NULL;
> +
>  static __inline__ uint8_t
>  conf_hash(const char *s)
>  {
> @@ -1397,6 +1400,52 @@ make_section(const char *section, const char *arg)
>  	return line;
>  }
>  
> +/* compose a comment line (with or without tag) */
> +static char *
> +make_comment(const char *tag, const char *comment)
> +{
> +	char *line;
> +	int ret;
> +
> +	if (tag == NULL || *tag == '\0') {
> +		ret = asprintf(&line, "# %s\n", comment);
> +	} else {
> +		ret = asprintf(&line, "# %s: %s\n", tag, comment);
> +	}
> +
> +	if (ret == -1) {
> +		xlog(L_ERROR, "malloc error composing header");
> +		return NULL;
> +	}
> +
> +	return line;
> +}
> +		
> +/* compose a 'file modified' comment */
> +static char *
> +make_timestamp(const char *tag, time_t when)
> +{
> +	struct tm *tstamp;
> +	char datestr[80];
> +	char *result = NULL;
> +
> +	tstamp = localtime(&when);
> +	if (strftime(datestr, sizeof(datestr), "%b %d %Y %H:%M:%S", tstamp) == 0) {
> +		xlog(L_ERROR, "error composing date");
> +		datestr[0] = '\0';
> +	}
> +
> +	if (modified_by) {
> +		char *tmpstr = NULL;
> +		asprintf(&tmpstr, "%s on %s", modified_by, datestr);
> +		result = make_comment(tag, tmpstr);
> +		free(tmpstr);
> +	} else {
> +		result = make_comment(tag, datestr);
> +	}
> +	return result;
> +}
> +
>  /* does the supplied line contain the named section header */
>  static bool
>  is_section(const char *line, const char *section, const char *arg)
> @@ -1406,6 +1455,10 @@ is_section(const char *line, const char *section, const char *arg)
>  	char *sub;
>  	bool found = false;
>  
> +	/* Not a valid section name  */
> +	if (strcmp(section, "#") == 0)
> +		return false;
> +
>  	/* skip leading white space */
>  	while (*line == '[' || isspace(*line))
>  		line++;
> @@ -1569,6 +1622,54 @@ is_comment(const char *line)
>  	return false;
>  }
>  
> +/* check that line contains the specified comment header */
> +static bool
> +is_taggedcomment(const char *line, const char *field)
> +{
> +	char *end;
> +	char *name;
> +	bool found = false;
> +
> +	if (line == NULL)
> +		return false;
> +
> +	while (isblank(*line))
> +		line++;
> +
> +	if (*line != '#')
> +		return false;
> +
> +	line++;
> +
> +	/* quick check, is this even a likely formatted line */
> +	end = strchr(line, ':');
> +	if (end == NULL)
> +		return false;
> +
> +	/* skip leading white space before field name */
> +	while (isblank(*line))
> +		line++;
> +
> +	name = strdup(line);
> +	if (name == NULL) {
> +		xlog_warn("conf_write: malloc failed");
> +		return false;
> +	}
> +
> +	/* strip trailing spaces from the name */
> +	end = strchr(name, ':');
> +	if (end) *(end--) = 0;
> +	while (end && end > name && isblank(*end))
> +		*(end--)=0;
> +
> +	if (strcasecmp(name, field)==0) 
> +		found = true;
> +
> +	free(name);
> +	return found;
> +}
> +
> +
>  /* delete a buffer queue whilst optionally outputting to file */
>  static int
>  flush_outqueue(struct tailhead *queue, FILE *fout)
> @@ -1772,6 +1873,7 @@ conf_write(const char *filename, const char *section, const char *arg,
>  	struct tailhead inqueue;
>  	char * buff = NULL;
>  	int buffsize = 0;
> +	time_t now = time(NULL);
>  
>  	TAILQ_INIT(&inqueue);
>  	TAILQ_INIT(&outqueue);
> @@ -1804,12 +1906,81 @@ conf_write(const char *filename, const char *section, const char *arg,
>  		if (lock_file(infile))
>  			goto cleanup;
>  
> -		if (append_line(&inqueue, NULL, make_section(section, arg)))
> +		if (strcmp(section, "#") == 0) {
> +			if (append_line(&inqueue, NULL, make_comment(tag, value)))
> +				goto cleanup;
> +		} else {
> +			if (append_line(&inqueue, NULL, make_section(section, arg)))
> +				goto cleanup;
> +
> +			if (append_line(&inqueue, NULL, make_tagline(tag, value)))
> +				goto cleanup;
> +		}
> +
> +		append_queue(&inqueue, &outqueue);
> +	} else 
> +	if (strcmp(section, "#") == 0) {
> +		/* Adding a comment line */
> +		struct outbuffer *where = NULL;
> +		struct outbuffer *next = NULL;
> +		bool found = false;
> +		int err = 0;
> +
> +		if (lock_file(infile))
>  			goto cleanup;
>  
> -		if (append_line(&inqueue, NULL, make_tagline(tag, value)))
> +		buffsize = 4096;
> +		buff = calloc(1, buffsize);
> +		if (buff == NULL) {
> +			xlog(L_ERROR, "malloc error for read buffer");
>  			goto cleanup;
> +		}
> +		buff[0] = '\0';
>  
> +		/* read in the file */
> +		do {
> +			if (*buff != '\0' 
> +			&& !is_taggedcomment(buff, "Modified")) {
> +				if (append_line(&inqueue, NULL, strdup(buff)))
> +					goto cleanup;
> +			}
> +
> +			err = read_line(&buff, &buffsize, infile);
> +		} while (err == 0);
> +
> +		/* if a tagged comment, look for an existing instance */
> +		if (tag && *tag != '\0') {
> +			where = TAILQ_FIRST(&inqueue);
> +			while (where != NULL) {
> +				next = TAILQ_NEXT(where, link);
> +				struct outbuffer *prev = TAILQ_PREV(where, tailhead, link);
> +				if (is_taggedcomment(where->text, tag)) {
> +					TAILQ_REMOVE(&inqueue, where, link);
> +					free(where->text);
> +					free(where);
> +					found = true;
> +					if (append_line(&inqueue, prev, make_comment(tag, value)))
> +						goto cleanup;
> +				}
> +				where = next;
> +			}
> +		}
> +		/* it wasn't tagged or we didn't find it */
> +		if (!found) {
> +			/* does the file end in a blank line or a comment */
> +			if (!TAILQ_EMPTY(&inqueue)) {
> +				struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead);
> +				if (tail && !is_empty(tail->text) && !is_comment(tail->text)) {
> +					/* no, so add one for clarity */
> +					if (append_line(&inqueue, NULL, strdup("\n")))
> +						goto cleanup;
> +				}
> +			}
> +			/* add the new comment line */
> +			if (append_line(&inqueue, NULL, make_comment(tag, value)))
> +				goto cleanup;
> +		}
> +		/* move everything over to the outqueue for writing */
>  		append_queue(&inqueue, &outqueue);
>  	} else {
>  		bool found = false;
> @@ -1831,7 +2002,8 @@ conf_write(const char *filename, const char *section, const char *arg,
>  
>  			/* read in one section worth of lines */
>  			do {
> -				if (*buff != '\0') {
> +				if (*buff != '\0' 
> +				&& !is_taggedcomment(buff, "Modified")) {
>  					if (append_line(&inqueue, NULL, strdup(buff)))
>  						goto cleanup;
>  				}
> @@ -1950,6 +2122,23 @@ conf_write(const char *filename, const char *section, const char *arg,
>  		} while(err == 0);
>  	}
>  
> +	if (modified_by) {
> +		/* check for and update the Modified header */
> +		/* does the file end in a blank line or a comment */
> +		if (!TAILQ_EMPTY(&outqueue)) {
> +			struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
> +			if (tail && !is_empty(tail->text) && !is_comment(tail->text)) {
> +				/* no, so add one for clarity */
> +				if (append_line(&outqueue, NULL, strdup("\n")))
> +					goto cleanup;
> +			}
> +		}
> +
> +		/* now append the modified date comment */
> +		if (append_line(&outqueue, NULL, make_timestamp("Modified", now)))
> +			goto cleanup;
> +	}
> +
>  	/* now rewind and overwrite the file with the updated data */
>  	rewind(infile);
>  
> diff --git a/tools/nfsconf/nfsconf.man b/tools/nfsconf/nfsconf.man
> index 1ae8543..3079198 100644
> --- a/tools/nfsconf/nfsconf.man
> +++ b/tools/nfsconf/nfsconf.man
> @@ -31,6 +31,8 @@ nfsconf \- Query various NFS configuration settings
>  .P
>  .B nfsconf \-\-set
>  .RB [ \-v | \-\-verbose ]
> +.RB [ \-m | \-\-modified
> +.IR "Modified by text" ]
>  .RB [ \-f | \-\-file
>  .IR infile.conf ]
>  .RB [ \-a | \-\-arg
> @@ -61,7 +63,7 @@ Test if a specific tag has a value set.
>  .IP "\fB\-g, \-\-get\fP"
>  Output the current value of the specified tag.
>  .IP "\fB\-s, \-\-set\fP"
> -Update or Add a tag and value to the config file, creating the file if necessary.
> +Update or Add a tag and value to the config file in a specified section, creating the tag, section, and file if necessary. If the section is defined as '#' then a comment is appended to the file. If a comment is set with a tag name then any exiting tagged comment with a matching name is replaced.
>  .IP "\fB\-u, \-\-unset\fP"
>  Remove the specified tag and its value from the config file.
>  .SH OPTIONS
> @@ -77,6 +79,9 @@ Select a different config file to operate upon, default is
>  .TP
>  .B \-a, \-\-arg \fIsubsection\fR
>  Select a specific sub-section
> +.SS Options only valid in \fB\-\-set\fR mode.
> +.B \-m, \-\-modified \fI"Modified by nfsconf"\fR
> +Set the text on the Modified date comment in the file. Set to empty to remove.
>  .SH EXIT STATUS
>  .SS \fB\-\-isset\fR mode
>  In this mode the command will return success (0) if the selected tag has a value, any other exit code indicates the value is not set, or some other error has occurred.
> diff --git a/tools/nfsconf/nfsconfcli.c b/tools/nfsconf/nfsconfcli.c
> index f98d0d1..361d386 100644
> --- a/tools/nfsconf/nfsconfcli.c
> +++ b/tools/nfsconf/nfsconfcli.c
> @@ -24,6 +24,7 @@ static void usage(const char *name)
>  	fprintf(stderr, " -v			Increase Verbosity\n");
>  	fprintf(stderr, " --file filename.conf	Load this config file\n");
>  	fprintf(stderr, "     (Default config file: " NFS_CONFFILE "\n");
> +	fprintf(stderr, " --modified \"info\"	Use \"info\" in file modified header\n");
>  	fprintf(stderr, "Modes:\n");
>  	fprintf(stderr, "  --dump [outputfile]\n");
>  	fprintf(stderr, "      Outputs the configuration to the named file\n");
> @@ -47,6 +48,8 @@ int main(int argc, char **argv)
>  
>  	confmode_t mode = MODE_NONE;
>  
> +	modified_by = "Modified by nfsconf";
> +
>  	while (1) {
>  		int c;
>  		int index = 0;
> @@ -59,10 +62,11 @@ int main(int argc, char **argv)
>  			{"dump",  optional_argument, 0, 'd' },
>  			{"file",  required_argument, 0, 'f' },
>  			{"verbose",	no_argument, 0, 'v' },
> +			{"modified", required_argument, 0, 'm' },
>  			{NULL,			  0, 0, 0 }
>  		};
>  
> -		c = getopt_long(argc, argv, "gsua:id::f:v", long_options, &index);
> +		c = getopt_long(argc, argv, "gsua:id::f:vm:", long_options, &index);
>  		if (c == -1) break;
>  
>  		switch (c) {
> @@ -99,6 +103,12 @@ int main(int argc, char **argv)
>  				mode = MODE_DUMP;
>  				dumpfile = optarg;
>  				break;
> +			case 'm':
> +				if (optarg == NULL || *optarg == 0)
> +					modified_by = NULL;
> +				else
> +					modified_by = optarg;
> +				break;
>  			default:
>  				usage(argv[0]);
>  				return 1;
> 



[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux