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

时间:2016-12-13 15:31:32

标签: java linux terminal tty

我有一个长期运行的Java服务器应用程序,它启动一个子进程来执行特定任务(在这种情况下,使用7z命令行实用程序提取7zip文件的内容,但是细节不应该与此相关)。

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

基本问题似乎是7z实用程序使用getpass系统调用来显示提示和读取用户输入。根据文件:

  

getpass()函数打开/ dev / tty(进程的控制终端),输出字符串提示,关闭回显,读取一行(&#34;密码&#34;),恢复终端状态并再次关闭/ dev / tty。

由于这是一个服务器应用程序,因此让子进程块等待来自Java终端的输入是不可接受的。我想要的是以编程方式关闭子进程终端上的输入,以便getpass调用立即返回而没有输入,并且子进程以错误代码退出。不幸的是,我关闭输入到子流程终端的所有尝试都产生了与上述相同的行为:

  • 我尝试在启动子流程后立即手动关闭Process.getOutputStream()返回的流。
  • 我尝试使用ProcessBuilder.redirectInput将子流程输入重定向为从空文件读取,并从/dev/null重新定位。
  • 为了更好的衡量,我甚至尝试将Redirect.INHERIT传递给ProcessBuilder,即使它看起来并不像我想要的那样远。

似乎这个应该是可行的,因为当7z使用与a的子进程完全相同的命令行启动{I}}时,我观察到了所需的行为终端输入连接到socat的守护进程/dev/null进程,但是如果使用我可以使用的工具在Java中完成此操作,我感到很茫然。

1 个答案:

答案 0 :(得分:0)

为了防止 getpass()函数打开/ dev / tty(进程的控制终端),我们必须安排该实用程序没有控制tty,这可以通过以下方法实现:呼叫setsid()。此C程序(将其命名为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 } )进行此调用,然后执行给定的实用程序:

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

我们可以在Java应用程序中使用它,方法是将其添加到ProcessBuilder命令的前面。 g。:

Compiler.java