All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] nfs-utils: config value setting
@ 2018-06-20 12:09 Justin Mitchell
  2018-06-20 12:11 ` [PATCH 1/5] nfs-utils: Ignore empty lines in config Justin Mitchell
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:09 UTC (permalink / raw)
  To: Linux NFS Mailing list; +Cc: Steve Dickson

As part of my ongoing efforts to improve storage management integration
with NFS I offer this support for setting configuration file values.

This code only updates a single config entry at a time, it attempts to
preserve the existing structure and style of the comments file, and
takes every precaution that the replacement file is written before using
rename(2) to replace the original.

The cli nfsconf tool is also updated to be the first consumer of this
functionality.

Signed-off-by: Justin Mitchell <jumitche@redhat.com>



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/5] nfs-utils: Ignore empty lines in config
  2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
@ 2018-06-20 12:11 ` Justin Mitchell
  2018-06-20 12:12 ` [PATCH 2/5] nfs-utils: Fix comparison check for subsection headers Justin Mitchell
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:11 UTC (permalink / raw)
  To: Linux NFS Mailing list; +Cc: Steve Dickson

The test for empty lines didn't ignore whitespace properly

Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
 support/nfs/conffile.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index ee94031..d20460e 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -255,14 +255,14 @@ conf_parse_line(int trans, char *line, const char *filename, int lineno, char **
 	char *inc_section = NULL, *inc_subsection = NULL;
 	char *relpath, *subconf;
 
+	/* Strip off any leading blanks */
+	while (isspace(*line))
+		line++;
+
 	/* Ignore blank lines */
 	if (*line == '\0')
 		return;
 
-	/* Strip off any leading blanks */
-	while (isblank(*line))
-		line++;
-
 	/* Lines starting with '#' or ';' are comments.  */
 	if (*line == '#' || *line == ';')
 		return;
-- 
1.8.3.1




^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/5] nfs-utils: Fix comparison check for subsection headers
  2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
  2018-06-20 12:11 ` [PATCH 1/5] nfs-utils: Ignore empty lines in config Justin Mitchell
@ 2018-06-20 12:12 ` Justin Mitchell
  2018-06-20 12:12 ` [PATCH 3/5] nfs-utils: swap xlog_err for less fatal version Justin Mitchell
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:12 UTC (permalink / raw)
  To: Linux NFS Mailing list; +Cc: Steve Dickson

When comparing two config section headers it incorrectly
handled matching section names where only one had a
subsection value

Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
 support/nfs/conffile.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index d20460e..51b85f2 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -727,6 +727,8 @@ retry:
 			continue;
 		if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0))
 			continue;
+		if (!arg && cb->arg)
+			continue;
 		if (strcasecmp(tag, cb->tag) != 0)
 			continue;
 		if (cb->value[0] == '$') {
-- 
1.8.3.1




^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/5] nfs-utils: swap xlog_err for less fatal version
  2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
  2018-06-20 12:11 ` [PATCH 1/5] nfs-utils: Ignore empty lines in config Justin Mitchell
  2018-06-20 12:12 ` [PATCH 2/5] nfs-utils: Fix comparison check for subsection headers Justin Mitchell
@ 2018-06-20 12:12 ` Justin Mitchell
  2018-06-20 12:13 ` [PATCH 4/5] nfs-utils: Add config file writing function Justin Mitchell
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:12 UTC (permalink / raw)
  To: Linux NFS Mailing list; +Cc: Steve Dickson

Use of xlog_err is immediately fatal, switch to using
xlog(L_ERROR, ...) instead so that the error handling and
cleanup mechanisms can operate properly

Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
 support/nfs/conffile.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index 51b85f2..c7c3a62 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -495,7 +495,7 @@ conf_readfile(const char *path)
 {
 	struct stat sb;
 	if (!path) {
-		xlog_err("conf_readfile: no path given");
+		xlog(L_ERROR, "conf_readfile: no path given");
 		return NULL;
 	}
 
-- 
1.8.3.1




^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 4/5] nfs-utils: Add config file writing function
  2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
                   ` (2 preceding siblings ...)
  2018-06-20 12:12 ` [PATCH 3/5] nfs-utils: swap xlog_err for less fatal version Justin Mitchell
