如何创建一个除了终端命令外不会以任何方式关闭的 X 窗口

Yia*_*los 6 openbox terminal x11

我正在尝试创建一个从 X11 开始的进程,它将是图形化的并且无法被我自己的用户杀死。

更具体地说,我有一个 urxvt 下拉终端,它在启动时开始并立即运行 tmux 会话。令我烦恼的是,当我忘记它是下拉终端并且我应该将其隐藏时,有时我会用Alt+杀死它F4。

当然,我不会丢失我的 tmux 会话,因为我可以从另一个终端重新连接到它,但是我必须重新启动下拉终端(使用确切的配置)并重新连接,这很乏味。

我想做的是不能Alt+F4 杀死这个特定的进程。

阅读本文后: 在 Linux 上使进程不可终止

我知道我应该启动属于另一个用户的进程,但即使我设法在我自己的 X 服务器中执行以下操作:

    sudo xhost +local:
    sudo runuser -l my-secondary-user -c urxvt
Run Code Online (Sandbox Code Playgroud)

我面临两种不受欢迎的行为:

  1. 该进程可以被我的主要用户(拥有 X 会话的用户)杀死。
  2. 虚拟终端开始以 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>
Run Code Online (Sandbox Code Playgroud)

这比以前稍微好一点:我不能Alt+F4那个终端,但现在也不能Alt+F4任何urxvt窗口。(猜猜这是因为新的 urxvt 窗口都在同一个类名下运行?)

除此之外,我可以从菜单栏中删除我需要的所有内容,或者右键单击我的色调面板,或者,当然,通过命令。但这种方法意味着:

  1. 我仍然可以通过右键单击 tint2 面板来终止我的下拉终端(如果我让下拉终端不显示为面板的图标,也许可以修复 - 应该可以通过一些研究)。
  2. 我不能Alt+F4其他终端窗口(不太喜欢它,但也许可以接受)。

有任何想法吗?

Sté*_*las 5

如果切换到xterm是一个选项,您可以使用下面的 hack。不过有一些注意事项。一旦您解决了其中的大部分问题,解决方案就会变得非常复杂,请参阅最后的最终脚本。

xterm -e 'trap "" HUP; your-application'
Run Code Online (Sandbox Code Playgroud)

收到来自窗口管理器的关闭指令后,xterm将向您的应用程序的进程组发送一个 SIGHUP,并且只有在进程返回时才退出自身。

这假设您的应用程序不会重置 SIGHUP 的处理程序,并且可能会对您的应用程序的子级产生不必要的副作用。

如果您的应用程序是tmux.

要解决这些问题,您可以执行以下操作:

xterm -e sh -c 'bash -mc tmux <&1 & trap "" HUP; wait'
Run Code Online (Sandbox Code Playgroud)

这样,tmux将在不同的进程组中启动,因此只有sh会收到 SIGHUP(并忽略它)。

现在,tmux无论如何这并不适用于重置这些信号的处理程序,但在一般情况下,根据您的实现sh,SIGINT、SIGQUIT 信号和通常两者都将被您的应用程序忽略,因为它bash是作为启动的来自非交互式sh. 这意味着您不能使用Ctrl+C或中断您的应用程序Ctrl+\。

这是 POSIX 的要求。一些 shellmksh不尊重它(至少不是当前版本),或者只是部分地像dashSIGINT 而不是 SIGQUIT。因此,如果mksh可用,您可以执行以下操作:

xterm -e mksh -c 'bash -mc your-application <&1 & trap "" HUP; wait'
Run Code Online (Sandbox Code Playgroud)

(尽管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)' "$@"
Run Code Online (Sandbox Code Playgroud)

(称为unclosable-xterm your-application and its args)。

现在,另一个副作用是我们正在创建并放在前台的新进程组(使用bash -m或setpgrp+tcsetpgrp以上)不再是会话领导进程组,因此不再是孤立的进程组(据说现在有一个父进程在照顾它(sh或perl))。

这意味着按下 后Ctrl+Z,该过程将暂停。在这里,我们粗心的父进程将退出,这意味着进程组将收到 SIGHUP(并希望死亡)。

为避免这种情况,我们可以忽略子进程中的 SIGTSTP,但是如果您的应用程序是交互式 shell,对于某些实现,例如mksh, yashor 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;
  }' "$@"
Run Code Online (Sandbox Code Playgroud)

另一个问题是,如果xterm不是因为关闭窗口管理器的其他原因而消失,例如,如果xterm被杀死或失去与 X 服务器的连接(例如,由于Window 管理器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;
  ' "$@"
Run Code Online (Sandbox Code Playgroud)

  • @John,请参阅编辑以了解适用于 `tmux` 的解决方案。请注意,`xterm` 比 `rxvt` 更具可配置性。 (2认同)