Управление интерактивным процессом с помощью PHP с использованием Symfony Process

Я пытаюсь управлять системным ssh-агентом , добавляя к нему новые ключи, используя ssh-add . Для этого я использую компонент Symfony Process .

Когда я запускаю этот код с веб-сайта, он отлично работает, но когда я запускаю тот же код в оболочке / консоли, процесс 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 может разговаривать с агентом, а затем отправляет пароль на вход. Как я уже говорил ранее, когда я запускаю это в веб-контексте, он работает, но он зависает в контексте оболочки / консоли.

Я работал с консолью, и соответствующие части выглядят так:

 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 и, прочитав эту очень старую статью из Wez Furlong, где он говорит о добавлении поддержки PTY к PHP.

    Процитировать статью:

    То, что это делает, похоже на создание канала для процесса, но вместо этого создает master (для вашего скрипта) и slave (для выполняемого процесса) pty-дескрипторы с использованием интерфейса / dev / ptmx вашей ОС. Это позволяет вам отправлять и извлекать данные из приложений, открывающих / dev / tty явно, – и это обычно делается при интерактивном запросе пароля.

    Оказывается, Symfony Process также поддерживает PTY, поэтому исходный код потребовал всего пару изменений. Сначала мне нужно указать, что я хочу использовать PTY вместо труб, вызывая setPty(true) . Затем мне нужно смоделировать, что пользователь нажал 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();