Процесс завершается после выхода из SSH на сервере Windows.

Вопрос или проблема

У меня есть машина с Windows Server 2016, которая должна удаленно выполнять файл, использующий CLI, с помощью SSH.

Известно, что выход из SSH-соединения завершает процессы, которые были запущены пользователем ssh. На машинах с Linux можно использовать решения, такие как nohup или screen, что приводит к тому, что процесс не завершается при выходе из системы.

Однако такие решения не работают на машинах с Windows, и я ищу способ предотвратить завершение процесса после выхода из системы.

  • Мой клиент — это машина с Ubuntu
  • Мой Windows Server 2016 использует OpenSSH с PowerShell в качестве оболочки по умолчанию
  • Моя программа настроена на работу в режиме демона, и окна cmd или powershell не остаются открытыми
  • Использование Get-Process -Name proc_name показывает, что процесс действительно работает
  • Использование Get-Process после выхода из SSH не находит такого процесса

Что я также пробовал:

  • Запуск файла .exe с использованием cmd.
  • Запуск файлов .bat и .ps, которые запускают .exe с его аргументами.

Есть ли способ решить эту проблему на машине с Windows?

После долгих поисков решений я нашел абсолютно правильный способ сделать это с помощью PowerShell.

Кажется, это один из тех модулей, которые редко используются или документированы онлайн, и практически невозможно разобраться в этом самостоятельно, используя документацию MSDN, если вы уже не очень хорошо знакомы с огромным миром Wmi Objects.

Вкратце:

Предполагая, что у меня есть программа foo.exe, которая должна работать в фоновом режиме с аргументами -a, -b и bar, я должен использовать точную команду:

Invoke-WmiMethod -Path 'Win32_Process' -Name Create -ArgumentList 'C:\Users\foo\Desktop\foo.exe -a -b bar'

Конечно, если ваш exe является “известным” (либо по умолчанию, как notepad или ping), или добавленным вами, полный путь не требуется при использовании -ArgumentList, и имя exe будет достаточно (notepad, ping и др.).

Дополнительные параметры:

Invoke-WmiMethod поддерживает дополнительные параметры, такие как -Credential, -ComputerName (хороший способ использовать локальный PS для Invoke чего-то на удаленной машине без использования SSH), -Impersonation и многие другие, документированные здесь.

Объяснение синтаксиса команды и дополнительные инструменты:

  • Аргумент -Path указывает на имя WmiObject. Существует множество WmiObjects, каждый с множеством Methods и Properties.
  • Аргумент -Name указывает используемый Method.
  • Чтобы перечислить все доступные Wmi-Objects, используйте Get-WmiObject -List (очень сложно найти нужный объект таким образом).
  • Чтобы перечислить и просмотреть все доступные Win32_Process Methods и Properties, используйте
Get-WmiObject -List |where{$_.name -match '^Win32_Process$'}

Это вернет вывод следующей структуры:

   NameSpace: ROOT\cimv2

Name                                Methods              Properties
----                                -------              ----------
Win32_Process                       {Create, Terminat... {Caption, CommandLine, CreationClassName, CreationDate...}

И, конечно, использование |Select-Object -ExpandProperty (_your_property_here_) раскроет все доступные Methods, такие как Create, которые мы использовали в команде выше, и все доступные Properties (которые мы не использовали).

Мне пришлось решить эту проблему для запуска экземпляра кластера Eclipse GlassFish по протоколу SSH на Windows Server 2025. Все найденные мной решения не сработали, кроме двух — первое — это создать службу, но это не то, что нам нужно было, и второе — использовать планировщик. Окончательный скрипт powershell выглядит так:

param(
    [Parameter(Mandatory=$true)]
    [string]$BatchFilePath
)
$stdin = [System.IO.StreamReader]::new([Console]::OpenStandardInput()).ReadToEnd()
$tempFile = [System.IO.Path]::GetTempFileName()
[System.IO.File]::WriteAllText($tempFile, $stdin)
$taskName = "GlassFishInstance_" + [System.Guid]::NewGuid().ToString()
$action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c $BatchFilePath < $tempFile"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(1)
$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType S4U
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Hours 0)
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings
Start-ScheduledTask -TaskName $taskName
Start-Sleep -Seconds 5
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false

