java - 从Java进程访问子进程控制终端

标签 java linux terminal tty

我有一个长时间运行的 Java 服务器应用程序,它启动一个子进程来执行特定任务(在本例中,使用 7z 命令行实用程序提取 7zip 文件的内容,但该特定细节应该'在这里是相关的)。

  • 服务器应用程序在 Ubuntu 14 下使用 Java 8 运行。
  • 子进程正在通过 Java ProcessBuilder 启动API。
  • 子进程访问的文件可能受密码保护。
  • 如果文件受密码保护并且没有提供密码作为命令行参数,7z程序将尝试向终端显示一条消息,提示输入密码,然后从终端读取密码。
  • 此时,子进程挂起并且不会完成,除非我点击 <Enter>在控制 Java 进程的终端中两次。

潜在的问题似乎是 7z实用程序使用 getpass显示提示和读取用户输入的系统调用。根据文档:

The getpass() function opens /dev/tty (the controlling terminal of the process), outputs the string prompt, turns off echoing, reads one line (the "password"), restores the terminal state and closes /dev/tty again.

因为这是一个服务器应用程序,所以让子进程阻塞等待来自 Java 终端的输入是 Not Acceptable 。相反,我想要发生的是以编程方式关闭子进程终端上的输入,以便 getpass调用立即返回,没有输入,子进程退出并返回错误代码。不幸的是,我所有关闭子进程终端输入的尝试都导致了与上述相同的行为:

  • 我尝试手动关闭 Process.getOutputStream() 返回的流子流程启动后立即执行。
  • 我尝试使用 ProcessBuilder.redirectInput 重定向子进程输入以从空文件和 /dev/null 中读取.
  • 为了更好的衡量,我什至尝试通过 Redirect.INHERIT到 ProcessBuilder,尽管这似乎不是我想要的。

看起来这应该是可行的,因为我观察到当 7z 时所需的行为使用与守护进程的子进程完全相同的命令行启动 socat终端输入连接到 /dev/null 的过程,但我不知道如何使用我掌握的工具在 Java 中完成此任务。

最佳答案

为了防止 getpass() 函数打开/dev/tty(进程的控制终端),我们必须安排该实用程序没有控制 tty,这是通过以下方式实现的打电话setsid() .这个 C 程序(我们将其命名为 notty)进行此调用,然后执行给定的实用程序:

int main(int argc, char *argv[])
{
    setsid();                   // get rid of the controlling tty
    close(0);                   // preclude reading STDIN as well
    execvp(argv[1], argv+1);    // execute the given program file
}

我们可以在 Java 应用程序中使用它,方法是将它添加到 ProcessBuilder 命令之前,例如。例如:

        ProcessBuilder pb = new ProcessBuilder("notty", "7z", … /*arguments*/);

关于java - 从Java进程访问子进程控制终端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41124827/

相关文章:

java - Hibernate 中的列名称

java - Spring Cassandra 模型映射

java - 无法获得 SocketPermission 以使用 RMI

terminal - 将 tmux 前缀重新绑定(bind)到 alt 键

java - 当从 Runtime.getRuntime().exec() 调用新终端时,线程永远不会结束

linux - Bash 脚本剪切且文件名未硬编码

linux - 我正在尝试安装 Varnish

java - 当另一个 JFrame 可见时,模式对话框并不总是位于未修饰的 JFrame 之上

node.js - 找不到 lb(环回 cli)命令 - MacOS

linux - 我如何将需要另一个文档的二进制文件添加到命令行?