zellij-org / zellij

A terminal workspace with batteries included
https://zellij.dev
MIT License
21.63k stars 654 forks source link

Preserve environment variable when attaching to existing session #1531

Open blurgyy opened 2 years ago

blurgyy commented 2 years ago

Currently the environment variables in shells opened in zellij are inherited from the session that starts the zellij server. It would be great to have the newly opened shells inherit the environment of the currently attached session. This is useful for, e.g., indicating current session is a SSH session via shell prompts.

raphCode commented 2 years ago

As far as I understand, you have a shell with, say, NEW_ENV="hello" set. You then attach to a pre-existing zellij session and expect zellij to apply that environment variable to every new pane you open from now on through this client?

That would probably be a bit confusing because you have to remember which panes have which environment as old panes already open still have the original server environment?

Can you elaborate on the use case?

blurgyy commented 2 years ago

Hi @raphCode, thanks for the reply.

As far as I understand, you have a shell with, say, NEW_ENV="hello" set. You then attach to a pre-existing zellij session and expect zellij to apply that environment variable to every pane you open from now on through this client?

No, I'm aware of that expecting zellij to change an existing shell's environments is indeed confusing, and that's not what I would expect, either.

Edit(I misunderstood the above question): Yes, I am requesting zellij to apply/merge the environments of the client to newly created panes/tabs ever since I attach this client to an existing zellij session.

Can you elaborate on the use case?

