According to rdist(1), the special and cmdspecial commands accept an
optional list of files. When the <name list> is omitted, the command
should execute for all files, otherwise it should be executed only
when one of the listed files is affected.
The special command works as expected, but cmdspecial runs for all
files regardless of <name list>.
The diff fixes cmdspecial by adding "sc_updfilelist" to the subcmd
structure to hold a list of affected files found in the <name list>
(sc_args). For cmdspecial commands without a <name list>, the module
level variable "updfilelist" is used as before.
The diff also addresses a couple of related issues, which aren't
technically necessary for the fix, but which helped with debugging and
testing, and make rdist user messages clearer. I can break them out
as separate patches if necessary.
Changes:
- Fix <name list> handling in cmdspecial commands
- Modify user message to indicate whether special or cmdspecial
commands apply to "any" file or a "list" of files
- Show cmdspecial commands that would be executed when the "verify"
option is set (rdist shows all other actions, including special
commands, when "verify" is enabled)
Test Script
-----------
I've included a shell script that demonstrates the <name list>
problem. It's attached as a diff for ease of use. I'm not suggesting
it be committed.
The script creates a Distfile with special and cmdspecial commands
with both a <name list> and no <name list> specified to highlight
the difference in handling by rdist.
The script runs the base-installed rdist twice, the first time with
two changed files, the second with just one. In both cases it executes
a simple sh script to log the env variable executed with the command
(special or cmdspecial).
If the script finds an executable rdist in /usr/src/usr.bin/rdist/, it
then runs that version with the same two test cases.
The script then displays a diff of the two logs showing that the
patched rdist executes according to the documentation, otherwise it
shows the output of the first execution to show the defective handling
of cmdspecial.
--Aaron
cvs diff: Diffing .
Index: client.c
===================================================================
RCS file: /cvs/src/usr.bin/rdist/client.c,v
retrieving revision 1.37
diff -u -p -r1.37 client.c
--- client.c 28 Jun 2019 13:35:03 -0000 1.37
+++ client.c 27 Nov 2020 14:45:16 -0000
@@ -66,7 +66,7 @@ struct namelist *updfilelist = NULL; /*
static void runspecial(char *, opt_t, char *, int);
static void addcmdspecialfile(char *, char *, int);
-static void freecmdspecialfiles(void);
+static void freecmdspecialfiles(struct namelist **);
static struct linkbuf *linkinfo(struct stat *);
static int sendhardlink(opt_t, struct linkbuf *, char *, int);
static int sendfile(char *, opt_t, struct stat *, char *, char *, int);
@@ -172,7 +172,8 @@ runspecial(char *starget, opt_t opts, ch
continue;
if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
continue;
- message(MT_CHANGE, "special \"%s\"", sc->sc_name);
+ message(MT_CHANGE, "special <%s> \"%s\"",
+ sc->sc_args == NULL ? "any" : "list", sc->sc_name);
if (IS_ON(opts, DO_VERIFY))
continue;
(void) sendcmd(C_SPECIAL,
@@ -201,11 +202,19 @@ addcmdspecialfile(char *starget, char *r
rfile = remfilename(source, Tdest, target, rname, destdir);
- for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
if (sc->sc_type != CMDSPECIAL)
continue;
- if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
+ if (sc->sc_args == NULL) {
+ isokay = TRUE;
continue;
+ } else if (!inlist(sc->sc_args, starget))
+ continue;
+ new = xmalloc(sizeof *new);
+ new->n_name = xstrdup(rfile);
+ new->n_regex = NULL;
+ new->n_next = sc->sc_updfilelist;
+ sc->sc_updfilelist = new;
isokay = TRUE;
}
@@ -222,11 +231,11 @@ addcmdspecialfile(char *starget, char *r
* Free the file list
*/
static void
-freecmdspecialfiles(void)
+freecmdspecialfiles(struct namelist **list)
{
struct namelist *ptr, *save;
- for (ptr = updfilelist; ptr; ) {
+ for (ptr = *list; ptr; ) {
if (ptr->n_name) (void) free(ptr->n_name);
save = ptr->n_next;
(void) free(ptr);
@@ -235,7 +244,7 @@ freecmdspecialfiles(void)
else
ptr = NULL;
}
- updfilelist = NULL;
+ *list = NULL;
}
/*
@@ -251,11 +260,15 @@ runcmdspecial(struct cmd *cmd, opt_t opt
for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
if (sc->sc_type != CMDSPECIAL)
continue;
- message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
+ if (sc->sc_args != NULL && sc->sc_updfilelist == NULL)
+ continue;
+ message(MT_CHANGE, "cmdspecial <%s> \"%s\"",
+ sc->sc_args == NULL ? "any" : "list", sc->sc_name);
if (IS_ON(opts, DO_VERIFY))
continue;
/* Send all the file names */
- for (f = updfilelist; f != NULL; f = f->n_next) {
+ f = sc->sc_updfilelist != NULL ? sc->sc_updfilelist :
updfilelist;
+ for (; f != NULL; f = f->n_next) {
if (first) {
(void) sendcmd(C_CMDSPECIAL, NULL);
if (response() < 0)
@@ -277,8 +290,9 @@ runcmdspecial(struct cmd *cmd, opt_t opt
while (response() > 0)
;
first = TRUE; /* Reset in case there are more CMDSPECIAL's */
+ freecmdspecialfiles(&sc->sc_updfilelist);
}
- freecmdspecialfiles();
+ freecmdspecialfiles(&updfilelist);
}
/*
@@ -1050,6 +1064,7 @@ statupdate(int u, char *starget, opt_t o
"%s: need to change to perm %#04o, owner %s,
group %s",
starget, lmode, user, group);
runspecial(starget, opts, rname, destdir);
+ addcmdspecialfile(starget, rname, destdir);
}
else {
message(MT_CHANGE, "%s: change to perm %#04o, owner %s,
group %s",
@@ -1079,6 +1094,7 @@ fullupdate(int u, char *starget, opt_t o
if (IS_ON(opts, DO_VERIFY)) {
message(MT_INFO, "%s: need to install", starget);
runspecial(starget, opts, rname, destdir);
+ addcmdspecialfile(starget, rname, destdir);
return(1);
}
if (!IS_ON(opts, DO_QUIET))
@@ -1110,6 +1126,7 @@ fullupdate(int u, char *starget, opt_t o
if (IS_ON(opts, DO_VERIFY)) {
message(MT_INFO, "%s: need to update", starget);
runspecial(starget, opts, rname, destdir);
+ addcmdspecialfile(starget, rname, destdir);
return(1);
}
if (!IS_ON(opts, DO_QUIET))
Index: client.h
===================================================================
RCS file: /cvs/src/usr.bin/rdist/client.h,v
retrieving revision 1.3
diff -u -p -r1.3 client.h
--- client.h 30 Aug 2017 07:42:52 -0000 1.3
+++ client.h 26 Nov 2020 18:14:08 -0000
@@ -84,6 +84,7 @@ struct subcmd {
opt_t sc_options;
char *sc_name;
struct namelist *sc_args;
+ struct namelist *sc_updfilelist;
struct subcmd *sc_next;
};
Index: gram.y
===================================================================
RCS file: /cvs/src/usr.bin/rdist/gram.y,v
retrieving revision 1.12
diff -u -p -r1.12 gram.y
--- gram.y 20 Jan 2015 09:00:16 -0000 1.12
+++ gram.y 27 Nov 2020 15:21:31 -0000
@@ -592,6 +592,7 @@ makesubcmd(int type)
sc->sc_type = type;
sc->sc_args = NULL;
sc->sc_next = NULL;
+ sc->sc_updfilelist = NULL;
sc->sc_name = NULL;
return(sc);
Index: test_cmdspecial
===================================================================
RCS file: test_cmdspecial
diff -N test_cmdspecial
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ test_cmdspecial 27 Nov 2020 15:44:43 -0000
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+TEST_DIR=/tmp/rdist
+rm -Rf $TEST_DIR*
+mkdir -p "$TEST_DIR/src" "$TEST_DIR/tgt"
+
+if [ -x ./rdist ]; then
+ rdist_p="$(pwd)/rdist"
+elif [ -x /usr/src/usr.bin/rdist/rdist ]; then
+ rdist_p="/usr/src/usr.bin/rdist/rdist"
+fi
+
+cd "$TEST_DIR"
+
+# Create Distfile
+cat <<- EOF > Distfile
+test:
+src -> localhost
+ install tgt ;
+ special src/file "echo special src/file env_REMFILE=\$REMFILE >> \
+rdist.log" ;
+ special "echo special any env_REMFILE=\$REMFILE >> rdist.log" ;
+ cmdspecial src/file "echo cmdspecial src/file env_FILES=\$FILES >> \
+rdist.log" ;
+ cmdspecial "echo cmdspecial any env_FILES=\$FILES >> rdist.log" ;
+EOF
+
+# Run unpatched rdist with both files modified
+# Both special and cmdspecial commands will execute
+# NOTE: the FILES variable for cmdspecial *with* a <name list>
+# will include the names of both files when it should
+# only include the affected files in the <name list>
+
+echo ""
+#echo "Unpatched rdist - update both files:" >> rdist.log
+touch -d 2020-01-01T08:00:00 src/file{,2}
+/usr/bin/rdist > /dev/null
+mv rdist{,_orgnl_both_files}.log
+
+
+# Run unpatched rdist with only file2 modified
+# The special command with no <name list> will execute as it should
+# The special command *with* a <name list> will not
+# NOTE: Both cmdspecial commands will execute
+# The cmdspecial command *with* a <name list> should
+# not execute!
+
+#echo "Unpatched rdist - update file2 only:" >> rdist.log
+touch -d 2020-01-01T09:00:00 src/file2
+/usr/bin/rdist > /dev/null
+mv rdist{,_orgnl_file2_only}.log
+
+
+if [ -n "$rdist_p" ]; then
+ rm src/* tgt/*
+
+ # Run unpatched rdist with both files modified
+ # Both special and cmdspecial commands will execute
+ # NOTE: the FILES variable for the cmdspecial *with* a
+ # <name list> will include only the specified file!
+
+ touch -d 2020-01-01T08:00:00 src/file{,2}
+ "$rdist_p" > /dev/null
+ mv rdist{,_patch_both_files}.log
+
+
+ # Run unpatched rdist with only file2 modified
+ # The special command with no <name list> executes as expected
+ # The special command *with* a <name list> will not
+ # NOTE: Only the cmdspecial command *without* a <name list>
+ # will execute, as it should
+
+ touch -d 2020-01-01T09:00:00 src/file2
+ "$rdist_p" > /dev/null
+ mv rdist{,_patch_file2_only}.log
+
+ cat <<- EOF
+ Patched rdist: $rdist_p
+
+ Diff logs when both files are affected
+ --------------------------------------
+ rdist_orgnl_both_files.log:
+ - The FILES variable for cmdspecial *with*
+ a specified <name list> shows both files!
+ rdist_patch_both_files.log:
+ - FILES shows only the expected file.
+
+ EOF
+ diff -u "$TEST_DIR/rdist_orgnl_both_files.log" \
+ "$TEST_DIR/rdist_patch_both_files.log"
+
+ cat <<- EOF
+
+ Diff logs when only one file is affected
+ ----------------------------------------
+ rdist_orgnl_file2_only.log:
+ - The FILES variable for the cmdspecial *with*
+ "src/file" in the <name list> still runs!
+ rdist_patch_file2_only.log:
+ - The cmdspecial doesn't run, as expected.
+
+ EOF
+ diff -u "$TEST_DIR/rdist_orgnl_file2_only.log" \
+ "$TEST_DIR/rdist_patch_file2_only.log"
+else
+ cat <<- EOF
+ Patched rdist not found
+
+ Log when both files are affected
+ --------------------------------------
+ rdist_orgnl_both_files.log:
+ - The FILES variable for cmdspecial *with*
+ a specified <name list> shows both files!
+
+ EOF
+ cat rdist_orgnl_both_files.log
+
+ cat <<- EOF
+
+ Log when only one file is affected
+ ----------------------------------------
+ rdist_orgnl_file2_only.log:
+ - The FILES variable for the cmdspecial *with*
+ "src/file" in the <name list> still runs!
+
+ EOF
+ cat rdist_orgnl_file2_only.log
+fi