Skip to content

Job handling is broken. #3952

@layus

Description

@layus

I have an issue running the command sudo echo lol | grep lol. This command is not as trivial as it seems because grep is a fish function, calling command grep --color=auto $argv behind the scene.

Now, a simple run of that command gives

layus@klatch ~/p/fish> sudo echo lol | grep lol
# Nothing happens, job is blocked. Entering ^Z.
Job 1, 'sudo echo lol | grep lol' has stopped
layus@klatch ~/p/fish> jobs
Job	Group	CPU	State	Command
1	7318	0%	stopped	sudo echo lol | grep lol
2	7319	0%	stopped	command grep --color=auto $argv
layus@klatch ~/p/fish> fg
Send job 1, 'sudo echo lol | grep lol' to foreground
[sudo] password for layus : 
layus@klatch ~/p/fish> fg
Send job 2, 'command grep --color=auto $argv' to foreground
lol
layus@klatch ~/p/fish> 

This behavior is not changed by #3922, and may relate to #206. We see that fish starts two jobs for one user job, and that these jobs are managed separately. Digging in the source code, we can find that one job is created for the top-level pipeline.

exec_job starts processes for each step of the pipeline. When it reaches grep, it detects a fish function and calls internal_exec_helper to start its body.
That function parses the function body and starts a new job to to handle its content.
That new job is not aware of its parent context, and start with a new pgid. Fish gives that process control over the terminal, removing it from sudo.

When sudo starts, it receives a SIGTTOU and gets stopped. Fish blocks because the grep job is still running, and unrelated to the sudo job.

Running ps to compare fish to other shells gives

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
===== ===== ===== ===== ======== ===== ===== ==== ====== =========
    1   764   764   764 ?           -1 Ss       0   0:00 sshd [...]
  764 31954 31954 31954 ?           -1 Ss       0   0:00  \_ sshd: me [priv]
31954 31956 31954 31954 ?           -1 S     1000   0:00  |   \_ sshd: me@pts/0
31956 31957 31957 31957 pts/0     7319 Ss    1000   0:00  |       \_ -zsh
31957  7306  7306 31957 pts/0     7319 S     1000   0:00  |           \_ ./fish -d4
 7306  7317  7306 31957 pts/0     7319 S     1000   0:00  |               \_ ./fish -d4
 7306  7318  7318 31957 pts/0     7319 T        0   0:00  |               \_ sudo echo lol
 7306  7319  7319 31957 pts/0     7319 S+    1000   0:00  |               \_ grep --color=auto lol
  764  7390  7390  7390 ?           -1 Ss       0   0:00  \_ sshd: me [priv]
 7390  7392  7390  7390 ?           -1 S     1000   0:00  |   \_ sshd: me@pts/4
 7392  7393  7393  7393 pts/4     7470 Ss    1000   0:00  |       \_ -zsh
 7393  7460  7460  7393 pts/4     7470 S     1000   0:00  |           \_ zsh
 7460  7470  7470  7393 pts/4     7470 S+       0   0:00  |               \_ sudo echo lol
 7460  7471  7470  7393 pts/4     7470 S+    1000   0:00  |               \_ grep --color=auto lol
  764  7474  7474  7474 ?           -1 Ss       0   0:00  \_ sshd: me [priv]
 7474  7476  7474  7474 ?           -1 S     1000   0:00      \_ sshd: me@pts/5
 7476  7477  7477  7477 pts/5     7530 Ss    1000   0:00          \_ -zsh
 7477  7519  7519  7477 pts/5     7530 S     1000   0:00              \_ bash
 7519  7530  7530  7477 pts/5      7530 S+       0   0:00                  \_ sudo echo lol
 7519  7531  7530  7477 pts/5      7530 S+    1000   0:00                  \_ grep lol

We can see that the current TPGID is 7319 while the sudo command has a PGID of 7318. It's status is T, which means that the process is "stopped, either by a job control signal or because it is being traced". Here it is because of the aforementioned SIGTTOU.
We see that other shells give the same PGID to all the subcommands.

The extra fish process is the "keepalive fork" used by fish. I still wonder why fish needs it while other shells do not, but it is not important.

From a job control point of view, fish shows two running jobs in jobs output, while bash and zsh will show only one, even if zsh seems to distinguish between the processes of the job. (^Z was added for clarity, but does not appear in the terminal)

ZSH:
--(layus@klatch)--> grep () { command grep --color=auto "$@"; }   
--(layus@klatch)-->  sudo echo lol | grep lol                      
[sudo] password for layus: ^Z
zsh: running    sudo echo lol | grep --color=auto lol
--(layus@klatch)--> jobs
[1]  + suspended           sudo echo lol | 
       suspended (signal)  grep --color=auto lol

BASH:
layus@klatch:~$ grep () { command grep --color=auto "$@"; }
layus@klatch:~$ sudo echo lol | grep lol
[sudo] password for layus: ^Z

[1]+  Stopped                 sudo echo lol | grep lol
layus@klatch:~$ jobs
[1]+  Stopped                 sudo echo lol | grep lol

We need to make function invocation part of the same job as their context. I believe that one command line should create one job in the jobs view. That would also immediately fix the SIGTTOU issue for sudo because both processes will have the same PGID and will both have control over the terminal.
Because they are handled exctly like function, begin/end blocks would be fixed at the same time.


On current master (version 2.5.0-259-gc0de8afa-dirty, aka c0de8af).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething that's not working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions