如何創建一個不會通過終端命令以外的任何方式關閉的 X 視窗
我正在嘗試創建一個從 X11 開始的程序,它將是圖形化的並且我自己的使用者無法殺死。
更具體地說,我有一個 urxvt 下拉終端,從啟動時開始並立即執行 tmux 會話。讓我煩惱的是,當我忘記它是下拉終端並且我應該隱藏它時,我有時會用
Alt
+殺死它。F4
當然,我不會失去我的 tmux 會話,因為我可以從另一個終端重新連接到它,但是我必須重新啟動下拉終端(使用確切的配置)並重新連接,這很乏味。
我想做的是不能
Alt
+F4
殺死這個特定的程序。讀完後: 在 Linux 上使程序無法殺死
我知道我應該以屬於另一個使用者的身份啟動該程序,但即使我設法在我自己的 X 伺服器中通過以下方式執行此操作:
sudo xhost +local: sudo runuser -l my-secondary-user -c urxvt
我面臨兩種不受歡迎的行為:
- 我的主要使用者(擁有 X 會話的使用者)可以終止該程序。
- 虛擬終端開始以 my-secondary-user 身份登錄(當然)。
(2)可能是可糾正的——只需執行一個命令以我的主要使用者身份登錄——但我不知道如何修復(1)。
有沒有辦法讓這項工作,或另一種方式來做我想做的事?如果可能的話,我寧願避免創建核心模組。
使用 Openbox 執行 Arch Linux。
編輯
我開始想我可以讓
Alt
+F4
不適用於這個特定的視窗。發現這個: Openbox:在每個應用程序的基礎上禁用 Alt-F4並修改了我的
rc.xml
:<keybind key="A-F4"> <action name="If"> <application class="URxvt" title="tmux-session"> </application> <then><!-- Do nothing for urxvt-tmux-session --></then> <else> <action name="Close" /> </else> </action> </keybind>
這比以前稍微好一點:我不能
Alt
+F4
那個終端,但現在也不能Alt
+F4
任何 urxvt 視窗。(猜猜這是因為新的 urxvt 視窗都在同一個類名下執行?)除此之外,我可以從菜單欄中殺死我需要的一切,或者右鍵點擊我的色調面板,或者當然,通過命令。但這種方法意味著:
- 我仍然可以通過右鍵點擊 tint2 面板來終止我的下拉終端(如果我讓下拉終端不顯示為面板的圖示,也許可以修復 - 應該通過一些研究來實現)。
- 我不能
Alt
+F4
其他終端視窗(不太喜歡它,但也許可以接受)。有任何想法嗎?
如果切換到
xterm
是一個選項,您可以使用下面的 hack。不過有一些注意事項。一旦解決了其中的大多數問題,解決方案就會變得非常複雜,請參閱最後的腳本。xterm -e 'trap "" HUP; your-application'
在收到來自視窗管理器的關閉指令後,
xterm
將向您的應用程序的程序組發送一個 SIGHUP 信號,並且僅在程序返回時自行退出。這假設您的應用程序不會重置 SIGHUP 的處理程序,並且可能會對您的應用程序的子級產生不必要的副作用。
如果您的應用程序是
tmux
.要解決這些問題,您可以執行以下操作:
xterm -e sh -c 'bash -mc tmux <&1 & trap "" HUP; wait'
這樣,
tmux
將在不同的程序組中啟動,因此只有sh
SIGHUP 會收到(並忽略它)。現在,這不適用於
tmux
重置這些信號的處理程序,但在一般情況下,根據您的實現sh
,SIGINT、SIGQUIT 信號以及通常兩者都將被您的應用程序忽略,因為它bash
作為啟動來自非互動式的非同步命令sh
。這意味著您不能使用Ctrl+C
or中斷您的應用程序Ctrl+\
。這是 POSIX 要求。一些 shell 像
mksh
不尊重它(至少不是目前版本),或者只是部分dash
地為 SIGINT 而不是 SIGQUIT。因此,如果mksh
可用,您可以這樣做:xterm -e mksh -c 'bash -mc your-application <&1 & trap "" HUP; wait'
(儘管
mksh
如果他們決定修復該不符合項,這在未來版本中可能不起作用)。或者,如果您不能保證
mksh
或bash
將可用,或者不想依賴將來可能發生變化的行為,您可以手動完成他們的工作,perl
例如編寫一個unclosable-xterm
包裝腳本,例如:#! /bin/sh - [ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}" exec xterm -e perl -MPOSIX -e ' $pid = fork; if ($pid == 0) { setpgrp or die "setpgrp: $!"; tcsetpgrp(0,getpid) or die "tcsetpgrp: $!"; exec @ARGV; die "exec: $!"; } die "fork: $!" if $pid < 0; $SIG{HUP} = "IGNORE"; waitpid(-1,WUNTRACED)' "$@"
(稱為
unclosable-xterm your-application and its args
)。現在,另一個副作用是我們正在創建並置於前台(上面帶有
bash -m
或setpgrp
+tcsetpgrp
)的新程序組不再是會話領導程序組,因此不再是孤立的程序組(據說現在有一個父程序在照顧它(sh
或perl
))。這意味著在按下 時
Ctrl+Z
,該過程將被暫停。在這裡,我們粗心的父程序將退出,這意味著程序組將得到一個 SIGHUP(並希望死掉)。為了避免這種情況,我們可以忽略子程序中的 SIGTSTP,但是如果你的應用程序是一個互動式 shell,對於某些實現,如
mksh
,yash
或rc
,Ctrl-Z
對它們執行的作業也不起作用。或者我們可以實現一個更細心的父母,每次停止時恢復孩子,比如:
#! /bin/sh - [ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}" exec xterm -e perl -MPOSIX -e ' $pid = fork; if ($pid == 0) { setpgrp or die "setpgrp: $!"; tcsetpgrp(0,getpid) or die "tcsetpgrp: $!"; exec @ARGV; die "exec: $!"; } die "fork: $!" if $pid < 0; $SIG{HUP} = "IGNORE"; while (waitpid(-1,WUNTRACED) > 0 && WIFSTOPPED(${^CHILD_ERROR_NATIVE})) { kill "CONT", -$pid; }' "$@"
另一個問題是,如果
xterm
由於其他原因而不是從視窗管理器關閉,例如如果xterm
被殺死或失去與 X 伺服器的連接(因為xkill
,您的視窗管理器的銷毀操作,或者 X 伺服器崩潰例如),那麼這些程序不會死掉,因為在這些情況下也會使用 SIGHUP 來終止它們。要解決這個問題,您可以poll()
在終端設備上使用(它會在執行時被拆除xterm
):#! /bin/sh - [ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}" exec xterm -e perl -w -MPOSIX -MIO::Poll -e ' $pid = fork; # start the command in a child process if ($pid == 0) { setpgrp or die "setpgrp: $!"; # new process group tcsetpgrp(0,getpid) or die "tcsetpgrp: $!"; # in foreground exec @ARGV; die "exec: $!"; } die "fork: $!" if $pid < 0; $SIG{HUP} = "IGNORE"; # ignore SIGHUP in the parent $SIG{CHLD} = sub { if (waitpid(-1,WUNTRACED) == $pid) { if (WIFSTOPPED(${^CHILD_ERROR_NATIVE})) { # resume the process when stopped # we may want to do that only for SIGTSTP though kill "CONT", -$pid; } else { # exit when the process dies exit; } } }; # watch for terminal hang-up $p = IO::Poll->new; $p->mask(STDIN, POLLERR); while ($p->poll <= 0 || $p->events(STDIN) & POLLHUP == 0) {}; kill "HUP", -$pid; ' "$@"