The specific use case is: when I open a new pane/tab, I would like to be able to tell if I am in an SSH connection or not, by examining the shell environment in new pane/tab. This is the behaviour of TMUX and it would be great if zellij can make this configurable (I don't know a way to make this configurable in TMUX, afaik this is a forced behaviour in TMUX).

To explain this, here is a screenshot of how TMUX (with completely default configuration) handles this:

image

where the 0-th pane (left) is opened locally, and the 1-st pane (right) is openned from an SSH connection.

Note how the shell prompts in the two panes differ where the prompt in the 1-st pane (right) shows my username and my machine's hostname since it's opened from within an SSH connection so the shell inherits the SSH connection's environment variables.

I hope this makes it clearer.

a-kenji commented 2 years ago

How is your shell configured in tmux @blurgyy?

blurgyy commented 2 years ago

Hi @a-kenji,

How is your shell configured in tmux @blurgyy?

If you are asking about the shell prompt, it is managed by starship, which is configured to show my username if any of SSH_TTY, SSH_CONNECTION or SSH_CLIENT is set, and to show the machine's hostname if SSH_CONNECTION is set:

image

a-kenji commented 2 years ago

Hi @blurgyy, I will rephrase my question: Did you configure your shell in tmux? (through default-shell)

blurgyy commented 2 years ago

No, I did not configure my shell in tmux in the first screenshot.

The first screenshot was taken after I deleted my ~/.config/tmux/tmux.conf, and I just checked there is no other tmux configuration enabled (I don't have a ~/.tmux.conf).

a-kenji commented 2 years ago

@blurgyy, Thank you! That means that your shell is running as a login shell, maybe that could explain the behaviour? I am not to sure if tmux is actually sharing all the environment variables with every window.

Do you see the same behaviour when setting the shell as an interactive shell in tmux?

blurgyy commented 2 years ago

Hi @a-kenji, thank you for your reply!

Do you see the same behaviour when setting the shell as an interactive shell in tmux?

Yes, I think so:

image

I followed https://wiki.archlinux.org/title/Tmux#Start_a_non-login_shell to launch a non-login shell with tmux, does a "non-login shell" here mean an "interactive shell"?


Some extra information:

set -g default-command "${SHELL}"
    set tsess "main"
    set default_wname "default"
    set wname (date '+%b-%d,%H-%M-%S')

    if not tmux has-session -t $tsess 2>/dev/null
      # If session '$tsess' does not exist, create first
      tmux start-server
      tmux select-window -t $tsess:0 \; \
        rename-window $default_wname
    end

    exec tmux new-session -t $tsess \; \
      new-window -n $wname \; \
      select-window -t $wname
a-kenji commented 2 years ago

@blurgyy, Thank you for checking! In that case it seems that tmux is indeed doing something special with the environment variables. This might warrant an option to include in zellij.

blurgyy commented 2 years ago

Cool, thanks for considering this!

raphCode commented 2 years ago

Thanks for the detail! If I understand correctly, this is your setup:

┌─────────────────────┐
│                     │       ┌───────────┐
│ ┌─────┐  ┌───────┐  │  ssh  │           │
│ │local├─►│ zellij│◄─┼───────┤ machine B │
│ │term │  └───────┘  │       │           │
│ └─────┘             │       └───────────┘
│      machine A      │
└─────────────────────┘

And you want zellij to pick up the environment from attached clients and apply it to newly opened panes.

I tested this, and indeed tmux does it, but only for some variables: https://github.com/tmux/tmux/blob/06869ff22fa9891c9633ce3e3efa77cac758b520/options-table.c#L746-L754

I still don't think this is a good idea because panes have different envs depending on who opened them and not based on who is using them. Also this leads to stale environments after the ssh connection closes and the clients go away.

But, if tmux does it, we might too...

dsully commented 9 months ago

+1 - I just ran into this issue where I have Neovim's clipboard setup to use OSC52 via SSH but pbcopy/pbpaste otherwise.

If my zellij session was started in a local terminal, then I SSH into it, Neovim still think's I'm local.

SmnTin commented 4 months ago

+1 Forwarding SSH_AUTH_SOCK and similar would be very useful!

Hylian commented 4 months ago

I ran into this as well; here's my usecase and workaround:

I use waypipe to proxy my Wayland session when reattaching to persistent tmux/zellij sessions over SSH. This allows me to share the Wayland clipboard between the local and remote system in nvim and other applications. (OSC52 copy from the remote system has not worked for me with nvim+zellij+foot, and I occasionally need to proxy graphical applications over waypipe anyways).

Tmux

With tmux, you can invoke tmux show-env $ENV_VAR from within the tmux session to get the current value of environment variables from the outside. Waypipe sets the WAYLAND_DISPLAY env var when it opens the SSH session, and I'll grab this updated value automatically.

In .zshrc:

function refresh {
  if [ -n "$TMUX" ] && [ -n "$SSH_TTY" ] && [ -n "$WAYLAND_DISPLAY" ]; then
    export $(tmux show-env WAYLAND_DISPLAY 2> /dev/null)
  fi
}

function preexec {
  refresh
}

In nvim:

  function! TmuxWaylandRefresh()
    if !empty($TMUX) && !empty($WAYLAND_DISPLAY)
      let prev_display = $WAYLAND_DISPLAY
      let display = split(system("tmux show-env WAYLAND_DISPLAY 2> /dev/null"), "=")[1][:-2]
      let $WAYLAND_DISPLAY = display
      "echom "Changed $WAYLAND_DISPLAY from " . prev_display . " to " . $WAYLAND_DISPLAY
    endif
  endfunction

  if exists('$TMUX')
    autocmd BufEnter,FocusGained * call TmuxWaylandRefresh()
  endif

Zellij

Since zellij doesn't provide a way to get updated env vars from outside the session, I had to get a bit creative.

When I SSH in, I first grab the value of WAYLAND_DISPLAY and write it to a temp file before reattaching to the session.

waypipe ssh -t $HOSTNAME "zsh -c 'printenv WAYLAND_DISPLAY > /tmp/wayland_display && zellij -s persist || zellij a persist'"

I then use this file to update the environment variable.

In .zshrc:

function refresh {
  if [ -n "$ZELLIJ" ] && [ -f /tmp/wayland_display ]; then
    export WAYLAND_DISPLAY=$(< /tmp/wayland_display)
  fi
}

function preexec {
  refresh
}

In nvim:

function! UpdateWaylandDisplay()
  if  !empty($ZELLIJ)
    let $WAYLAND_DISPLAY = readfile("/tmp/wayland_display", 1)[0]
  endif
endfunction

(Ideally, I'd want to check if I'm in an SSH session with $SSH_TTY, but I haven't bothered with that for now.)

gdevenyi commented 3 months ago

I think the request should be made clear here that this should be a manually curated list of environment variables that get updated on zellij attach. It should definitely not be all variables.

Ones that make sense: SSH_* parameters are an obvious ones DISPLAY is another (for ssh forwarded X) WAYLAND_DISPLAY