Message ID | 1556881021.20707.9.camel@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | nfs-utils: Enable adding of comments and date modified to nfs.conf files | expand |
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@redhat.com> 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; >
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;
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@redhat.com> --- 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(-)