@ 2018-06-20 12:13 ` Justin Mitchell
  2018-06-20 12:14 ` [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool Justin Mitchell
  2018-06-25 15:38 ` [PATCH 0/5] nfs-utils: config value setting Steve Dickson
  5 siblings, 0 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:13 UTC (permalink / raw)
  To: Linux NFS Mailing list; +Cc: Steve Dickson

Adds a function to nfsconf handling to write a single config
entry, creating the file and section headers as required.

Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
 support/include/conffile.h |   1 +
 support/nfs/conffile.c     | 621 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 622 insertions(+)

diff --git a/support/include/conffile.h b/support/include/conffile.h
index bc2d61f..a3340f9 100644
--- a/support/include/conffile.h
+++ b/support/include/conffile.h
@@ -67,6 +67,7 @@ extern int      conf_match_num(const char *, const char *, int);
 extern int      conf_remove(int, const char *, const char *);
 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 *);
 
 /*
  * Convert letter from upper case to lower case
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index c7c3a62..c2dbced 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -1323,3 +1323,624 @@ cleanup:
 	}
 	return;
 }
+
+/* struct and queue for buffing output lines */
+TAILQ_HEAD(tailhead, outbuffer);
+
+struct outbuffer {
+	TAILQ_ENTRY(outbuffer) link;
+	char *text;
+};
+
+static struct outbuffer *
+make_outbuffer(char *line)
+{
+	struct outbuffer *new;
+
+	if (line == NULL)
+		return NULL;
+
+	new = calloc(1, sizeof(struct outbuffer));
+	if (new == NULL) {
+		xlog(L_ERROR, "malloc error creating outbuffer");
+		return NULL;
+	}
+	new->text = line;
+	return new;
+}
+
+/* compose a properly escaped tag=value line */
+static char *
+make_tagline(const char *tag, const char *value)
+{
+	char *line;
+	int ret;
+
+	if (!value)
+		return NULL;
+
+	if (should_escape(value))
+		ret = asprintf(&line, "%s = \"%s\"\n", tag, value);
+	else
+		ret = asprintf(&line, "%s = %s\n", tag, value);
+
+	if (ret == -1) {
+		xlog(L_ERROR, "malloc error composing a tag line");
+		return NULL;
+	}
+	return line;
+}
+
+/* compose a section header line */
+static char *
+make_section(const char *section, const char *arg)
+{
+	char *line;
+	int ret;
+
+	if (arg)
+		ret = asprintf(&line, "[%s \"%s\"]\n", section, arg);
+	else
+		ret = asprintf(&line, "[%s]\n", section);
+
+	if (ret == -1) {
+		xlog(L_ERROR, "malloc error composing section header");
+		return NULL;
+	}
+	return line;
+}
+
+/* does the supplied line contain the named section header */
+static bool
+is_section(const char *line, const char *section, const char *arg)
+{
+	char *end;
+	char *name;
+	char *sub;
+	bool found = false;
+
+	/* skip leading white space */
+	while (*line == '[' || isspace(*line))
+		line++;
+
+	name = strdup(line);
+	if (name == NULL) {
+		xlog_warn("conf_write: malloc failed ");
+		return false;
+	}
+
+	/* find the end */
+	end = strchr(name, ']');
+
+	/* malformed line */
+	if (end == NULL) {
+		xlog_warn("conf_write: warning: malformed section name");
+		goto cleanup;
+	}
+
+	while (*end && ( *end == ']' || isblank(*end)))
+		*(end--) = '\0';
+
+	/* is there a subsection name (aka arg) */
+	sub = strchr(name, '"');
+	if (sub) {
+		end = sub - 1;
+		*(sub++) = '\0';
+
+		/* trim whitespace between section name and arg */
+		while (end > name && isblank(*end))
+			*(end--) = '\0';
+
+		/* trim off closing quote */
+		end = strchr(sub, '"');
+		if (end == NULL) {
+			xlog_warn("conf_write: warning: malformed sub-section name");
+			goto cleanup;
+		}
+		*end = '\0';
+	}
+
+	/* ready to compare */
+	if (strcasecmp(section, name)!=0)
+		goto cleanup;
+
+	if (arg != NULL) {
+		if (sub == NULL || strcasecmp(arg, sub)!=0)
+			goto cleanup;
+	} else {
+		if (sub != NULL)
+			goto cleanup;
+	}
+
+	found = true;
+
+cleanup:
+	free(name);
+	return found;
+}
+
+/* check that line contains the specified tag assignment */
+static bool
+is_tag(const char *line, const char *tagname)
+{
+	char *end;
+	char *name;
+	bool found = false;
+
+	/* quick check, is this even an assignment line */
+	end = strchr(line, '=');
+	if (end == NULL)
+		return false;
+
+	/* skip leading white space before tag name */
+	while (isblank(*line))
+		line++;
+
+	name = strdup(line);
+	if (name == NULL) {
+		xlog_warn("conf_write: malloc failed");
+		return false;
+	}
+
+	/* trim any newline characters */
+	end = strchr(name, '\n');
+	if (end)
+		*end = '\0';
+	end = strchr(name, '\r');
+	if (end)
+		*end = '\0';
+
+	/* find the assignment equals sign */
+	end = strchr(name, '=');
+
+	/* malformed line, i swear the equals was there earlier */
+	if (end == NULL) {
+		xlog_warn("conf_write: warning: malformed tag name");
+		goto cleanup;
+	}
+
+	/* trim trailing whitespace after tag name */
+	do {
+		*(end--) = '\0';
+	}while (end > name && *end && isblank(*end));
+
+	/* quoted string, take contents of quotes only */
+	if (*name == '"') {
+		char * new = strdup(name+1);
+		end = strchr(new, '"');
+		if (end != NULL) {
+			*end = 0;
+			free(name);
+			name = new;
+		} else {
+			free(new);
+		}
+	}
+
+	/* now compare */
+	if (strcasecmp(tagname, name) == 0)
+		found = true;
+
+cleanup:
+	free(name);
+	return found;
+}
+
+/* is this an empty line ? */
+static bool
+is_empty(const char *line)
+{
+	const char *p = line;
+
+	if (line == NULL)
+		return true;
+	if (*line == '\0')
+		return true;
+
+	while (*p != '\0' && isspace(*p))
+		p++;
+
+	if (*p == '\0')
+		return true;
+
+	return false;
+}
+
+/* is this line just a comment ? */
+static bool
+is_comment(const char *line)
+{
+	if (line == NULL)
+		return false;
+
+	while (isblank(*line))
+		line++;
+
+	if (*line == '#')
+		return true;
+
+	return false;
+}
+
+/* delete a buffer queue whilst optionally outputting to file */
+static int
+flush_outqueue(struct tailhead *queue, FILE *fout)
+{
+	int ret = 0;
+	while (queue->tqh_first != NULL) {
+		struct outbuffer *ob = queue->tqh_first;
+		TAILQ_REMOVE(queue, ob, link);
+		if (ob->text) {
+			if (fout) {
+				ret = fprintf(fout, "%s", ob->text);
+				if (ret == -1) {
+					xlog(L_ERROR, "Error writing to config file: %s",
+						 strerror(errno));
+					fout = NULL;
+				}
+			}
+			free(ob->text);
+		}
+		free(ob);
+	}
+	if (ret == -1)
+		return 1;
+	return 0;
+}
+
+/* read one line of text from a file, growing the buffer as necessary */
+static int
+read_line(char **buff, int *buffsize, FILE *in)
+{
+	char *readp;
+	int used = 0;
+	bool again = false;
+
+	/* make sure we have a buffer to read into */
+	if (*buff == NULL) {
+		*buffsize = 4096;
+		*buff = calloc(1, *buffsize);
+		if (*buff == NULL) {
+			xlog(L_ERROR, "malloc error for read buffer");
+			return -1;
+		}
+	}
+
+	readp = *buff;
+
+	do {
+		int len;
+
+		/* read in a chunk */
+		if (fgets(readp, *buffsize-used, in)==NULL)
+			return -1;
+
+		len = strlen(*buff);
+		if (len == 0)
+			return -1;
+
+		/* was this the end of a line, or partial read */
+		readp = *buff + len - 1;
+
+		if (*readp != '\n' && *readp !='\r') {
+			/* no nl/cr must be partial read, go again */
+			readp++;
+			again = true;
+		} else {
+			/* that was a normal end of line */
+			again = false;
+		}
+
+		/* do we need more space */
+		if (again && *buffsize - len < 1024) {
+			int offset = readp - *buff;
+			char *newbuff;
+			*buffsize += 4096;
+			newbuff = realloc(*buff, *buffsize);
+			if (newbuff == NULL) {
+				xlog(L_ERROR, "malloc error reading line");
+				return -1;
+			}
+			*buff = newbuff;
+			readp = newbuff + offset;
+		}
+	} while(again);
+	return 0;
+}
+
+/* append a line to the given location in the queue */
+static int
+append_line(struct tailhead *queue, struct outbuffer *entry, char *line)
+{
+	int ret = 0;
+	char *end;
+	bool splitmode = false;
+	char *start = line;
+
+	if (line == NULL)
+		return -1;
+
+	/* if there are \n's in the middle of the string
+	 * then we need to split it into folded lines */
+	do {
+		char *thisline;
+		struct outbuffer *qbuff;
+
+		end = strchr(start, '\n');
+		if (end && *(end+1) != '\0') {
+			*end = '\0';
+
+			ret = asprintf(&thisline, "%s\\\n", start);
+			if (ret == -1) {
+				xlog(L_ERROR, "malloc error composing output");
+				return -1;
+			}
+			splitmode = true;
+			start = end+1;
+		} else {
+			end = NULL;
+			if (splitmode) {
+				thisline = strdup(start);
+				if (thisline == NULL)
+					return -1;
+			} else {
+				thisline = start;
+			}
+		}
+
+		qbuff = make_outbuffer(thisline);
+		if (qbuff == NULL)
+			return -1;
+
+		if (entry) {
+			TAILQ_INSERT_AFTER(queue, entry, qbuff, link);
+			entry = TAILQ_NEXT(entry, link);
+		} else {
+			TAILQ_INSERT_TAIL(queue, qbuff, link);
+		}
+	}while (end != NULL);
+
+	/* we malloced copies of this, so free the original */
+	if (splitmode)
+		free(line);
+
+	return 0;
+}
+
+/* is this a "folded" line, i.e. ends in backslash */
+static bool
+is_folded(const char *line)
+{
+	const char *end;
+	if (line == NULL)
+		return false;
+
+	end = line + strlen(line);
+	while (end > line) {
+		end--;
+		if (*end != '\n' && *end != '\r')
+			break;
+	}
+
+	if (*end == '\\')
+		return true;
+
+	return false;
+}
+
+/***
+ * Write a value to an nfs.conf style filename
+ *
+ * create the file if it doesnt already exist
+ * if value==NULL removes the setting (if present)
+ */
+int
+conf_write(const char *filename, const char *section, const char *arg,
+	   const char *tag, const char *value)
+{
+	int fdout = -1;
+	char *outpath = NULL;
+	FILE *outfile = NULL;
+	FILE *infile = NULL;
+	int ret = 1;
+	struct tailhead outqueue;
+	char * buff = NULL;
+	int buffsize = 0;
+
+	TAILQ_INIT(&outqueue);
+
+	if (!filename) {
+		xlog_warn("conf_write: no filename supplied");
+		return ret;
+	}
+
+	if (!section || !tag) {
+		xlog_warn("conf_write: section or tag name missing");
+		return ret;
+	}
+
+	if (asprintf(&outpath, "%s.XXXXXX", filename) == -1) {
+		xlog(L_ERROR, "conf_write: error composing temp filename");
+		return ret;
+	}
+
+	fdout = mkstemp(outpath);
+	if (fdout < 0) {
+		xlog(L_ERROR, "conf_write: open temp file %s failed: %s",
+			 outpath, strerror(errno));
+		goto cleanup;
+	}
+
+	outfile = fdopen(fdout, "w");
+	if (!outfile) {
+		xlog(L_ERROR, "conf_write: fdopen temp file failed: %s",
+			 strerror(errno));
+		goto cleanup;
+	}
+
+	infile = fopen(filename, "r");
+	if (!infile) {
+		if (!value) {
+			xlog_warn("conf_write: config file \"%s\" not found, nothing to do", filename);
+			ret = 0;
+			goto cleanup;
+		}
+
+		xlog_warn("conf_write: config file \"%s\" not found, creating.", filename);
+		if (append_line(&outqueue, NULL, make_section(section, arg)))
+			goto cleanup;
+
+		if (append_line(&outqueue, NULL, make_tagline(tag, value)))
+			goto cleanup;
+
+		if (flush_outqueue(&outqueue, outfile))
+			goto cleanup;
+	} else {
+		bool found = false;
+		int err = 0;
+
+		buffsize = 4096;
+		buff = calloc(1, buffsize);
+		if (buff == NULL) {
+			xlog(L_ERROR, "malloc error for read buffer");
+			goto cleanup;
+		}
+
+		buff[0] = '\0';
+		do {
+			struct outbuffer *where = NULL;
+
+			/* read in one section worth of lines */
+			do {
+				if (*buff != '\0') {
+					if (append_line(&outqueue, NULL, strdup(buff)))
+						goto cleanup;
+				}
+
+				err = read_line(&buff, &buffsize, infile);
+			} while (err == 0 && buff[0] != '[');
+
+			/* find the section header */
+			where = TAILQ_FIRST(&outqueue);
+			while (where != NULL) {
+				if (where->text != NULL && where->text[0] == '[')
+					break;
+				where = TAILQ_NEXT(where, link);
+			}
+
+			/* this is the section we care about */
+			if (where != NULL && is_section(where->text, section, arg)) {
+				/* is there an existing assignment */
+				while ((where = TAILQ_NEXT(where, link)) != NULL) {
+					if (is_tag(where->text, tag)) {
+						found = true;
+						break;
+					}
+				}
+
+				if (found) {
+					struct outbuffer *prev = TAILQ_PREV(where, tailhead, link);
+					bool again = false;
+
+					/* remove current tag */
+					do {
+						struct outbuffer *next = TAILQ_NEXT(where, link);
+						TAILQ_REMOVE(&outqueue, where, link);
+						if (is_folded(where->text))
+							again = true;
+						else
+							again = false;
+						free(where->text);
+						free(where);
+						where = next;
+					} while(again && where != NULL);
+
+					/* insert new tag */
+					if (value) {
+						if (append_line(&outqueue, prev, make_tagline(tag, value)))
+							goto cleanup;
+					}
+				} else
+				/* no existing assignment found and we need to add one */
+				if (value) {
+					/* rewind past blank lines and comments */
+					struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+
+					/* comments immediately before a section usually relate
+					 * to the section below them */
+					while (tail != NULL && is_comment(tail->text))
+						tail = TAILQ_PREV(tail, tailhead, link);
+
+					/* there is usually blank line(s) between sections */
+					while (tail != NULL && is_empty(tail->text))
+						tail = TAILQ_PREV(tail, tailhead, link);
+
+					/* now add the tag here */
+					if (append_line(&outqueue, tail, make_tagline(tag, value)))
+						goto cleanup;
+
+					found = true;
+				}
+			}
+
+			/* EOF and correct section not found, so add one */
+			if (err && !found && value) {
+				/* did the last section end in a blank line */
+				struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+				if (tail && !is_empty(tail->text)) {
+					/* no, so add one for clarity */
+					if (append_line(&outqueue, NULL, strdup("\n")))
+						goto cleanup;
+				}
+
+				/* add the new section header */
+				if (append_line(&outqueue, NULL, make_section(section, arg)))
+					goto cleanup;
+
+				/* now add the tag */
+				if (append_line(&outqueue, NULL, make_tagline(tag, value)))
+					goto cleanup;
+			}
+
+			/* we are done with this section, write it out */
+			if (flush_outqueue(&outqueue, outfile))
+				goto cleanup;
+		} while(err == 0);
+	}
+
+	if (infile) {
+		fclose(infile);
+		infile = NULL;
+	}
+
+	fdout = -1;
+	if (fclose(outfile)) {
+		xlog(L_ERROR, "Error writing config file: %s", strerror(errno));
+		goto cleanup;
+	}
+
+	/* now swap the old file for the new one */
+	if (rename(outpath, filename)) {
+		xlog(L_ERROR, "Error updating config file: %s: %s\n", filename, strerror(errno));
+		ret = 1;
+	} else {
+		ret = 0;
+		free(outpath);
+		outpath = NULL;
+	}
+
+cleanup:
+	flush_outqueue(&outqueue, NULL);
+
+	if (buff)
+		free(buff);
+	if (infile)
+		fclose(infile);
+	if (fdout != -1)
+		close(fdout);
+	if (outpath) {
+		unlink(outpath);
+		free(outpath);
+	}
+	return ret;
+}
-- 
1.8.3.1




^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool
  2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
                   ` (3 preceding siblings ...)
  2018-06-20 12:13 ` [PATCH 4/5] nfs-utils: Add config file writing function Justin Mitchell
@ 2018-06-20 12:14 ` Justin Mitchell
  2018-06-22  3:18   ` Calum Mackay
  2018-06-25 15:38 ` [PATCH 0/5] nfs-utils: config value setting Steve Dickson
  5 siblings, 1 reply; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:14 UTC (permalink / raw)
  To: Linux NFS Mailing list; +Cc: Steve Dickson

Use the new conf_write() function to add setting and
unsetting of config file values to the cli tool

Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
 tools/nfsconf/nfsconf.man  | 26 ++++++++++++++++
 tools/nfsconf/nfsconfcli.c | 75 +++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 90 insertions(+), 11 deletions(-)

diff --git a/tools/nfsconf/nfsconf.man b/tools/nfsconf/nfsconf.man
index 5b5e914..197caae 100644
--- a/tools/nfsconf/nfsconf.man
+++ b/tools/nfsconf/nfsconf.man
@@ -28,6 +28,25 @@ nfsconf \- Query various NFS configuration settings
 .IR subsection ]
 .IR section
 .IR tag
