zellij-org / zellij

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

Serialisation struggles with shell strings #2925

Open lysogeny opened 11 months ago

lysogeny commented 11 months ago

Hey there!

I love the new resurrection feature. Unfortunately I managed to find a bug in the serialisation. When resurrection a session that includes pipes and strings, the session runs a command with the wrong arguments.

For example, running

dmesg -w --color=always | grep --color=never -P '(oom|Out of memory)'

in a shell, quitting the session and resurrecting it produces unexpected behaviour. In this example, grep returns

grep: missing closing parenthesis

Basic information

zellij --version:

zellij 0.39.0

stty size:

63 271

uname -av or ver(Windows):

Linux vm-129-69.cloud.dkfz-heidelberg.de 6.5.5-200.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Sun Sep 24 15:52:44 UTC 2023 x86_64 GNU/Linux

Other software

dmesg --version:

dmesg from util-linux 2.38.1

grep --version:

grep (GNU grep) 3.8
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others; see
<https://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

zsh --version:

zsh 5.9 (x86_64-redhat-linux-gnu)

Further information

How to reproduce:

# start a session
zellij -s serial-bug 
# run a fancy command
dmesg -w --color=always | grep --color=never -P '(oom|Out of memory)' 
# Now quit (e.g. C-q)
# Re-attach
zellij a serial-bug
# Hit enter to re-run the command

Produces:

grep: missing closing parenthesis

Notably, the pane is also called grep --color=auto --color=never -P (oom|Out of memory)

This appears to be because the pipe and ' are being incorrectly serialised:

% cat ~/.cache/zellij/0.39.0/session_info/serial-bug/session-layout.kdl
layout {
    tab name="Tab #1" focus=true hide_floating_panes=true {
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        pane command="grep" cwd="/var/home/ada" focus=true {
            args "--color=auto" "--color=never" "-P" "(oom|Out" "of" "memory)"
            start_suspended true
        }
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
    new_tab_template {
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        pane cwd="/var/home/ada"
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
}%       

Note how the command for the pane is grep and the string got mangled:

            args "--color=auto" "--color=never" "-P" "(oom|Out" "of" "memory)"

If I replace this with

            args "--color=auto" "--color=never" "-P" "(oom|Out of memory)"

grep works without errors, but is missing the dmesg input. I don't know to what extent this is fixeable but figured I should still report it.

Thank you for your time!

zeratax commented 10 months ago

yeah this is a big issue for me on nixos where lots of programs are wrapped.

if i have something running like nvim . it gets serialized as

 /home/nixos/.nix-profile/bin/nvim --cmd lua vim.g.node_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-node';vim.g.loaded_python_provider=0;vim.g.python3_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-python3';vim.g.ruby_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9

nvim here is just a shell script that sets a bunch of path variables and then executes

exec -a "$0" "/nix/store/az6kgqpgs570xbk0c2c3pl0jx4ncgxqd-neovim-unwrapped-0.9.1/bin/nvim"  --cmd "lua vim.g.node_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-node';vim.g.loaded_python_provider=0;vim.g.python3_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-python3';vim.g.ruby_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-ruby'" "$@"

but due to the missing quotation marks and the missing positional argument i can't enter my nvim session.

rubenmate commented 9 months ago

yeah this is a big issue for me on nixos where lots of programs are wrapped.

if i have something running like nvim . it gets serialized as

 /home/nixos/.nix-profile/bin/nvim --cmd lua vim.g.node_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-node';vim.g.loaded_python_provider=0;vim.g.python3_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-python3';vim.g.ruby_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9

nvim here is just a shell script that sets a bunch of path variables and then executes

exec -a "$0" "/nix/store/az6kgqpgs570xbk0c2c3pl0jx4ncgxqd-neovim-unwrapped-0.9.1/bin/nvim"  --cmd "lua vim.g.node_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-node';vim.g.loaded_python_provider=0;vim.g.python3_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-python3';vim.g.ruby_host_prog='/nix/store/qmqip8ajqhzmf3gs68y8177nw7s56bd2-neovim-0.9.1/bin/nvim-ruby'" "$@"

but due to the missing quotation marks and the missing positional argument i can't enter my nvim session.

So this is why all my nix installed packages aren't resurrecting.

Hope this can be solved, for me the resurrection just doesn't do it right now. I have to drop the shells because the resurrection command it's not what I want.

Tanish2002 commented 8 months ago

Has anyone has found a workaround to this(nixos) for now? on tmux-resurrect I could setup a resurrect-hook-post-save-all to manually fix the issues.

hmajid2301 commented 6 months ago

@imsnif Hey, sorry to bother you, I am trying to have a look at this and trying to see if the issue is with the serialization and particularly splitting the args on whitespace.

I am wondering where I should be looking. I am currently in looking over here: https://github.com/zellij-org/zellij/blob/462239b535c22eac29634e210ad86dde3fda37ce/zellij-utils/src/session_serialization.rs#L219-L227.

I am not a rust developer :sweat: so trying my best to follow the code.

Could you provide a bit of guidance if I am looking in the right place :pray: .

Thank you very much, I appreciate you are very busy!

sinh-x commented 2 months ago

I wonder if I can resurrect the sessions with pane layout without rerun previous command? Can it make an options so I don't have to go to each pane and press ESC on each pane?

monk3yd commented 2 months ago

Same issue here

mikebcbc commented 2 weeks ago

Following. Same here.

imsnif commented 1 week ago

These have been (mostly) fixed by https://github.com/zellij-org/zellij/pull/3636 (still unreleased but just merged to main).

I say "mostly" because what the PR fixed is the serialization part, the session-serialization still relies on the system's perspective of the running process (at the time of writing gleaned through ps) in order to determine what's running inside a pane.

To give an example, in the original report, the command: dmesg -w --color=always | grep --color=never -P '(oom|Out of memory)'. Here, we see a pipeline managed by the user's shell (eg. bash). From the perspective of the system, what we'll see is the last foreground command (in this case grep)*. So the resurrection will assume what's running inside the pane is the grep command.

Other examples might be various applications that run inside some sort of wrapper (eg. nix, electron, node, various java runtimes, etc.)

While there are ways around this, they are often particular to the task at hand. Maintaining a list of ways this information can be found and linking it to the particular command type is out of scope for Zellij core. In the future, I hope to add to the plugin API to allow plugins to optionally add other methods of doing this. For now, I feel this solution is good for the vast majority of cases.

*I hope readers will forgive me this simplification