- Package:
- base-files
- Source:
- base-files
- Description:
- Debian base system miscellaneous files
- Submitter:
- "Georgios M. Zarkadas"
- Date:
- 2024-04-15 14:39:03 UTC
- Severity:
- normal
- Tags:
This is a solution to bug #571086 (now closed) which keeps /etc/login.defs as the only place to set PATH, by computing the set there value on the fly using only grep and coreutils (both essential packages and thus guaranteed to be always present). The benefit of using it is that there is no need to sync the two PATH values; any change to /etc/login.defs will be immediately applied to subsequent logins. regards George Zarkadas - -- System Information: Debian Release: 6.0.2 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'proposed-updates'), (500, 'stable'), (450, 'testing-proposed-updates'), (450, 'testing'), (400, 'unstable') Architecture: amd64 (x86_64) Kernel: Linux 2.6.32-5-amd64 (SMP w/4 CPU cores) Locale: LANG=el_GR.utf8, LC_CTYPE=el_GR.utf8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/bash Versions of packages base-files depends on: ii gawk [awk] 1:3.1.7.dfsg-5 GNU awk, a pattern scanning and pr ii mawk [awk] 1.3.3-15 a pattern scanning and text proces base-files recommends no packages. base-files suggests no packages. - -- no debconf information iQEcBAEBAgAGBQJOFIriAAoJEJWXIVmJ5BwWy4UH/RX8WAriNj/tVBXXdlr9rfOh zvAkDynCJygZZuyKa44cA8taNzpiSnphQOgtoWufbxt6TNN637G037HJQmBGKkjy ELzT3tcO3SpEIxX+m7+QgWhbc04Or/p96Khmy5xbyAqw1bUl9XK37EAlrj3j3IyN 8aAjbvnYaT3rASqX1oxlYSdCJBcaL0RATcBP9PMvmG8VJxWDDAbUV9PfGy5PG1HA fSJtDYlcOEy71PqF5Ojr/Tbh9PUR5eVgn9sD/yqETgXgdW0O0y3VGF0j6u57eRfF PF05H9wneOxt9hkfWEdykcUdtSDo9+/9f/lZ2yzntM482EGY4vBWSEOjL5Iy4so= =/I6N -----END PGP SIGNATURE-----
pathkey=ENV_PATH
if [ "$(id -u)" -eq 0 ]
then
pathkey=ENV_SUPATH
fi
ifs=${IFS+_$IFS}
unset IFS
while read -r key val
do
case "$key" in
"$pathkey")
# the "PATH=" prefix is optional
PATH=${val#PATH=}
export PATH
;;
UMASK) # may want to handle this too
umask "$val"
;;
esac
done < /etc/login.defs
[ -z "$ifs" ] || IFS=${ifs#?}
unset ifs pathkey key val
The uid check could also be changed, to avoid a subshell and to help
with the case where 'id' isn't found in $PATH:
if [ -f /proc/1/environ ]
then
if [ -r /proc/1/environ ]
then
pathkey=ENV_SUPATH
fi
elif [ "$(id -u)" -eq 0 ]
then
pathkey=ENV_SUPATH
fi
or (with non-bash still using a subshell):
if [ "${EUID:-$(id -u)}" -eq 0 ]
Hey.
Georgios, your suggestion has some problems:
- it doesn't take care of (accidental) multiple definitions of these
settings (in which case obviously the last one wins).
- apparently variables set in login.defs may start with whitespace.
- according to login.defs(5), PATH= may be omitted, in which case your
patch breaks.
- it overwrites the PATH when already set, which IMHO it shouldn't
Michael,
- your 2nd version uses /proc, thus it wouldn't work on non-Linuxes and
therefore I don't even comment to it further.
- I'm afraid I don't understand what you do with the IFS and it seems
overly complex:
- What's "ifs=${IFS+_$IFS}" some bashism? And what if the user's
calling environment would have "ifs" and/or _$IFS already used
somehow?
Same for pathkey key val ... these don't seem like names that noone
would ever use.
- since you probably just set it for the read, why not:
IFS="" read -r ...
?
- you have the same problems as Georgios above, at least the thing
with the whitespace
I'd propose the following solution, which addresses the above problem
with only one "disadvantage":
- I call out 4 processes, but only in the case that PATH isn't already
set, which should basically never happen, as login (or maybe even PAM
on some systems) already does that for us.
PATH="${PATH:-"$( { /bin/sed -n "s/^[[:space:]]*ENV_$(if [ "$(/usr/bin/id -u)" = 0 ]; then /usr/bin/printf SU; fi)PATH[[:space:]][[:space:]]*\(\|PATH=\)\(.*\)$/\2/p" /etc/login.defs | /usr/bin/tail -n 1; } 2> /dev/null )"}"
- POSIX sh compatible
AFAICS, I also don't use any non POSIX options to the POSIX tools.
- The tools I call: sed, id, printf, tail
are all from essential+required packages, and even busybox contains
them.
- PATH isn't overwritten if already set (but it is if unset or "")
- leading whitespace accepted
- with the tail I use the last definition of the setting
- full paths to the tools are required, since the current PATH may be
bogus, unset or ""
- any errors are sent to /dev/null (but it's unlikely that this would
ever fail, probably only when the tools are not found or no memory is
left.
And even then, the worst likely thing is that an unset/"" PATH is
overwritten with a "" PATH
If Santiago likes one could even add another layer of ${:-foo} around,
which kicks in if no PATH info is found from login.defs, and then again
sets the hardcoded PATH as now... or even look at other sources first
(are there any?)
The only problem then is that one likely needs to invoke id more often.
Using a local e.g. in a function
determine_path()
{
local UID="$(/usr/bin/id -u)"
#plus more voodoo
}
wouldn't solve the problem, a) "local" is strictly speaking not POSIX,
IIRC, b) one would overwrite a function the user might have possible set
outside.
A real solution would be to don't set it, but do everything from within
a (); subshell, e.g.
PATH="$( ( \
UID="$(/usr/bin/id -u)"
#plus more voodoo
prtinf "some path in the end"
) )"
Don't forget the spaces between the braces,... otherwise it would try
arithmetic expansion.
Cheers,
Chris.
Hey.
Georgios, your suggestion has some problems:
- it doesn't take care of (accidental) multiple definitions of these
settings (in which case obviously the last one wins).
- apparently variables set in login.defs may start with whitespace.
- according to login.defs(5), PATH= may be omitted, in which case your
patch breaks.
- it overwrites the PATH when already set, which IMHO it shouldn't
Michael,
- your 2nd version uses /proc, thus it wouldn't work on non-Linuxes and
therefore I don't even comment to it further.
- I'm afraid I don't understand what you do with the IFS and it seems
overly complex:
- What's "ifs=${IFS+_$IFS}" some bashism? And what if the user's
calling environment would have "ifs" and/or _$IFS already used
somehow?
Same for pathkey key val ... these don't seem like names that noone
would ever use.
- since you probably just set it for the read, why not:
IFS="" read -r ...
?
- you have the same problems as Georgios above, at least the thing
with the whitespace
I'd propose the following solution, which addresses the above problem
with only one "disadvantage":
- I call out 4 processes, but only in the case that PATH isn't already
set, which should basically never happen, as login (or maybe even PAM
on some systems) already does that for us.
PATH="${PATH:-"$( { /bin/sed -n "s/^[[:space:]]*ENV_$(if [ "$(/usr/bin/id -u)" = 0 ]; then /usr/bin/printf SU; fi)PATH[[:space:]][[:space:]]*\(\|PATH=\)\(.*\)$/\2/p" /etc/login.defs | /usr/bin/tail -n 1; } 2> /dev/null )"}"
- POSIX sh compatible
AFAICS, I also don't use any non POSIX options to the POSIX tools.
- The tools I call: sed, id, printf, tail
are all from essential+required packages, and even busybox contains
them.
- PATH isn't overwritten if already set (but it is if unset or "")
- leading whitespace accepted
- with the tail I use the last definition of the setting
- full paths to the tools are required, since the current PATH may be
bogus, unset or ""
- any errors are sent to /dev/null (but it's unlikely that this would
ever fail, probably only when the tools are not found or no memory is
left.
And even then, the worst likely thing is that an unset/"" PATH is
overwritten with a "" PATH
If Santiago likes one could even add another layer of ${:-foo} around,
which kicks in if no PATH info is found from login.defs, and then again
sets the hardcoded PATH as now... or even look at other sources first
(are there any?)
The only problem then is that one likely needs to invoke id more often.
Using a local e.g. in a function
determine_path()
{
local UID="$(/usr/bin/id -u)"
#plus more voodoo
}
wouldn't solve the problem, a) "local" is strictly speaking not POSIX,
IIRC, b) one would overwrite a function the user might have possible set
outside.
A real solution would be to don't set it, but do everything from within
a (); subshell, e.g.
PATH="$( ( \
UID="$(/usr/bin/id -u)"
#plus more voodoo
prtinf "some path in the end"
) )"
Don't forget the spaces between the braces,... otherwise it would try
arithmetic expansion.
Cheers,
Chris.
Oh and I hope this isn't considered rude, but I think this really is a bug and not just a whislist, since it breaks the well defined behaviour of login(1) setting the PATH on most Linuxes :-) Cheers, Chris.
And sorry for the noise, but it came just something to my mind:
PATH="${PATH:-"$( { /bin/sed -n "s/^[[:space:]]*ENV_$(if [ "$(/usr/bin/id -u)" = 0 ]; then /usr/bin/printf SU; fi)PATH[[:space:]][[:space:]]*\(\|PATH=\)\(.*\)$/\2/p" /etc/login.defs | /usr/bin/tail -n 1; } 2> /dev/null )"}"
can be improved even more to:
PATH="${PATH:-"$( { PATH=/bin sed -n "s/^[[:space:]]*ENV_$(if [ "$(PATH=/usr/bin id -u)" = 0 ]; then PATH=/usr/bin printf SU; fi)PATH[[:space:]][[:space:]]*\(\|PATH=\)\(.*\)$/\2/p" /etc/login.defs | PATH=/usr/bin tail -n 1; } 2> /dev/null )"}"
The advantage is over using the full path to the programs is:
In many cases, these are implemented as built-ins, so we'd actually save
the fork and use the in-shell version.
This applies at least to busybox and bash (where printf is builtin).
Oh and yes, I use printf over echo.
I don't think printf is slower than echo, and echo is stricly speaking
deprecated by POSIX. ;)
Cheers,
Chris.
And sorry for the noise, but it came just something to my mind:
PATH="${PATH:-"$( { /bin/sed -n "s/^[[:space:]]*ENV_$(if [ "$(/usr/bin/id -u)" = 0 ]; then /usr/bin/printf SU; fi)PATH[[:space:]][[:space:]]*\(\|PATH=\)\(.*\)$/\2/p" /etc/login.defs | /usr/bin/tail -n 1; } 2> /dev/null )"}"
can be improved even more to:
PATH="${PATH:-"$( { PATH=/bin sed -n "s/^[[:space:]]*ENV_$(if [ "$(PATH=/usr/bin id -u)" = 0 ]; then PATH=/usr/bin printf SU; fi)PATH[[:space:]][[:space:]]*\(\|PATH=\)\(.*\)$/\2/p" /etc/login.defs | PATH=/usr/bin tail -n 1; } 2> /dev/null )"}"
The advantage is over using the full path to the programs is:
In many cases, these are implemented as built-ins, so we'd actually save
the fork and use the in-shell version.
This applies at least to busybox and bash (where printf is builtin).
Oh and yes, I use printf over echo.
I don't think printf is slower than echo, and echo is stricly speaking
deprecated by POSIX. ;)
Cheers,
Chris.
Absolutely no way, this is completely insane. The file /etc/profile *must* be human readable, not a collection of clever hacks designed to avoid touching /etc/profile as if it were read only. Moreover, people do not change the PATH so often, so having to change /etc/profile is not a big deal. If you insist that this is a bug, then it will remain "unfixed". (But I might better eventually close it so that everybody stop suggesting obfuscating /etc/profile) Thanks.
Absolutely no way, this is completely insane. The file /etc/profile *must* be human readable, not a collection of clever hacks designed to avoid touching /etc/profile as if it were read only. Moreover, people do not change the PATH so often, so having to change /etc/profile is not a big deal. If you insist that this is a bug, then it will remain "unfixed". (But I might better eventually close it so that everybody stop suggesting obfuscating /etc/profile) Thanks.
uhm... thank you? .... o.O
I didn't see the above to be byte-code,.. and human readable is always
relatively...
But of course I agree that my proposal is a bit more complex than the
average shell code.
OTOH, adding a comment like
should be simply enough for like 98% of all users.
And I wouldn't say it's particularly more readable to add a large set of
shell functions.
Nevertheless, it screws up in many places where the PATH is already set
from higher levels, ranging from login(1) over e.g. when one would sent
his PATH via e.g. ssh.
In the end I do not care how we call it... call it "fixing it" or
"improving it",... what's the difference?
No one calls this a "bug" just in order to blame you personally :P
But as you can see from the above examples it actually influences things
in an annoying way.
If you don't like doing it in the right way that works in all
situations, fine... but then do at least something like:
if [ -z "$PATH" ]; then
if [ "`/usr/bin/id -u`" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
fi
fi
While IMHO this is still ugly, as it introduces yet another location
where a default value for a base variable is defined in Debian (and see
all the issues we've had with /etc/environment and how long it takes us
to really fully get rid of it), it at least doesn't break things if the
PATH is already set :)
Well I don't think that closing a bug, which actually causes breakages
and for which there are simple fixes (like e.g. not setting the PATH at
all in /etc/profile, as this is not really its job) is an appropriate
way to deal with things.
Best wishes,
Chris.
Hi. Let's see what we should do to remove PATH from /etc/profile.
Ubuntu did that a lot of time ago and this is their changelog:
base-files (3.1.9ubuntu3) dapper; urgency=low
* Implement OneTruePathSpec:
* share/profile: Remove PATH setting.
* debian/control: Add dependency libpam-modules (>= 0.79-3ubuntu3) so that
the user does not end up without any $PATH at all.
So, before I go ahead, I'd like to be sure that things will not break horribly
for somebody. Do we need any kind of dependency like the one on libpam-modules
above? (It's no longer in their current debian/control file).
Can we also drop PATH from /etc/profile in non-Linux systems like hurd-i386?
(I'm Cc:ing Samuel for that).
Thanks.
Hey. I mostly forgot the details of this issue ^^ What I can say at least (which is of course not a definite answer) is, that I personally run my systems since quite some years now without touching PATH in any of .profile, .bashrc, /etc/profile, /etc/bash.bashrc. Some people want to add e.g. ~/bin to their PATH, but I think the default profile didn't do that out of the box, or did it? I personally found that (adding ~/bin/ anyway) rather unclean, as it will get inherited by all programs started by my session, but what most of the time I actually want is some additional commands or overrides for just my interactive sessions. So instead of ever adding ~/bin to the PATH, I do set up any executable in there as an alias (which won't get exported to other shells). Cheers, Chris.
Hello, Santiago Vila, le lun. 15 avril 2024 13:08:30 +0200, a ecrit: I tried to comment out the PATH definition from /etc/profile on the exodar porterbox, and it seems to be going fine? Samuel