From 56b366441d2e5f8046e2fe967987fe03958542f6 Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Thu, 5 Oct 2017 16:17:09 +0200 Subject: vm.args check for name/sname parameter The current version of extended_bin checks if there is a name or sname parameter in vm.args and refuses to start if there is none. However, it is allowed that the vm.args file (more abstract, any -args_file that is given to erl/erlexec etc) itself may contain -args_file parameters (see http://erlang.org/doc/man/erl.html), which may contain the name/sname parameters. This change will recursively scan the files mentioned in -args_file parameters in vm.args as well as -args_file parameters in the mentioned files etcetc, and return the first occurence of a name/sname parameter. Two points are worth mentioning, though: - The name/sname check works only with absolute paths in the args_file parameters. Relative paths are probably a bad idea there, anyway, since it would make any setup rather fragile. - There is no check for circular dependencies. There was none before, and this change does not add any. --- priv/templates/extended_bin | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin index 2580dcc..e3b8f1a 100755 --- a/priv/templates/extended_bin +++ b/priv/templates/extended_bin @@ -294,8 +294,22 @@ export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" ERTS_LIB_DIR="$(dirname "$ERTS_DIR")/lib" VMARGS_PATH=$(add_path vm.args $VMARGS_PATH) -# Extract the target node name from node.args -NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH" || true) +# Extract the target node name from vm.args or referenced args_files +NAME_ARG=$(awk 'function find_name(file) { + while ((getline line0) { + if (line~/^-args_file\s+/) { + gsub(/^-args_file\s+/, "", line) + find_name(line) + } + else if (line~/^-s?name\s+/) { + print line + exit + } + } +} +{ + find_name(FILENAME) +}' "$VMARGS_PATH") # Perform replacement of variables in ${NAME_ARG} NAME_ARG=$(eval echo "${NAME_ARG}") -- cgit v1.2.3 From dfa6c7ab45304080038145433a42a69233ac1dcb Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Fri, 6 Oct 2017 12:03:11 +0200 Subject: fix for OSX awk usage of \s in awk regexp is a gawk extension. OSX comes with a different variant of awk. This fix should make the awk code POSIX-compliant and should work in all variants of awk (tested with gawk --traditional) --- priv/templates/extended_bin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin index e3b8f1a..3d876f0 100755 --- a/priv/templates/extended_bin +++ b/priv/templates/extended_bin @@ -297,11 +297,11 @@ VMARGS_PATH=$(add_path vm.args $VMARGS_PATH) # Extract the target node name from vm.args or referenced args_files NAME_ARG=$(awk 'function find_name(file) { while ((getline line0) { - if (line~/^-args_file\s+/) { - gsub(/^-args_file\s+/, "", line) + if (line~/^-args_file +/) { + gsub(/^-args_file +/, "", line) find_name(line) } - else if (line~/^-s?name\s+/) { + else if (line~/^-s?name +/) { print line exit } -- cgit v1.2.3 From e85edb20ee6badf512b7b311da401e8fa6b618f8 Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Fri, 6 Oct 2017 12:35:40 +0200 Subject: fixed indentation --- priv/templates/extended_bin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin index 3d876f0..ac9f0a5 100755 --- a/priv/templates/extended_bin +++ b/priv/templates/extended_bin @@ -296,19 +296,19 @@ ERTS_LIB_DIR="$(dirname "$ERTS_DIR")/lib" VMARGS_PATH=$(add_path vm.args $VMARGS_PATH) # Extract the target node name from vm.args or referenced args_files NAME_ARG=$(awk 'function find_name(file) { - while ((getline line0) { - if (line~/^-args_file +/) { - gsub(/^-args_file +/, "", line) - find_name(line) - } - else if (line~/^-s?name +/) { - print line - exit - } + while ((getline line0) { + if (line~/^-args_file +/) { + gsub(/^-args_file +/, "", line) + find_name(line) } + else if (line~/^-s?name +/) { + print line + exit + } + } } { - find_name(FILENAME) + find_name(FILENAME) }' "$VMARGS_PATH") # Perform replacement of variables in ${NAME_ARG} NAME_ARG=$(eval echo "${NAME_ARG}") -- cgit v1.2.3 From c0e3e93a01da2d8659bb87e80b695cc24ccd40c2 Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Mon, 16 Oct 2017 13:39:44 +0200 Subject: Augmented vm.args checks vm.args and referenced args_files will now be checked for: - non-existing -args_files - circular dependencies between -args_files - relative paths in -args_files - multiple/mixed occurences of -name and -sname parameters - missing -name or -sname parameters --- priv/templates/extended_bin | 64 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin index ac9f0a5..3822205 100755 --- a/priv/templates/extended_bin +++ b/priv/templates/extended_bin @@ -294,30 +294,70 @@ export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" ERTS_LIB_DIR="$(dirname "$ERTS_DIR")/lib" VMARGS_PATH=$(add_path vm.args $VMARGS_PATH) -# Extract the target node name from vm.args or referenced args_files -NAME_ARG=$(awk 'function find_name(file) { + +# Check vm.args and other files referenced via -args_file parameters for: +# - nonexisting -args_files +# - circular dependencies of -args_files +# - relative paths in -args_file parameters +# - multiple/mixed occurences of -name and -sname parameters +# - missing -name or -sname parameters +# If all checks pass, extract the target node name +set +e +TMP_NAME_ARG=$(awk 'function check_name(file) +{ + if (system("test -f "file)) { + print "File \""file"\" not found" + exit 1 + } while ((getline line0) { if (line~/^-args_file +/) { - gsub(/^-args_file +/, "", line) - find_name(line) + gsub(/^-args_file +| *$/, "", line) + if (!(line~/^\//)) { + print "Relative path \""line"\" encountered in "file + exit 1 + } + if (line in files) { + print "Circular reference to \""line"\" encountered in "file + exit 1 + } + files[line]=line + check_name(line) } else if (line~/^-s?name +/) { - print line - exit + if (name!="") { + print "\""line"\" found in "file" but specified before as \""name"\"" + exit 1 + } + name=line } } } + +BEGIN { + split("", files) + name="" +} + { - find_name(FILENAME) + check_name(FILENAME) + if (name=="") { + print "Need to have exactly one of either -name or -sname parameters but none found" + exit 1 + } + print name + exit 0 }' "$VMARGS_PATH") +case $? in + 0) NAME_ARG="$TMP_NAME_ARG";; + *) echo "$TMP_NAME_ARG" + exit 1;; +esac +unset TMP_NAME_ARG +set -e + # Perform replacement of variables in ${NAME_ARG} NAME_ARG=$(eval echo "${NAME_ARG}") -if [ -z "$NAME_ARG" ]; then - echo "vm.args needs to have either -name or -sname parameter." - exit 1 -fi - # Extract the name type and name from the NAME_ARG for REMSH NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')" NAME="$(echo "$NAME_ARG" | awk '{print $2}')" -- cgit v1.2.3 From f92e215ed3e88b027bbdf4bfb7260aa0305d917f Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Tue, 17 Oct 2017 16:24:27 +0200 Subject: more sophisticated args_file checks - distinction between non-existing and existing but non-readable args_file - fixed circularity check to include the base vm.args file --- priv/templates/extended_bin | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin index 3822205..1834619 100755 --- a/priv/templates/extended_bin +++ b/priv/templates/extended_bin @@ -306,27 +306,31 @@ set +e TMP_NAME_ARG=$(awk 'function check_name(file) { if (system("test -f "file)) { - print "File \""file"\" not found" - exit 1 + print file" not found" + exit 3 + } + if (system("test -r "file)) { + print file" not readable" + exit 3 } while ((getline line0) { if (line~/^-args_file +/) { gsub(/^-args_file +| *$/, "", line) if (!(line~/^\//)) { - print "Relative path \""line"\" encountered in "file - exit 1 + print "relative path "line" encountered in "file + exit 4 } if (line in files) { - print "Circular reference to \""line"\" encountered in "file - exit 1 + print "circular reference to "line" encountered in "file + exit 5 } files[line]=line check_name(line) } else if (line~/^-s?name +/) { if (name!="") { - print "\""line"\" found in "file" but specified before as \""name"\"" - exit 1 + print "\""line"\" parameter found in "file" but already specified as \""name"\"" + exit 2 } name=line } @@ -339,20 +343,23 @@ BEGIN { } { + files[FILENAME]=FILENAME check_name(FILENAME) if (name=="") { - print "Need to have exactly one of either -name or -sname parameters but none found" + print "need to have exactly one of either -name or -sname parameters but none found" exit 1 } print name exit 0 }' "$VMARGS_PATH") -case $? in +TMP_NAME_ARG_RC=$? +case $TMP_NAME_ARG_RC in 0) NAME_ARG="$TMP_NAME_ARG";; *) echo "$TMP_NAME_ARG" - exit 1;; + exit $TMP_NAME_ARG_RC;; esac unset TMP_NAME_ARG +unset TMP_NAME_ARG_RC set -e # Perform replacement of variables in ${NAME_ARG} -- cgit v1.2.3 From 42d06d64a55c6385c45dde7dc7c1ce959862df46 Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Tue, 17 Oct 2017 16:34:55 +0200 Subject: added tests for args_file checks tests now include if: - start succeeds when the node name is given in a different args file than vm.args - start fails when no node name given - start fails when multiple node names given - start fails when referenced args_file does not exist - start fails when a referenced args_file is not readable - start fails when an args_file is referenced via a relative path - start fails when there are circular dependencies between args_files --- test/rlx_extended_bin_SUITE.erl | 140 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/test/rlx_extended_bin_SUITE.erl b/test/rlx_extended_bin_SUITE.erl index a9a7df7..c2e6bc2 100644 --- a/test/rlx_extended_bin_SUITE.erl +++ b/test/rlx_extended_bin_SUITE.erl @@ -22,6 +22,13 @@ end_per_suite/1, init_per_testcase/2, all/0, + start_sname_in_other_argsfile/1, + start_fail_when_no_name/1, + start_fail_when_multiple_names/1, + start_fail_when_missing_argsfile/1, + start_fail_when_nonreadable_argsfile/1, + start_fail_when_relative_argsfile/1, + start_fail_when_circular_argsfiles/1, ping/1, shortname_ping/1, longname_ping/1, @@ -67,7 +74,10 @@ init_per_testcase(_, Config) -> {state, State1} | Config]. all() -> - [ping, shortname_ping, longname_ping, attach, pid, restart, reboot, escript, + [start_sname_in_other_argsfile, start_fail_when_no_name, start_fail_when_multiple_names, + start_fail_when_missing_argsfile, start_fail_when_nonreadable_argsfile, + start_fail_when_relative_argsfile, start_fail_when_circular_argsfiles, + ping, shortname_ping, longname_ping, attach, pid, restart, reboot, escript, remote_console, replace_os_vars, replace_os_vars_multi_node, replace_os_vars_included_config, replace_os_vars_custom_location, replace_os_vars_dev_mode, replace_os_vars_twice, custom_start_script_hooks, builtin_wait_for_vm_start_script_hook, builtin_pid_start_script_hook, @@ -1387,6 +1397,134 @@ custom_status_script(Config) -> {ok, [Status]} = file:consult(filename:join([OutputDir, "status.txt"])), {ok, {status, foo, _, foo} = Status}. +start_sname_in_other_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + VmArgs = filename:join([LibDir1, "vm.args"]), + VmArgs2 = VmArgs ++ ".2", + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + ec_file:write(VmArgs, "-args_file " ++ VmArgs2 ++ "\n\n" + "-setcookie cookie\n"), + + ec_file:write(VmArgs2, "-sname foo\n"), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + %% now start/stop the release to make sure the extended script is working + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])), + timer:sleep(2000), + {ok, "pong"} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])), + {ok, _} = sh(filename:join([OutputDir, "foo", "bin", "foo stop"])), + %% a ping should fail after stopping a node + {error, 1, _} = sh(filename:join([OutputDir, "foo", "bin", "foo ping"])). + +start_fail_when_no_name(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 1). + +start_fail_when_multiple_names(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-name foo\n\n" + "-name bar\n\n" + "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 2). + +start_fail_when_missing_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file " ++ VmArgs ++ ".nonexistent\n\n" + "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 3). + +start_fail_when_nonreadable_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + VmArgs2 = VmArgs ++ ".nonreadable", + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file " ++ VmArgs2 ++ "\n\n" + "-setcookie cookie\n"), + ec_file:write(VmArgs2, ""), + file:change_mode(VmArgs2, 8#00333), + start_fail_with_vmargs(Config, VmArgs, 3). + +start_fail_when_relative_argsfile(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file vm.args.relative\n\n" + "-setcookie cookie\n"), + start_fail_with_vmargs(Config, VmArgs, 4). + +start_fail_when_circular_argsfiles(Config) -> + LibDir1 = proplists:get_value(lib1, Config), + VmArgs = filename:join([LibDir1, "vm.args"]), + VmArgs2 = VmArgs ++ ".2", + VmArgs3 = VmArgs ++ ".3", + ec_file:write(VmArgs, "-name foo\n\n" + "-args_file " ++ VmArgs2 ++ "\n\n" + "-setcookie cookie\n"), + ec_file:write(VmArgs2, "-args_file " ++ VmArgs3 ++ "\n"), + ec_file:write(VmArgs3, "-args_file " ++ VmArgs2 ++ "\n"), + start_fail_with_vmargs(Config, VmArgs, 5). + +%%------------------------------------------------------------------- +%% Helper Function for start_fail_when_* tests +%%------------------------------------------------------------------- +start_fail_with_vmargs(Config, VmArgs, ExpectedCode) -> + LibDir1 = proplists:get_value(lib1, Config), + + rlx_test_utils:create_app(LibDir1, "goal_app", "0.0.1", [stdlib,kernel], []), + + ConfigFile = filename:join([LibDir1, "relx.config"]), + + rlx_test_utils:write_config(ConfigFile, + [{release, {foo, "0.0.1"}, + [goal_app]}, + {lib_dirs, [filename:join(LibDir1, "*")]}, + {vm_args, VmArgs}, + {generate_start_script, true}, + {extended_start_script, true} + ]), + + OutputDir = filename:join([proplists:get_value(priv_dir, Config), + rlx_test_utils:create_random_name("relx-output")]), + + {ok, _State} = relx:do([{relname, foo}, + {relvsn, "0.0.1"}, + {goals, []}, + {lib_dirs, [LibDir1]}, + {log_level, 3}, + {output_dir, OutputDir}, + {config, ConfigFile}], ["release"]), + + %% now start/stop the release to make sure the extended script is working + {error, ExpectedCode, _} = sh(filename:join([OutputDir, "foo", "bin", "foo start"])). + %%%=================================================================== %%% Helper Functions %%%=================================================================== -- cgit v1.2.3