romkatv / zsh4humans

A turnkey configuration for Zsh
MIT License
1.71k stars 112 forks source link

/etc/profile not sourced #252

Closed VorpalBlade closed 1 year ago

VorpalBlade commented 1 year ago

It appears when using z4h that /etc/profile is not sourced. This causes /etc/profile.d not to be sourced, which means things like /etc/locale.conf and debuginfod URIs are not set up.

On a graphical login (KDE) these files are still sources (and then the environment inherited by graphical terminals). But when logging in on the virtual terminals (or when using su -), they appear to be ignored.

On Arch Linux (which I use) the setup is as follows:

~ ❯ cat /etc/zsh/zprofile                        
emulate sh -c 'source /etc/profile'
~ ❯ cat /etc/profile      
# /etc/profile

# Set our umask
umask 022

# [ cut out for brevity ]

# Load profiles from /etc/profile.d
if test -d /etc/profile.d/; then
    for profile in /etc/profile.d/*.sh; do
        test -r "$profile" && . "$profile"
    done
    unset profile
fi

# [...]
~ ❯ ls /etc/profile.d/                                           
debuginfod.csh     flatpak.sh    gawk.sh  jre.sh                 libreoffice-fresh.sh  opencascade.sh  vte.csh
debuginfod.sh      freetype2.sh  gpm.sh   lesspipe.sh            locale.sh             perlbin.csh     vte.sh
flatpak-bindir.sh  gawk.csh      jre.csh  libreoffice-fresh.csh  mercurial.sh          perlbin.sh

There are several of these I care about being sourced (such as locale and debuginfod) which sets relevant environment variables, as well as some that extend PATH, and similar. There are others (like vte.sh and gawk.sh) that are more questionable.

Is this a z4h bug? Is it intentional? What is the preferred solution to this when using z4h? It is kind of hard to manually keep track of which ones I still want to source (and are relevant), and that might change over time, especially on a rolling release distro like Arch.

romkatv commented 1 year ago

z4h does not source global rc files by design. If you want to source /etc/zsh/zprofile, add ~/.zprofile with the following content:

z4h source /etc/zsh/zprofile
romkatv commented 1 year ago

FWIW, I would strongly recommend NOT sourcing /etc/zsh/zprofile. If there are individual files in there that you want, either source them directly or (better yet), copy the content you care about into your own user rc files. The latter usually allows you to remove the crap that inevitably accompanies the rare useful bits found in global rc files. You can often make the code orders of magnitude faster, too.

VorpalBlade commented 1 year ago

Thanks for the quick reply! After looking through those files I think a better approach is to pick and choose which parts I want, as some (such as vte.sh) seem highly problematic when using z4h.

Doing that is not completely trivial though (this is mostly for the benefit of anyone else who runs into this and finds this bug):

romkatv commented 1 year ago
  • Several of them depend on the function append_path defined in /etc/profile which is unset before /etc/profile returns. append_path basically ensures there are no duplicates in path (so mostly for the benefit of bash). Still an analog needs to be provided to allow sourcing those files individually.

You can modify path like this:

# Append.
path+=(/some/dir)
# Prepend.
path=(/some/dir $path)

There won't be dups.

You can also add dirs only if they exist, which is nice for performance. Like this:

path+=(/some/dir(/N))

z4h already has all the useful bits of vte.sh. As usual, a rewrite makes it 1000 times faster and avoids silly bugs.

VorpalBlade commented 1 year ago

The problem with copying things into my own files is that some of that stuff is distro specific. Since I use the same z4h setup (using chezmoi) on multiple distros (Arch and Debian), that would complicate things. And then there is the issue of keeping up with future updates of course.

Currently I'm thinking something like this could work:

append_path() { PATH="$PATH:$1" }
for profile_part in debuginfod.sh locale.sh; do
    if [[ -f /etc/profile.d/$profile_part ]]; then
        emulate sh -c "source /etc/profile.d/$profile_part"
    fi
done
unset append_path profile_part
romkatv commented 1 year ago

It's virtually never distro specific. If you can show me a specific example, I might be able to show how to write it in zsh.

VorpalBlade commented 1 year ago

It appears that locale.sh (which caused the original bug I ran into with incorrect locales in root shells) is distro specific. One solution (and perhaps the cleanest one) is of course to not use /etc/locale.conf but just set the locale I care about directly in the zshprofile.

~ ❯ cat /etc/profile.d/locale.sh       
#!/bin/sh

# load locale.conf in XDG paths.
# /etc/locale.conf loads and overrides by kernel command line is done by systemd
# But we override it here, see FS#56688
if [ -z "$LANG" ]; then
  if [ -n "$XDG_CONFIG_HOME" ] && [ -r "$XDG_CONFIG_HOME/locale.conf" ]; then
    . "$XDG_CONFIG_HOME/locale.conf"
  elif [ -n "$HOME" ] && [ -r "$HOME/.config/locale.conf" ]; then
    . "$HOME/.config/locale.conf"
  elif [ -r /etc/locale.conf ]; then
    . /etc/locale.conf
  fi
fi

# define default LANG to C if not already defined
LANG=${LANG:-C}

# export all locale (7) variables when they exist
export LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY \
       LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT \
       LC_IDENTIFICATION
~ ❯ pacman -Qo /etc/profile.d/locale.sh 
/etc/profile.d/locale.sh is owned by filesystem 2022.10.18-1

Most other things (except debuginfod and locale) are only relevant in GUI sessions to me, and there it seems it is already sourced somehow by either the login manager SDDM or KDE.

VorpalBlade commented 1 year ago

Hm, exporting LC_* from .zprofile does not seem to work for some weird reason. Only LANG seems to take effect.

export LANG=en_GB.UTF-8
export LC_NUMERIC=sv_SE.UTF-8
export LC_TIME=sv_SE.UTF-8
export LC_MONETARY=sv_SE.UTF-8
export LC_PAPER=sv_SE.UTF-8
export LC_MEASUREMENT=sv_SE.UTF-8

Does z4h mess with locales?

EDIT: More specifically something sets LC_ALL=C.UTF-8 when I log into a vt.

romkatv commented 1 year ago

EDIT: More specifically something sets LC_ALL=C.UTF-8 when I log into a vt.

If the current locale isn't UTF-8, z4h will set LC_ALL to a UTF-8 one.

It appears that locale.sh (which caused the original bug I ran into with incorrect locales in root shells) is distro specific.

This should do it:

[[ -n $LANG ]] || z4h source /etc/locale.conf
unset LC_ALL
romkatv commented 1 year ago

C.UTF-8 is the best locale for the terminal for tech-minded people. It's the most "international" w.r.t. number formatting, dates, currency, etc. When posting the output of tools and asking others for help, it's best if that output is made with C.UTF-8 locale.

VorpalBlade commented 1 year ago

Ah, maybe it gets confused by my slightly complicated UTF-8 locale. LANG is set, and then I use some LC_* overrides.

Your suggestion doesn't do the right thing though, because locale.conf does not contain export (so it needs to be followed by some export commands.

As for C.UTF-8 being best. Yes, to some extent. I still need A4 (not letter paper), so LC_PAPER must be set to something European if I recall correctly. I prefer non-american date formats (who in their right mind uses middle endianness for the month!?), and so on. And weeks should start on Monday, not Sunday of course.

So there is an argument for setting LANG=C.UTF-8 and using various LC_* overrides. But I do prefer British spelling. :)

VorpalBlade commented 1 year ago

Thanks for the quick help even through z4h is on minimal maintenance mode!

VorpalBlade commented 1 year ago

I'm opening a separate issue that it is LANG that should be set, not LC_ALL though. After reading through man 7 locale this seems like the right option.

romkatv commented 1 year ago

Ah, maybe it gets confused by my slightly complicated UTF-8 locale. LANG is set, and then I use some LC_* overrides.

Maybe it gets confused but it's more likely that whatever you set results in non-utf-8 encoding. You can check with this command:

zmodload zsh/langinfo && print -r -- ${langinfo[CODESET]}

Your suggestion doesn't do the right thing though, because locale.conf does not contain export (so it needs to be followed by some export commands.

You can use z4h source /etc/profile.d/locale.sh. It's decent.

I'm opening a separate issue that it is LANG that should be set, not LC_ALL though. After reading through man 7 locale this seems like the right option.

LANG won't override whatever parameters are setting a non-utf-8 locale. Having a non-utf-8 locale is a problem of much greater proportion than date formatting, so if the user ended up with a non-utf-8 locale, LC_ALL it is.

VorpalBlade commented 1 year ago

LANG won't override whatever parameters are setting a non-utf-8 locale. Having a non-utf-8 locale is a problem of much greater proportion than date formatting, so if the user ended up with a non-utf-8 locale, LC_ALL it is.

Hm true, this only makes sense for the "no locale set yet" case, which is what I was running into (it seems LC_ALL is set during .zshenv, which is before .zprofile).

I guess it would be too complicated to detect that situation separately?


And I do end up with a UTF-8 locale. It is just that apparently .zprofile is after the LC_ALL check.

romkatv commented 1 year ago

z4h sets locale in zshenv as early as possible because otherwise it's difficult to use the terminal at all. When changing locale, unset all parameters instead of patching them. Like this:

unset -m 'LC_*|LANG'
hgkjshegfskef commented 1 year ago

In my case (Fedora), instead of

z4h source /etc/zsh/zprofile

I had to use

source /etc/zprofile

in ~/.zprofile, otherwise it seemingly wouldn't do anything. Probably because z4h is not yet configured?

And /etc/zprofile contains:

#
# /etc/zprofile and ~/.zprofile are run for login shells
#

_src_etc_profile()
{
    #  Make /etc/profile happier, and have possible ~/.zshenv options like
    # NOMATCH ignored.
    #
    emulate -L ksh

    # source profile
    if [ -f /etc/profile ]; then
        source /etc/profile
    fi
}
_src_etc_profile

unset -f _src_etc_profile

Then, when SDDM runs KDE, it launches a zsh login shell, which goes to $ZDOTDIR, finds ~/.zprofile, which in turn sources /etc/zprofile, which sources the scripts under /etc/profile.d in ksh emulation mode.

Now, things like .desktop files from flatpak are found by KRunner.

romkatv commented 1 year ago

In my case (Fedora), instead of

z4h source /etc/zsh/zprofile

I had to use

source /etc/zprofile

in ~/.zprofile, otherwise it seemingly wouldn't do anything. Probably because z4h is not yet configured?

z4h source /etc/zsh/zprofile doesn't do anything on your machine because /etc/zsh/zprofile does not exist.

hgkjshegfskef commented 1 year ago

No, that is not the reason. Sorry, I meant to write:

instead of z4h source /etc/zprofile I had to use source /etc/zprofile

I have just tested it. E.g. with z4h command I don't see the application entries in KDE menu, while with just sourcing they do show up.

romkatv commented 1 year ago

I have just tested it. E.g. with z4h command I don't see the application entries in KDE menu, while with just sourcing they do show up.

This sounds very surprising given that all zsh configs that use zsh4humans utilize z4h source command. I've just tried it on my machine and it works fine.