From: Justin Mitchell <jumitche@redhat.com>
To: Linux NFS Mailing list <linux-nfs@vger.kernel.org>
Cc: Steve Dickson <SteveD@RedHat.com>
Subject: [PATCH v2 5/7] nfs-utils: tidy up output of conf_report
Date: Thu, 03 May 2018 17:50:08 +0100 [thread overview]
Message-ID: <1525366208.7550.23.camel@redhat.com> (raw)
In-Reply-To: <1525365943.7550.18.camel@redhat.com>
Previously unused conf_report() function is modified to output
an alphabetically sorted version of the current configuration
to the nominated FILE handle.
Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
support/include/conffile.h | 3 +-
support/nfs/conffile.c | 293 +++++++++++++++++++++++++++++++--------------
2 files changed, 207 insertions(+), 89 deletions(-)
diff --git a/support/include/conffile.h b/support/include/conffile.h
index 6baaf9a..bc2d61f 100644
--- a/support/include/conffile.h
+++ b/support/include/conffile.h
@@ -37,6 +37,7 @@
#include <ctype.h>
#include <stdint.h>
#include <stdbool.h>
+#include <stdio.h>
struct conf_list_node {
TAILQ_ENTRY(conf_list_node) link;
@@ -65,7 +66,7 @@ extern void conf_cleanup(void);
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(void);
+extern void conf_report(FILE *);
/*
* Convert letter from upper case to lower case
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index e65caaf..1e8034a 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -1080,117 +1080,234 @@ conf_end(int transaction, int commit)
* Configuration is "stored in reverse order", so reverse it again.
*/
struct dumper {
- char *s, *v;
+ char *section;
+ char *arg;
+ char *tag;
+ char *value;
struct dumper *next;
};
+/*
+ * Test if two nodes belong to the same (sub)sections
+ */
+static int
+dumper_section_compare(const struct dumper *nodea, const struct dumper *nodeb)
+{
+ int ret;
+
+ /* missing node, shouldnt happen */
+ if (!nodea || !nodeb)
+ return -1;
+
+ /* no section names at all, they are equal */
+ if (!nodea->section && !nodeb->section)
+ return 0;
+
+ /* if only one has a section name, the blank one goes first */
+ if (!nodea->section && nodeb->section)
+ return -1;
+
+ if (nodea->section && !nodeb->section)
+ return 1;
+
+ /* both have section names, but do they match */
+ ret = strcmp(nodea->section, nodeb->section);
+
+ /* section names differ, that was easy */
+ if (ret != 0)
+ return ret;
+
+ /* sections matched, but how about sub-sections,
+ * again, if only one has a value the blank goes first
+ */
+ if (!nodea->arg && nodeb->arg)
+ return -1;
+
+ if (nodea->arg && !nodeb->arg)
+ return 1;
+
+ /* both have sub-section args and they differ */
+ if (nodea->arg && nodeb->arg
+ && (ret=strcmp(nodea->arg, nodeb->arg))!=0)
+ return ret;
+
+ return 0;
+}
+
+/* If a string starts or ends with a space it should be quoted */
+static bool
+should_escape(const char *text)
+{
+ int len;
+
+ /* no string, no escaping needed */
+ if (!text)
+ return false;
+
+ /* first character is a space */
+ if (isspace(text[0]))
+ return true;
+
+ /* last character is a space */
+ len = strlen(text);
+ if (isspace(text[len-1]))
+ return true;
+
+ return false;
+}
+
static void
-conf_report_dump(struct dumper *node)
+conf_report_dump_text(struct dumper *head, FILE *ff)
{
- /* Recursive, cleanup when we're done. */
- if (node->next)
- conf_report_dump(node->next);
-
- if (node->v)
- xlog(LOG_INFO, "%s=\t%s", node->s, node->v);
- else if (node->s) {
- xlog(LOG_INFO, "%s", node->s);
- if (strlen(node->s) > 0)
- free(node->s);
+ const struct dumper *node = head;
+ const struct dumper *last = NULL;
+
+ for (node=head; node!=NULL; node=node->next) {
+ /* starting a new section, print the section header */
+ if (dumper_section_compare(last, node)!=0) {
+ if (node != head)
+ fprintf(ff, "\n");
+ if (node->arg)
+ fprintf(ff, "[%s \"%s\"]\n", node->section, node->arg);
+ else
+ fprintf(ff, "[%s]\n", node->section);
+ }
+
+ /* now print the tag and its value */
+ fprintf(ff, " %s", node->tag);
+ if (node->value) {
+ if (should_escape(node->value))
+ fprintf(ff, " = \"%s\"", node->value);
+ else
+ fprintf(ff, " = %s", node->value);
+ }
+ fprintf(ff, "\n");
+
+ last = node;
}
+}
- free (node);
+/* sort by tag compare function */
+static int
+dumper_compare(const void *a, const void *b)
+{
+ const struct dumper *nodea = *(struct dumper **)a;
+ const struct dumper *nodeb = *(struct dumper **)b;
+ int ret;
+
+ /* missing node, shouldnt happen */
+ if (!nodea || !nodeb)
+ return -1;
+
+ /* are the two nodes in different (sub)sections */
+ ret = dumper_section_compare(nodea, nodeb);
+ if (ret != 0)
+ return ret;
+
+ /* sub-sections match (both blank, or both same)
+ * so we compare the tag names
+ */
+
+ /* blank tags shouldnt happen, but paranoia */
+ if (!nodea->tag && !nodeb->tag)
+ return 0;
+
+ /* still shouldnt happen, but use the blank-goes-first logic */
+ if (!nodea->tag && nodeb->tag)
+ return -1;
+ if ( nodea->tag && !nodeb->tag)
+ return 1;
+
+ /* last test, compare the tags directly */
+ ret = strcmp(nodea->tag, nodeb->tag);
+ return ret;
}
-void
-conf_report (void)
+/* sort all of the report nodes */
+static struct dumper *
+conf_report_sort(struct dumper *start)
{
- struct conf_binding *cb, *last = 0;
- unsigned int i, len, diff_arg = 0;
- char *current_section = (char *)0;
- char *current_arg = (char *)0;
- struct dumper *dumper, *dnode;
+ struct dumper **list;
+ struct dumper *node;
+ unsigned int count = 0;
+ unsigned int i=0;
+
+ /* how long is this list */
+ for (node=start; node!=NULL; node=node->next)
+ count++;
+
+ /* no need to sort a list with less than 2 items */
+ if (count < 2)
+ return start;
+
+ /* build an array of all the nodes */
+ list = calloc(count, sizeof(struct dumper *));
+ if (!list)
+ goto mem_err;
+
+ for (node=start,i=0; node!=NULL; node=node->next) {
+ list[i++] = node;
+ }
+
+ /* sort the array alphabetically by section and tag */
+ qsort(list, count, sizeof(struct dumper *), dumper_compare);
+
+ /* rebuild the linked list in sorted order */
+ for (i=0; i<count-1; i++) {
+ list[i]->next = list[i+1];
+ }
+ list[count-1]->next = NULL;
+
+ /* remember the new head of list and discard the sorting array */
+ node = list[0];
+ free(list);
- dumper = dnode = (struct dumper *)calloc(1, sizeof *dumper);
- if (!dumper)
- goto mem_fail;
+ /* return the new head of list */
+ return node;
+
+mem_err:
+ free(list);
+ return NULL;
+}
+
+/* Output a copy of the current configuration to file */
+void
+conf_report(FILE *outfile)
+{
+ struct conf_binding *cb = NULL;
+ unsigned int i;
+ struct dumper *dumper = NULL, *dnode = NULL;
xlog(LOG_INFO, "conf_report: dumping running configuration");
- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
+ /* build a linked list of all the config nodes */
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) {
- if (!cb->is_default) {
- /* Make sure the Section arugment is the same */
- if (current_arg && current_section && cb->arg) {
- if (strcmp(cb->section, current_section) == 0 &&
- strcmp(cb->arg, current_arg) != 0)
- diff_arg = 1;
- }
- /* Dump this entry. */
- if (!current_section || strcmp(cb->section, current_section)
- || diff_arg) {
- if (current_section || diff_arg) {
- len = strlen (current_section) + 3;
- if (current_arg)
- len += strlen(current_arg) + 3;
- dnode->s = malloc(len);
- if (!dnode->s)
- goto mem_fail;
-
- if (current_arg)
- snprintf(dnode->s, len, "[%s \"%s\"]",
- current_section, current_arg);
- else
- snprintf(dnode->s, len, "[%s]", current_section);
-
- dnode->next =
- (struct dumper *)calloc(1, sizeof (struct dumper));
- dnode = dnode->next;
- if (!dnode)
- goto mem_fail;
-
- dnode->s = "";
- dnode->next =
- (struct dumper *)calloc(1, sizeof (struct dumper));
- dnode = dnode->next;
- if (!dnode)
- goto mem_fail;
- }
- current_section = cb->section;
- current_arg = cb->arg;
- diff_arg = 0;
- }
- dnode->s = cb->tag;
- dnode->v = cb->value;
- dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
- dnode = dnode->next;
- if (!dnode)
- goto mem_fail;
- last = cb;
+ struct dumper *newnode = calloc(1, sizeof (struct dumper));
+ if (!newnode)
+ goto mem_fail;
+
+ newnode->next = dumper;
+ dumper = newnode;
+
+ newnode->section = cb->section;
+ newnode->arg = cb->arg;
+ newnode->tag = cb->tag;
+ newnode->value = cb->value;
}
}
- if (last) {
- len = strlen(last->section) + 3;
- if (last->arg)
- len += strlen(last->arg) + 3;
- dnode->s = malloc(len);
- if (!dnode->s)
- goto mem_fail;
- if (last->arg)
- snprintf(dnode->s, len, "[%s \"%s\"]", last->section, last->arg);
- else
- snprintf(dnode->s, len, "[%s]", last->section);
- }
- conf_report_dump(dumper);
- return;
+ /* sort the list then print it */
+ dumper = conf_report_sort(dumper);
+ conf_report_dump_text(dumper, outfile);
+ goto cleanup;
mem_fail:
xlog_warn("conf_report: malloc/calloc failed");
+cleanup:
+ /* traverse the linked list freeing all the nodes */
while ((dnode = dumper) != 0) {
dumper = dumper->next;
- if (dnode->s)
- free(dnode->s);
free(dnode);
}
return;
--
1.8.3.1
next prev parent reply other threads:[~2018-05-03 16:50 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-05-03 16:45 [PATCH v2 0/7] nfs-utils: nfsconf cli tool and code tests Justin Mitchell
2018-05-03 16:47 ` [PATCH v2 1/7] nfs-utils: Fix minor memory leaks Justin Mitchell
2018-05-03 16:48 ` [PATCH v2 2/7] nfs-utils: Make config includes relative to current config Justin Mitchell
2018-05-03 16:48 ` [PATCH v2 3/7] nfs-utils: Use config file name in error messages Justin Mitchell
2018-05-03 16:49 ` [PATCH v2 4/7] nfs-utils: Indicate if config file was missing Justin Mitchell
2018-05-03 16:50 ` Justin Mitchell [this message]
2018-05-03 16:50 ` [PATCH v2 6/7] nfs-utils: Add nfsconftool cli Justin Mitchell
2018-05-03 16:51 ` [PATCH v2 7/7] nfs-utils: use nfsconftool cli to test library function Justin Mitchell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1525366208.7550.23.camel@redhat.com \
--to=jumitche@redhat.com \
--cc=SteveD@RedHat.com \
--cc=linux-nfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).