[ClusterLabs] ocf scripts shell and local variables

Lars Ellenberg lars.ellenberg at linbit.com
Tue Aug 30 12:53:24 EDT 2016

On Tue, Aug 30, 2016 at 06:15:49PM +0200, Dejan Muhamedagic wrote:
> On Tue, Aug 30, 2016 at 10:08:00AM -0500, Dmitri Maziuk wrote:
> > On 2016-08-30 03:44, Dejan Muhamedagic wrote:
> > 
> > >The kernel reads the shebang line and it is what defines the
> > >interpreter which is to be invoked to run the script.
> > 
> > Yes, and does the kernel read when the script is source'd or executed via
> > any of the mechanisms that have the executable specified in the call,
> > explicitly or implicitly?
> I suppose that it is explained in enough detail here:
> https://en.wikipedia.org/wiki/Shebang_(Unix)
> In particular:
> https://en.wikipedia.org/wiki/Shebang_(Unix)#Magic_number
> > >None of /bin/sh RA requires bash.
> > 
> > Yeah, only "local".
> As already mentioned elsewhere in the thread, local is supported
> in most shell implementations and without it we otherwise
> wouldn't to be able to maintain software. Not sure where local
> originates, but wouldn't bet that it's bash.

Let's just agree that as currently implemented,
our collection of /bin/sh scripts won't run on ksh as shipped with
solaris (while there likely are ksh derivatives in *BSD somewhere
that would be mostly fine with them).

And before this turns even more into a "yes, I'm that old, too" thread,
may I suggest to document that we expect a
"dash compatible" /bin/sh, and that we expect scripts
to have a bash shebang (or as appropriate) if they go beyond that.

Then check for incompatible shells in ocf-shellfuncs,
and just exit early if we detect incompatibilities.

For a suggestion on checking for a proper "local" see below.
(Add more checks later, if someone feels like it.)

Though, if someone rewrites not the current agents, but the "lib/ocf*"
help stuff to be sourced by shell based agents in a way that would
support RAs in all bash, dash, ash, ksh, whatever,
and the result turns out not too much worse than what we have now,
I'd have no problem with that...



And for the "typeset" crowd,
if you think s/local/typeset/ was all that was necessary
to support function local variables in ksh, think again:

ksh -c '
	function a {
		echo "start of a: x=$x"
		typeset x=a
		echo "before b: x=$x"
		echo "end of a: x=$x"
	function b {
		echo "start of b: x=$x ### HAHA guess this one was unexpected to all but ksh users"
		typeset x=b
		echo "end of b: x=$x"
	echo "before a: x=$x"
	echo "after a: x=$x"

Try the same with bash.
Also remember that sometimes we set a "local" variable in a function
and expect it to be visible in nested functions, but also set a new
value in a nested function and expect that value to be reflected
in the outer scope (up to the last "local").

diff --git a/heartbeat/ocf-shellfuncs.in b/heartbeat/ocf-shellfuncs.in
index 6d9669d..4151630 100644
--- a/heartbeat/ocf-shellfuncs.in
+++ b/heartbeat/ocf-shellfuncs.in
@@ -920,3 +920,37 @@ ocf_is_true "$OCF_TRACE_RA" && ocf_start_trace
 if ocf_is_true "$HA_use_logd"; then
 	: ${HA_LOGD:=yes}
+# We use a lot of function local variables with the "local" keyword.
+# Which works fine with dash and bash,
+# but does not work with e.g. ksh.
+# Fail cleanly with a sane error message,
+# if the current shell does not feel compatible.
+	[ $__ocf_check_for_incompatible_shell_k = v1 ] || return 1
+	local __ocf_check_for_incompatible_shell_k=v2
+	[ $__ocf_check_for_incompatible_shell_k = v2 ] || return 1
+	return 0
+	[ $__ocf_check_for_incompatible_shell_k = v0 ] || return 1
+	local __ocf_check_for_incompatible_shell_k=v1
+	__ocf_check_for_incompatible_shell_l2
+	[ $__ocf_check_for_incompatible_shell_k = v1 ] || return 1
+	return 0
+	local __ocf_check_for_incompatible_shell_k=v0
+	__ocf_check_for_incompatible_shell_l1
+	[ $__ocf_check_for_incompatible_shell_k = v0 ] && return 0
+	ocf_exit_reason "Current shell seems to be incompatible. We suggest dash or bash (compatible)."

More information about the Users mailing list