1

If I do ctrl-c and ctrl-d to kill procs in panes and then exit from tmux, then all the child processes will die.

But if I do this:

ctrl+b followed by shift+%

then select Y to kill the window, then the procs stay alive. Any idea how to ensure the procs are killed when the window is killed?

3

Ctrl+c vs kill-window

With Ctrl+c you normally (when stty -a shows intr = ^C) send SIGINT to processes in the foreground process group. You seem to assume any process that gets SIGINT exits (gets killed) but in general it may not.

You mentioned Ctrl+d, so probably there's an interactive shell between the tmux server and the process you want to terminate. In this case SIGINT is sent to the process, not to the shell; but even if the shell got it, it wouldn't exit. (I'm not saying it's always like this; I'm saying it matches your description).

So it seems after the process terminates upon Ctrl+c, you hit Ctrl+d to exit the shell.

When you kill a window in tmux, respective pseudo-terminals are closed and processes using them as their controlling terminals receive SIGHUP. Such a process may send SIGHUP to its children then (for a shell see this answer). A process may ignore the signal (see nohup). Even if it ignores the signal, it may (or may not) die later if it tries to use a pseudo-terminal that is no more.

So in brief:

  • you send SIGINT or SIGHUP to the process in question;
  • it is up to the process if and how it reacts to SIGINT or SIGHUP.

Sending SIGINTs

A process may ignore SIGINT. If Ctrl+c does what you want for processes you're dealing with, then maybe it's enough to send SIGINTs to them prior to killing the window and sending SIGHUPs; as if you hit Ctrl+c in every pane.

The following command lists processes (immediate children of the tmux server) in panes in the current window, obtains respective foreground process groups and sends SIGINT to each:

tmux list-panes -F "#{pane_pid}" | xargs ps -o tpgid= | xargs -I{} kill -s SIGINT -{}

The problem is this very pipeline will also receive SIGINT and it may terminate prematurely. To deal with this

  • run the command from elsewhere and use -t to specify a window,
  • or implement some logic, so the foreground process group of the pipeline is excluded,
  • or run the pipeline in the background:

    tmux list-panes -F "#{pane_pid}" | xargs ps -o tpgid= | xargs -I{} kill -s SIGINT -{} &
    
  • or make the command ignore the signal:

    sh -c '
    trap "" SIGINT
    tmux list-panes -F "#{pane_pid}" | xargs ps -o tpgid= | xargs -I{} kill -s SIGINT -{}
    '
    

I think the approach is prone to race condition though; things may change between ps and kill. A better(?) alternative is to send Ctrl+c to each pane:

tmux list-panes -F "#{pane_id}" | xargs -I {} tmux send-keys -t {} C-c &

Hook

Use this hook to make tmux send Ctrl+c to each pane in a window that is about to be kill-windowed:

tmux set-hook before-kill-window 'run-shell "
   tmux list-panes -t \"#{window_id}\" -F \"##{pane_id}\" | xargs -I {} tmux send-keys -t {} C-c
"'

This looks awful because there are three levels of quoting, still it seems to work in my tests (I haven't tested thoroughly though). The purpose of ## is to defer expansion of #{pane_id}. The point is #{window_id} needs to be expanded when run-shell works, but #{pane_id} must survive and only be expanded when list-panes generates its output.

Note the hook covers kill-window but not kill-pane, kill-session or kill-server. Also kill-window is not just kill-pane repeated several times, so before-kill-pane hook will certainly not cover all cases when a pane is forcefully destroyed. And there are also respawn-pane -k and respawn-window -k commands.


What if it's not enough?

A process may not terminate upon SIGINT. With tmux list-panes -F "#{pane_pid}" as a starting point you can try to obtain a list of PIDs (and/or PGIDs) you'd like to send SIGTERM (or even SIGKILL) to. The main problem with this general approach is it's possible to spawn a process and break its connection with its ancestors and tty completely.

Therefore in general you may not be able to track all processes that originated from the given tmux window.

3
  • "When you kill a window in tmux, the tmux server sends SIGHUP to processes in the respective panes" Does it really do that? – mosvy Feb 21 '20 at 13:38
  • @AlexanderMills As to the rest, TL;DR; if you want to kill every process in your session pkill -s0, pgrep -as0, pkill -KILL -s0 (if a process did setsid(), it will survive it, as expected). I don't expect any terminal emulator to do that automatically, but you could probably create a key binding ;-) – mosvy Feb 21 '20 at 13:39
  • @mosvy Well, now I guess it does not. – Kamil Maciorowski Feb 21 '20 at 13:52
1

You are not giving enough information.

C-b % splits the window, it doesn't kill anything, so you must have changed the key binding. What do you have it bound to? Or do you mean C-b &?

Killing a pane will close the pty and the kernel will send any processes which had the pty as controlling terminal a SIGHUP which should make them exit, unless they ignore it.

What exactly are you running in the pane? Do the processes not exit at all or do they become zombies?

Also it would be helpful to know what tmux version you are using.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.