+.P
+.B nfsconf \-\-set
+.RB [ \-v | \-\-verbose ]
+.RB [ \-f | \-\-file
+.IR infile.conf ]
+.RB [ \-a | \-\-arg
+.IR subsection ]
+.IR section
+.IR tag
+.IR value
+.P
+.B nfsconf \-\-unset
+.RB [ \-v | \-\-verbose ]
+.RB [ \-f | \-\-file
+.IR infile.conf ]
+.RB [ \-a | \-\-arg
+.IR subsection ]
+.IR section
+.IR tag
 .SH DESCRIPTION
 The
 .B nfsconf
@@ -41,6 +60,10 @@ Output an alphabetically sorted dump of the current configuration in conf file f
 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.
+.IP "\fB\-u, \-\-unset\fP"
+Remove the specified tag and its value from the config file.
 .SH OPTIONS
 .SS Options valid in all modes
 .TP
@@ -69,6 +92,9 @@ The tool allows for easy testing of configuration values from shell scripts, her
 .TP
 .B nfsconf --file /etc/nfsmount.conf --get --arg /home MountPoint background
 Show default value for \fIbackground\fR option for NFS mounts of the \fI/home\fR path.
+.TP
+.B nfsconf --file /etc/nfs.conf --set nfsd debug 1
+Enable debugging in nfsd
 .SH FILES
 .TP
 .B /etc/nfs.conf
