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

3
我有一个长时间运行的Java服务器应用程序,启动子进程执行特定任务(在这种情况下,使用命令行实用程序7z提取7zip文件的内容,但这个细节在这里不应该相关)。
  • 服务器应用程序在Ubuntu 14下使用Java 8运行。
  • 子进程通过Java ProcessBuilder API启动。
  • 子进程访问的文件可能受到密码保护。
  • 如果文件受密码保护且没有提供命令行参数作为密码,则7z程序将尝试向终端显示提示输入密码的消息,然后从终端读取密码。
  • 此时,子进程会挂起,并且除非我在控制Java进程的终端上按两次<Enter>,否则无法完成。
基本问题似乎是7z实用程序使用getpass系统调用来显示提示并读取用户输入。 根据文档:

getpass()函数打开/ dev / tty(进程的控制终端),输出字符串prompt,关闭回声,读取一行(“密码”),恢复终端状态并再次关闭/ dev / tty。

由于这是服务器应用程序,因此不能接受子进程阻塞等待Java终端的输入。 相反,我希望以编程方式关闭子进程终端上的输入,以便getpass调用立即返回,并且子进程退出并出现错误代码。 不幸的是,我所有尝试关闭子进程终端的输入的尝试都导致与上述相同的行为:
  • 我尝试在启动子进程后立即手动关闭Process.getOutputStream()返回的流。
  • 我尝试使用ProcessBuilder.redirectInput将子进程输入重定向为从空文件和/dev/null读取。
  • 为了保险起见,我甚至尝试将Redirect.INHERIT传递给ProcessBuilder,即使那似乎根本不是我想要的。

这似乎是可行的,因为当7z以与守护进程化的socat进程相同的命令行作为子进程启动时,我观察到所需的行为,其中终端输入连接到/dev/null,但我不知道如何使用我拥有的工具在Java中实现这一点。


据我所知,Java 8中没有管理子进程的方法。扩展进程API是在Java 9中引入的。 - LEQADA
很遗憾,从浏览更新后的Java 9 API文档中,我没有看到任何立即显而易见的东西可以帮助我解决这个问题。 - Alex
1个回答

0
为了避免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
}

我们可以通过将其前置到ProcessBuilder命令中,从Java应用程序中使用它,例如:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接