#989628 base-files: shell profile check for bash and interactiveness

Package:
base-files
Source:
base-files
Description:
Debian base system miscellaneous files
Submitter:
Christoph Anton Mitterer
Date:
2021-06-10 20:33:03 UTC
Severity:
normal
#989628#5
Date:
2021-06-09 02:56:42 UTC
From:
To:
Hi.


(CCing Georgios M. Zarkadas from #632887 for his comments on (3)
below, if any.)



Currently /usr/share/base-files/profile has:
	if [ "${PS1-}" ]; then
	  if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then


and /usr/share/base-files/dot.profile has:
	if [ "$BASH" ]; then



I think there are several issues with that, respectively possible
improvements:


1) I'd say, both should do the same (i.e. /etc/profile and ~/.profile),
   so that should be aligned.


2) /etc/bash.bashrc already checks whether run interactively, so
   if it's just for whether /etc/bash.bashrc shall be sourced or not, the
   `if [ "${PS1-}" ]; then` is not really needed (other than avoiding the
   sourcing).

   But even then, using PS1 is IMO not optimal.
   The variable may exist in a non-interactive shell (e.g. when exported
   in a calling parent shell).
   The following:
	if [ -n "${-##*i*}" ]  ||  [ -z "${-}" ]; then
	        return
	fi
   uses the shell options which, I guess, one can assume are always right.
   The above code should be POSIXly correct and unlike some constructs
   with `case` avoid the hypothetical issue of a future option "I" being
   mistaken for "i", when bash's nocasematch option would be on.


3) The check for /bin/sh:
   - Shouldn't that also be in .profile?
   - Shouddn't it also cover the sourcing of /etc/profile.d/*.sh
     (for the same reasons)?
   - The check itself is IMO problematic:
     bash's manual talks about the "sh-like-behaviour" when invoked as
     `sh`, not necessarily as `/bin/sh`.
     Also this would break, should sh ever go to /usr/bin/sh as part of
     the usr-merge.

     So, wouldn't it be better to check e.g.:
     if [ -z "${0##*/sh}" ]  ||  [ "$0" = sh ] ; then
	#it's run as sh
     ?
     I think $0 can never be empty, so there's no need to check that it's
     actually set to something.


4) $BASH might be bogus.
   Just export BASH, and then run dash...
   Well, it's no our business to prevent users from shooting themselves,
   at least not to every extent.

   But one could do:
   if  [ -n "${BASH-}" ]   &&   { [ "$(ps -p $$ -o exe=)" = /usr/bin/bash ]  ||  [ "$(ps -p $$ -o exe=)" = /bin/bash ]; };  then

   That has the short-circuit that if BASH is not there, don't source bashrc.
   So there should be nearly no speed impact for shell scripts.

   But if it's there, the above would also check whether the executable
   is actually /usr/bin/bash or /bin/bash.
   The above is not fully POSIX-compatible (POSIX ps has no "exe").
   Calling ps twice is a bit ugly...  but I see no other way than using
   a helper variable. Maybe the `i` can be used, which is already used below
   when sourcing /etc/profile.d/*.sh .

   Also, ps isn't guaranteed to be there (procps isn't essential).
   So either one would need to check for that... or e.g. use /proc/$$/exe


Cheers,
Chris.

#989628#10
Date:
2021-06-10 20:24:05 UTC
From:
To:
Oh and I forgot:

Mine also doesn't set the PATH. This seems to be done already by
someone else (PAM?) and again, it would only work for login-shells but
not for e.g. desktop sessions or shells spawned in there.

#989628#15
Date:
2021-06-10 20:22:39 UTC
From:
To:
Taking that one back,... /etc/profile.d/*.sh is obviously meant to me
like /etc/profile, which would also get sourced when invoked as `sh`.



I myself use now the following for /etc/profile and .profile (just with
/etc/bash.bashrc replaced with ~/.bashrc:
-----------------------------------------------------------------------
#check whether any internal variables used in this script have already been set and unset them
if [ -n "${profile_p+is_set}" ]; then
	printf 'Warning: The variable `profile_p` had already been set when executing `%s` and will be unset/overwritten.\n' '~/.profile' >&2
	unset -v profile_p
fi


#source `/etc/bash.bashrc`
if [ -z "${-##*i*}" ]  &&  [ -n "$-" ]; then
	#when executed in an interactive (login) shell …


	# The existence of a non-empty variable `BASH` is merely an indicator that the
	# shell might be bash. This serves as a rapid test.
	# However, its non-existence guarantees that the shell isn’t bash.

	if  [ -n "${BASH-}" ]   &&   { [ -n "${0##*/sh}" ]  &&  [ "$0" != 'sh' ]; };  then
		#… that is (presumably) bash and not invoked as `sh` and …


		# `ps`, which is part of the non-essential package `procps`, may not be
		# available. `realpath`, which is part of the essential package `coreutils`, is
		# guaranteed to be available.

		if {  [ -x /bin/ps ]   &&   { profile_p="$( /bin/ps -o exe= -p $$ 2>/dev/null )" || profile_p='';  [ "${profile_p}" = /usr/bin/bash ]  ||  [ "${profile_p}" = /bin/bash ]; };  }    || \
		   {  [ -L /proc/$$/exe ]   &&   { profile_p="$( /usr/bin/realpath --canonicalize-existing /proc/$$/exe 2>/dev/null )" || profile_p='';  [ "${profile_p}" = /usr/bin/bash ]  ||  [ "${profile_p}" = /bin/bash ]; };  }; then
			#… it’s actually bash


			if [ -f /etc/bash.bashrc ]; then
				. /etc/bash.bashrc
			fi
		fi
	fi
fi


#source `/etc/profile.d/*.sh`
if [ -d /etc/profile.d ]; then
	for profile_p in /etc/profile.d/*.sh; do
		if [ -f "${profile_p}" ]; then
			. "${profile_p}"
		fi
	done
fi


#cleanups
unset -v profile_p
-----------------------------------------------------------------------

That also no longer sets PS1 as /etc/profile does right now. This would
anyway just get used in login shells and just for non-bash... and it
seems e.g. dash sets #/$ automatically.

But of course one could just add the `else` back above.


It also uses -f instead of -r when sourcing /etc/profile.d . I think it
makes more sense to check whether these are regular files and actually
give and error if they're not readable.


Maybe one can just drop the {...} that uses ps to find out which binary
is used by the shell - ps also needs /proc.
Also, this might not work for HURD/kFreeBSD ... so either just not
check it at all, or one might need to add some alternative way for
those.



Well, pick whatever you like :-D


Cheers,
Chris.