使用Symfony Process使用PHP控制交互式进程

时间:2014-11-19 16:56:50

标签: php shell symfony process symfony-process

我正在尝试使用ssh-agent向系统添加新密钥来控制系统ssh-add。为此,我使用Symfony Process组件。

当我从网站运行此代码时,它运行得很好但是当我在shell /控制台中运行相同的代码时,ssh-add进程会挂起Enter passphrase for <path to key>:

代码的简化版本看起来像这样

use Symfony\Component\Process\Process;

$keyPath = '<path to key>';
$keyPassword = '<password for unlocking the key>';
$socketPath = '<path to ssh-agent socket>';

$sshAdd = new Process(
    "ssh-add {$keyPath}",
    null,
    [
        'SSH_AUTH_SOCK' => $socketPath
    ],
    $keyPassword
);
$sshAdd->run();

正如您在上面的代码中看到的,我打电话给ssh-add,在环境中设置SSH_AUTH_SOCK,以便ssh-add可以与代理商交谈,然后在输入。正如我之前所说,当我在Web上下文中运行它时,它可以工作但它挂在shell /控制台上下文中。

我在控制台中运行时做了strace,相关部分看起来像这样

open("<path to key>", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(4, "<key password>", <length of password>)      = 20
close(4)                                              = 0
wait4(9650, 0x7fff00ab3554, WNOHANG|WSTOPPED, NULL)   = 0
select(8, [5 7], [], [], {0, 0})                      = 0 (Timeout)
wait4(9650, 0x7fff00ab3554, WNOHANG|WSTOPPED, NULL)   = 0
select(8, [5 7], [], [], {0, 0})                      = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000}Enter passphrase for <path to key>:) = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
select(8, [5 7], [], [], {0, 200000})                 = 0 (Timeout)
...

正如您所看到的,写入似乎被忽略,ssh-add程序开始阻止等待输入。

1 个答案:

答案 0 :(得分:7)

在阅读了ssh-add的源代码之后,最后找到了解决这个问题的方法,并阅读了this very old article from Wez Furlong,他谈到了向PHP添加PTY支持。

引用文章:

  

这样做与创建进程的管道类似,而是使用操作系统的/ dev / ptmx接口创建master(对于您的脚本)和slave(对于您正在运行的进程)pty句柄。这允许您向显式打开/ dev / tty的应用程序发送和捕获数据 - 这通常在交互式提示输入密码时完成。

原来Symfony Process也支持PTY,因此原始代码只需要进行一些更改。首先,我需要通过调用setPty(true)来指定我想使用PTY而不是管道。然后,我需要模拟用户在输入密码后输入 ENTER ,只需在输入中附加换行符即可。

最终代码看起来像这样(在更改的行上有注释)

use Symfony\Component\Process\Process;

$keyPath = '<path to key>';
$keyPassword = '<password for unlocking the key>';
$socketPath = '<path to ssh-agent socket>';

$sshAdd = new Process(
    "ssh-add {$keyPath}",
    null,
    [
        'SSH_AUTH_SOCK' => $socketPath
    ],
    $keyPassword . "\n"   // Append a line feed to simulate pressing ENTER
);
$sshAdd->setPty(true);   // Use PTY instead of the default pipes
$sshAdd->run();