Featured image of post reptyr:SSH 忘開 tmux 也救得回來,把 process 移進去

reptyr:SSH 忘開 tmux 也救得回來,把 process 移進去

reptyr 用 ptrace 把已在執行的 process 重新附加到新的 terminal。SSH 忘開 tmux 就跑了長時間任務,或者想把 background process 移進 screen,reptyr 一個指令搞定。

SSH 進去跑了一個要跑幾小時的任務,沒開 tmux。 現在要離開,直接關視窗 process 就死了,nohup 也沒加。 reptyr 可以把這個 process 移進 tmux,讓你安心斷線。

reptyr 是什麼

reptyrptrace 系統呼叫把一個已在執行的 process 重新附加到新的 terminal。它真正改變的是 process 的 controlling terminal,不只是重導 I/O。

這個差別很重要。以前用 gdb 的 screenify 腳本也能做類似的事,但有三個問題:

  • 舊 terminal 的輸入還是會被 process 接收
  • 視窗大小改變(resize)沒辦法通知 ncurses 程式
  • 新 terminal 的 Ctrl-C 無法生效

reptyr 因為真正換掉 controlling terminal,這三個問題都沒有。

安裝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Ubuntu / Debian
sudo apt install reptyr

# Arch
sudo pacman -S reptyr

# macOS(透過 Homebrew,Linux 模擬)
brew install reptyr

# 從原始碼編譯
git clone https://github.com/nelhage/reptyr
cd reptyr && make && sudo make install

基本用法

1
reptyr <PID>

執行後,PID 對應的 process 就會附加到目前的 terminal,接收這個 terminal 的輸入輸出和訊號(Ctrl-CCtrl-Z 都有效)。

實際情境:SSH 忘開 tmux

這是最常用的場景。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 先確認 process 的 PID
jobs -l
# 或
ps aux | grep my-script

# 2. 如果 process 是 foreground,先暫停它
Ctrl-Z

# 3. 移到 background
bg

# 4. 讓它脫離目前 shell 的管控(不然 shell 結束時它還是會死)
disown

# 5. 開 tmux
tmux new -s rescue

# 6. 在 tmux 裡用 reptyr 把它拉進來
reptyr <PID>

# 7. 現在可以安全斷線了
Ctrl-B D  # detach tmux

重新連線時:

1
2
ssh yourserver
tmux attach -t rescue

process 還在跑,輸出也在。

ptrace_scope 的限制(Ubuntu)

Ubuntu 10.10 以後預設禁止非 root 用 ptrace attach 到其他 process,reptyr 執行時會看到:

1
Unable to attach to pid 12345: Operation not permitted

臨時解除限制:

1
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

永久設定:

1
2
# 編輯 /etc/sysctl.d/10-ptrace.conf
kernel.yama.ptrace_scope = 0

或者用 sudo reptyr <PID> 直接以 root 執行。

ptrace_scope 的值:0 = 允許、1 = 僅限父 process(預設)、2 = 僅限 root、3 = 完全禁止。降到 1 就夠用了,不一定要到 0。

進階:reptyr -l

1
2
reptyr -l
# 輸出:/dev/pts/7

建立一個獨立的 pseudo-terminal pair,不跟任何 shell 連結。主要用途是 gdb 偵錯時把 inferior 的 I/O 導到這個 pty:

1
(gdb) set inferior-pty /dev/pts/7

比直接把 gdb 的 terminal 讓出去乾淨很多。

不適用的情況

  • daemon process:已經完全脫離 terminal 的 process(沒有 controlling terminal)無法附加
  • setuid binary:ptrace 無法 attach 到 setuid 程式(安全限制)
  • 已死的 process:當然救不回來

小結

忘開 tmux 不是世界末日。reptyr <PID> 一行,process 就從快死的 SSH session 搬進 tmux,繼續跑完。記得先 disown 讓它脫離 shell,再開 tmux 把它拉進去。Ubuntu 的話順便把 ptrace_scope 調整一下。

參考資源