Объяснение

  • Запланированная задача не имеет доступа к оригинальному STDIN, и нам нужно было отправить что-то с другой машины, поэтому здесь мы используем временный файл.
  • Задача выполняется только один раз, через секунду
  • Задача использует того же пользователя, который бы выполнял программу в обычном режиме
  • В нашем случае это сервер, поэтому мы не хотим его останавливать до тех пор, пока не решим или это не станет неизбежным (перезагрузка)
  • После выполнения задачи мы явно отменяем ее планирование.

После завершения сессии SSH программа, в нашем случае экземпляр сервера GlassFish, продолжает работать.

.

Ответ или решение

Чтобы решить проблему завершения запущенных процессов после выхода из сеанса SSH на Windows Server 2016, следует воспользоваться возможностями PowerShell и планировщика задач Windows. В отличие от Linux, где можно использовать команды вроде nohup или screen, на Windows такие решения не работают напрямую.

Теоретическая основа

При завершении SSH-сеанса процессы, инициированные пользователем, как правило, тоже завершаются. Это связано с привязкой процесса к пользовательскому сеансу. Чтобы обойти это ограничение, необходимо запустить процесс в контексте, где его существование не зависит от пользовательского сеанса SSH. В этом нам может помочь использование объектов WMI и планировщика задач Windows.

Invoke-WmiMethod

Команда Invoke-WmiMethod используется для вызова методов WMI, что позволяет выполнять команды на системном уровне, независимо от пользовательского сеанса. Это создаёт процесс, не привязанный к сеансу текущего пользователя.

Планировщик задач Windows

Создание задания в планировщике задач предоставляет возможность выполнять скрипты независимо от текущего состояния сеанса SSH. Вы можете расписать задачу на выполнение немедленно или в любое другое время, и заданные программы будут выполнены под указанной учетной записью.

Пример решения

Для запуска программы, например, foo.exe с аргументами на сервере Windows после закрытия SSH, вы можете применить нижеследующий метод.

Пример использования Invoke-WmiMethod:

Invoke-WmiMethod -Path 'Win32_Process' -Name Create -ArgumentList 'C:\Users\foo\Desktop\foo.exe -a -b bar'

Этот подход создаёт процесс, который продолжит выполнение даже после завершения SSH-сеанса, поскольку он инициируется через WMI в собственном контексте.

Пример использования планировщика задач:

param(
    [Parameter(Mandatory=$true)]
    [string]$BatchFilePath
)
$stdin = [System.IO.StreamReader]::new([Console]::OpenStandardInput()).ReadToEnd()
$tempFile = [System.IO.Path]::GetTempFileName()
[System.IO.File]::WriteAllText($tempFile, $stdin)
$taskName = "YourTaskName_" + [System.Guid]::NewGuid().ToString()
$action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c $BatchFilePath < $tempFile"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(1)
$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType S4U
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Hours 0)
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings
Start-ScheduledTask -TaskName $taskName
Start-Sleep -Seconds 5
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false

Применение

Эти решения подходят в ситуациях, когда необходимо гарантировать, что процессы будут работать независимо от активности сеанса пользователя. Использование Invoke-WmiMethod и планировщика задач Windows позволяет создавать более автономные системы, подходящие для работы с серверными приложениями, такими как экземпляры серверов, которые должны оставаться активными после выхода из SSH.

Подводя итог, данный подход поддерживает стабильность и независимость выполнения критически важных сервисов на сервере Windows даже после завершения сеанса подключения, обеспечивая надёжность и предоставляя больше гибкости в администрировании.

Оцените материал
Добавить комментарий

Капча загружается...