diff --git a/tools/nfsconf/nfsconfcli.c b/tools/nfsconf/nfsconfcli.c
index 034ec51..b5cb132 100644
--- a/tools/nfsconf/nfsconfcli.c
+++ b/tools/nfsconf/nfsconfcli.c
@@ -12,7 +12,9 @@ typedef enum {
 	MODE_NONE,
 	MODE_GET,
 	MODE_ISSET,
-	MODE_DUMP
+	MODE_DUMP,
+	MODE_SET,
+	MODE_UNSET
 } confmode_t;
 
 static void usage(const char *name)
@@ -29,11 +31,14 @@ static void usage(const char *name)
 	fprintf(stderr, "      Output one specific config value\n");
 	fprintf(stderr, "  --isset [--arg subsection] {section} {tag}\n");
 	fprintf(stderr, "      Return code indicates if config value is present\n");
+	fprintf(stderr, "  --set [--arg subsection] {section} {tag} {value}\n");
+	fprintf(stderr, "      Set and Write a config value\n");
+	fprintf(stderr, "  --unset [--arg subsection] {section} {tag}\n");
+	fprintf(stderr, "      Remove an existing config value\n");
 }
 
 int main(int argc, char **argv)
 {
-	const char * val;
 	char * confpath = NFS_CONFFILE;
 	char * arg = NULL;
 	int verbose=0;
@@ -47,6 +52,8 @@ int main(int argc, char **argv)
 		int index = 0;
 		struct option long_options[] = {
 			{"get",		no_argument, 0, 'g' },
+			{"set",		no_argument, 0, 's' },
+			{"unset",	no_argument, 0, 'u' },
 			{"arg",	  required_argument, 0, 'a' },
 			{"isset", 	no_argument, 0, 'i' },
 			{"dump",  optional_argument, 0, 'd' },
@@ -55,7 +62,7 @@ int main(int argc, char **argv)
 			{NULL,			  0, 0, 0 }
 		};
 
-		c = getopt_long(argc, argv, "ga:id::f:v", long_options, &index);
+		c = getopt_long(argc, argv, "gsua:id::f:v", long_options, &index);
 		if (c == -1) break;
 
 		switch (c) {
@@ -75,6 +82,12 @@ int main(int argc, char **argv)
 			case 'g':
 				mode = MODE_GET;
 				break;
+			case 's':
+				mode = MODE_SET;
+				break;
+			case 'u':
+				mode = MODE_UNSET;
+				break;
 			case 'i':
 				mode = MODE_ISSET;
 				break;
@@ -105,14 +118,18 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	if (conf_init_file(confpath)) {
-		/* config file was missing or had an error, warn about it */
-		if (verbose || mode != MODE_ISSET)
-			fprintf(stderr, "Error loading config file %s\n",
-				confpath);
-		/* this isnt fatal for --isset */
-		if (mode != MODE_ISSET)
-			return 1;
+	if (mode != MODE_SET && mode != MODE_UNSET) {
+		if (conf_init_file(confpath)) {
+			/* config file was missing or had an error, warn about it */
+			if (verbose || mode != MODE_ISSET) {
+				fprintf(stderr, "Error loading config file %s\n",
+					confpath);
+			}
+
+			/* this isnt fatal for --isset */
+			if (mode != MODE_ISSET)
+				return 1;
+		}
 	}
 
 	/* --dump mode, output the current configuration */
@@ -144,6 +161,7 @@ int main(int argc, char **argv)
 	if (mode == MODE_GET || mode == MODE_ISSET) {
 		char * section = NULL;
 		char * tag = NULL;
+		const char * val;
 
 		/* test they supplied section and tag names */
 		if (optind+1 >= argc) {
@@ -169,6 +187,41 @@ int main(int argc, char **argv)
 				fprintf(stderr, "Tag '%s' not found\n", tag);
 			ret = 1;
 		}
+	} else
+	if (mode == MODE_SET || mode == MODE_UNSET) {
+		char * section = NULL;
+		char * tag = NULL;
+		char * val = NULL;
+		int need = 2;
+
+		if (mode == MODE_UNSET)
+			need = 1;
+
+		/* test they supplied section and tag names */
+		if (optind+need >= argc) {
+			fprintf(stderr, "Error: insufficient arguments for mode\n");
+			usage(argv[0]);
+			ret = 2;
+			goto cleanup;
+		}
+
+		/* now we have a section and tag name */
+		section = argv[optind++];
+		tag = argv[optind++];
+		if (mode == MODE_SET)
+			val = argv[optind++];
+
+		/* setting an empty string is same as unsetting */
+		if (val!=NULL && *val == '\0') {
+			mode = MODE_UNSET;
+			val = NULL;
+		}
+
+		if (conf_write(confpath, section, arg, tag, val)) {
+			if (verbose)
+				fprintf(stderr, "Error writing config\n");
+			ret = 1;
+		}
 	} else {
 		fprintf(stderr, "Mode not yet implimented.\n");
 		ret = 2;
-- 
1.8.3.1




^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool
  2018-06-20 12:14 ` [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool Justin Mitchell
@ 2018-06-22  3:18   ` Calum Mackay
  2018-06-22  9:43     ` Justin Mitchell
  0 siblings, 1 reply; 9+ messages in thread
From: Calum Mackay @ 2018-06-22  3:18 UTC (permalink / raw)
  To: Justin Mitchell, Linux NFS Mailing list; +Cc: calum.mackay, Steve Dickson

hi Justin, it's not a part of your patch, but I noticed a tiny typo: see 
below.

On 20/06/2018 1:14 pm, Justin Mitchell wrote:
> Use the new conf_write() function to add setting and
> unsetting of config file values to the cli tool
> 
> Signed-off-by: Justin Mitchell <jumitche@redhat.com>
> ---
>   tools/nfsconf/nfsconf.man  | 26 ++++++++++++++++
>   tools/nfsconf/nfsconfcli.c | 75 +++++++++++++++++++++++++++++++++++++++-------
>   2 files changed, 90 insertions(+), 11 deletions(-)
> 
> diff --git a/tools/nfsconf/nfsconf.man b/tools/nfsconf/nfsconf.man
> index 5b5e914..197caae 100644
> --- a/tools/nfsconf/nfsconf.man
> +++ b/tools/nfsconf/nfsconf.man
> @@ -28,6 +28,25 @@ nfsconf \- Query various NFS configuration settings
>   .IR subsection ]
>   .IR section
>   .IR tag
> +.P
> +.B nfsconf \-\-set
> +.RB [ \-v | \-\-verbose ]
> +.RB [ \-f | \-\-file
> +.IR infile.conf ]
> +.RB [ \-a | \-\-arg
> +.IR subsection ]
> +.IR section
> +.IR tag
> +.IR value
> +.P
> +.B nfsconf \-\-unset
> +.RB [ \-v | \-\-verbose ]
> +.RB [ \-f | \-\-file
> +.IR infile.conf ]
> +.RB [ \-a | \-\-arg
> +.IR subsection ]
> +.IR section
> +.IR tag
>   .SH DESCRIPTION
>   The
>   .B nfsconf
> @@ -41,6 +60,10 @@ Output an alphabetically sorted dump of the current configuration in conf file f
>   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.
> +.IP "\fB\-u, \-\-unset\fP"
> +Remove the specified tag and its value from the config file.
>   .SH OPTIONS
>   .SS Options valid in all modes
>   .TP
> @@ -69,6 +92,9 @@ The tool allows for easy testing of configuration values from shell scripts, her
>   .TP
>   .B nfsconf --file /etc/nfsmount.conf --get --arg /home MountPoint background
>   Show default value for \fIbackground\fR option for NFS mounts of the \fI/home\fR path.
> +.TP
> +.B nfsconf --file /etc/nfs.conf --set nfsd debug 1
> +Enable debugging in nfsd
>   .SH FILES
>   .TP
>   .B /etc/nfs.conf
> diff --git a/tools/nfsconf/nfsconfcli.c b/tools/nfsconf/nfsconfcli.c
> index 034ec51..b5cb132 100644
> --- a/tools/nfsconf/nfsconfcli.c
> +++ b/tools/nfsconf/nfsconfcli.c
> @@ -12,7 +12,9 @@ typedef enum {
>   	MODE_NONE,
>   	MODE_GET,
>   	MODE_ISSET,
> -	MODE_DUMP
> +	MODE_DUMP,
> +	MODE_SET,
> +	MODE_UNSET
>   } confmode_t;
>   
>   static void usage(const char *name)
> @@ -29,11 +31,14 @@ static void usage(const char *name)
>   	fprintf(stderr, "      Output one specific config value\n");
>   	fprintf(stderr, "  --isset [--arg subsection] {section} {tag}\n");
>   	fprintf(stderr, "      Return code indicates if config value is present\n");
> +	fprintf(stderr, "  --set [--arg subsection] {section} {tag} {value}\n");
> +	fprintf(stderr, "      Set and Write a config value\n");
> +	fprintf(stderr, "  --unset [--arg subsection] {section} {tag}\n");
> +	fprintf(stderr, "      Remove an existing config value\n");
>   }
>   
>   int main(int argc, char **argv)
>   {
> -	const char * val;
>   	char * confpath = NFS_CONFFILE;
>   	char * arg = NULL;
>   	int verbose=0;
> @@ -47,6 +52,8 @@ int main(int argc, char **argv)
>   		int index = 0;
>   		struct option long_options[] = {
>   			{"get",		no_argument, 0, 'g' },
> +			{"set",		no_argument, 0, 's' },
> +			{"unset",	no_argument, 0, 'u' },
>   			{"arg",	  required_argument, 0, 'a' },
>   			{"isset", 	no_argument, 0, 'i' },
>   			{"dump",  optional_argument, 0, 'd' },
> @@ -55,7 +62,7 @@ int main(int argc, char **argv)
>   			{NULL,			  0, 0, 0 }
>   		};
>   
> -		c = getopt_long(argc, argv, "ga:id::f:v", long_options, &index);
> +		c = getopt_long(argc, argv, "gsua:id::f:v", long_options, &index);
>   		if (c == -1) break;
>   
>   		switch (c) {
> @@ -75,6 +82,12 @@ int main(int argc, char **argv)
>   			case 'g':
>   				mode = MODE_GET;
>   				break;
> +			case 's':
> +				mode = MODE_SET;
> +				break;
> +			case 'u':
> +				mode = MODE_UNSET;
> +				break;
>   			case 'i':
>   				mode = MODE_ISSET;
>   				break;
> @@ -105,14 +118,18 @@ int main(int argc, char **argv)
>   		return 1;
>   	}
>   
> -	if (conf_init_file(confpath)) {
> -		/* config file was missing or had an error, warn about it */
> -		if (verbose || mode != MODE_ISSET)
> -			fprintf(stderr, "Error loading config file %s\n",
> -				confpath);
> -		/* this isnt fatal for --isset */
> -		if (mode != MODE_ISSET)
> -			return 1;
> +	if (mode != MODE_SET && mode != MODE_UNSET) {
> +		if (conf_init_file(confpath)) {
> +			/* config file was missing or had an error, warn about it */
> +			if (verbose || mode != MODE_ISSET) {
> +				fprintf(stderr, "Error loading config file %s\n",
> +					confpath);
> +			}
> +
> +			/* this isnt fatal for --isset */
> +			if (mode != MODE_ISSET)
> +				return 1;
> +		}
>   	}
>   
>   	/* --dump mode, output the current configuration */
> @@ -144,6 +161,7 @@ int main(int argc, char **argv)
>   	if (mode == MODE_GET || mode == MODE_ISSET) {
>   		char * section = NULL;
>   		char * tag = NULL;
> +		const char * val;
>   
>   		/* test they supplied section and tag names */
>   		if (optind+1 >= argc) {
> @@ -169,6 +187,41 @@ int main(int argc, char **argv)
>   				fprintf(stderr, "Tag '%s' not found\n", tag);
>   			ret = 1;
>   		}
> +	} else
> +	if (mode == MODE_SET || mode == MODE_UNSET) {
> +		char * section = NULL;
> +		char * tag = NULL;
> +		char * val = NULL;
> +		int need = 2;
> +
> +		if (mode == MODE_UNSET)
> +			need = 1;
> +
> +		/* test they supplied section and tag names */
> +		if (optind+need >= argc) {
> +			fprintf(stderr, "Error: insufficient arguments for mode\n");
> +			usage(argv[0]);
> +			ret = 2;
> +			goto cleanup;
> +		}
> +
> +		/* now we have a section and tag name */
> +		section = argv[optind++];
> +		tag = argv[optind++];
> +		if (mode == MODE_SET)
> +			val = argv[optind++];
> +
> +		/* setting an empty string is same as unsetting */
> +		if (val!=NULL && *val == '\0') {
> +			mode = MODE_UNSET;
> +			val = NULL;
> +		}
> +
> +		if (conf_write(confpath, section, arg, tag, val)) {
> +			if (verbose)
> +				fprintf(stderr, "Error writing config\n");
> +			ret = 1;
> +		}
>   	} else {
>   		fprintf(stderr, "Mode not yet implimented.\n");

-> implemented

>   		ret = 2;
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool
  2018-06-22  3:18   ` Calum Mackay
@ 2018-06-22  9:43     ` Justin Mitchell
  0 siblings, 0 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-22  9:43 UTC (permalink / raw)
  To: Calum Mackay; +Cc: Linux NFS Mailing list

On Fri, 2018-06-22 at 04:18 +0100, Calum Mackay wrote:
> hi Justin, it's not a part of your patch, but I noticed a tiny typo: see 
> below.
> 
> On 20/06/2018 1:14 pm, Justin Mitchell wrote:
> >   	} else {
> >   		fprintf(stderr, "Mode not yet implimented.\n");
> 
> -> implemented

Well spotted, a quick check with the spell checker spots a few others,
so patch submitted separately.

thanks for pointing it out
-J





^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] nfs-utils: config value setting
  2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
                   ` (4 preceding siblings ...)
  2018-06-20 12:14 ` [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool Justin Mitchell
@ 2018-06-25 15:38 ` Steve Dickson
  5 siblings, 0 replies; 9+ messages in thread
From: Steve Dickson @ 2018-06-25 15:38 UTC (permalink / raw)
  To: Justin Mitchell, Linux NFS Mailing list



On 06/20/2018 08:09 AM, Justin Mitchell wrote:
> As part of my ongoing efforts to improve storage management integration
> with NFS I offer this support for setting configuration file values.
> 
> This code only updates a single config entry at a time, it attempts to
> preserve the existing structure and style of the comments file, and
> takes every precaution that the replacement file is written before using
> rename(2) to replace the original.
> 
> The cli nfsconf tool is also updated to be the first consumer of this
> functionality.
> 
> Signed-off-by: Justin Mitchell <jumitche@redhat.com>
All 5 committed... 

steved.

> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2018-06-25 15:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
2018-06-20 12:11 ` [PATCH 1/5] nfs-utils: Ignore empty lines in config Justin Mitchell
2018-06-20 12:12 ` [PATCH 2/5] nfs-utils: Fix comparison check for subsection headers Justin Mitchell
2018-06-20 12:12 ` [PATCH 3/5] nfs-utils: swap xlog_err for less fatal version Justin Mitchell
2018-06-20 12:13 ` [PATCH 4/5] nfs-utils: Add config file writing function Justin Mitchell
2018-06-20 12:14 ` [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool Justin Mitchell
2018-06-22  3:18   ` Calum Mackay
2018-06-22  9:43     ` Justin Mitchell
2018-06-25 15:38 ` [PATCH 0/5] nfs-utils: config value setting Steve Dickson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.