【问题标题】:Controlling an interactive process with PHP using Symfony Process使用 Symfony 进程控制 PHP 的交互式进程
【发布时间】:2015-01-17 07:20:39
【问题描述】:

我正在尝试通过使用ssh-add 添加新密钥来控制系统ssh-agent。为此,我使用了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 程序开始阻塞等待输入。

【问题讨论】:

  • 你在嵌套命令吗?该问题期待用户的输入,但它在您的包装过程中被阻止。您需要找到一种非交互方式执行底层命令的方法。
  • 我不认为 ssh_add 进程被阻止,因为我可以手动输入密码。更重要的是 PHP 脚本无法为用户输入密码,因为 ssh_add 已移至前台,而 PHP 脚本只是等待进程完成。
  • fd 的 5 和 7 是什么? fd 4在哪里打开?使用&lt; /dev/null(可能还有2&lt;/dev/null)从shell 运行脚本是否有助于它工作?

标签: php shell symfony process symfony-process


【解决方案1】:

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

引用文章:

这类似于为进程创建管道,但使用操作系统的 /dev/ptmx 接口创建主(用于您的脚本)和从属(用于您正在运行的进程)pty 句柄。这允许您向显式打开 /dev/tty 的应用程序发送数据并从中捕获数据——这通常在交互式提示输入密码时完成。

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

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

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();

【讨论】:

    猜你喜欢
    • 2013-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-14
    • 2017-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多