Aserrar su servicio de Windows: una guía para "programadores no reales"


Una vez que piense en cómo convertir un script o aplicación en un servicio de Windows. Lo más probable es que la tarea no sea tan trivial, al menos la aplicación necesitará una interfaz especial para recibir comandos del sistema. Y dado que hay requisitos y limitaciones, es decir, guiones y muletas, encantadores para el corazón, para superar.


El artículo será útil para aquellos que, como yo, "no son un programador real".


¿Por qué necesito un servicio si hay tareas programadas?


A diferencia de las tareas asignadas, el servicio se ejecuta constantemente, comienza desde el inicio de la PC y puede ser controlado por las herramientas de Windows. Además, un script lanzado regularmente puede necesitar datos de un lanzamiento anterior, y puede ser útil obtener datos de fuentes externas, por ejemplo, en el caso del servidor TCP o web.


Personalmente, en los últimos cinco años tuve que crear un servicio tres veces y media:


  • Se requería crear un servicio en fail2ban para Windows 2003. que funcionara con los registros de FileZilla y Apache, y si se sospechaba que tenía fuerza bruta, bloqueaba la IP con las herramientas estándar de Windows: ipsec.
  • Un análogo del servidor telnet para versiones domésticas de Windows. Se tardó en ejecutar comandos en estaciones de trabajo remotas que ejecutaban Windows 7 Home. De hecho, el segundo intento de jugar servicios.
  • Reproductor de música para el piso comercial bajo Windows. La tarea en TK podría resolverse con la ayuda de mpd y un paquete de guiones, pero decidí: si hiciéramos guiones, ¿por qué no "apilar" al jugador yo mismo? Basado en la biblioteca BASS.dll .
  • Al elegir un servidor web con soporte para descargar archivos en Windows, una opción era HFS . No puede trabajar solo, por lo que tuvo que "empujarlo" al servicio. Como resultado, no me gustó la solución y acabo de instalar el tema Apaxy en el servidor web Apache.

Para crear un servicio, puede usar lenguajes de programación para adultos como C. Pero si no desea comunicarse con Visual Studio, tome las utilidades listas para usar. Hay soluciones pagas como FireDaemon Pro o AlwaysUp , pero tradicionalmente nos enfocaremos en las gratuitas.


La primera manera De Microsoft


Este mecanismo ya antiguo consta de dos componentes: la utilidad instsrv.exe para instalar el servicio y srvany.exe , el proceso para iniciar cualquier archivo ejecutable. Supongamos que creamos un servidor web en PowerShell usando el módulo Polaris . El guión será extremadamente simple:


New-PolarisGetRoute -Path '/helloworld' -Scriptblock { $Response.Send('Hello World!') } Start-Polaris -Port 8080 while($true) { Start-Sleep -Milliseconds 10 } 


El trabajo del llamado "servidor".


Ahora intentemos convertir el script en un servicio. Para hacer esto, descargue las herramientas del Kit de recursos de Windows , donde estarán nuestras utilidades. Comencemos instalando un servicio vacío con el comando:


 instsrv WebServ C:\temp\rktools\srvany.exe 

Donde WebServ es el nombre de nuestro nuevo servicio. Si es necesario, a través del complemento services.msc , puede especificar el usuario con el que se iniciará el servicio y permitir la interacción con el escritorio.


Ahora escribamos la ruta a nuestro script usando magia de registro. Los parámetros de servicio están en la clave de registro HKLM \ SYSTEM \ CurrentControlSet \ Services \ WebServ . En él, necesitamos agregar una nueva sección Parámetros y crear allí un parámetro de cadena Aplicación , que indique en él la ruta al archivo ejecutable. En el caso de un script de PowerShell, se verá así:


 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoProfile -File C:\temp\Polaris\server.ps1 


Servicio a medida.


Puedes correr y disfrutar.



Servicio de trabajo.


Sin embargo, este método tiene desventajas:


  • Las utilidades son antiguas, desarrolladas antes de la invención de PowerShell, UAC y otras cosas.
  • Srvany no controla el funcionamiento de la aplicación. Incluso si cae en error, el servicio continuará su trabajo como si nada hubiera pasado.
  • Tendrá que ajustar y profundizar en el registro. ¿Recuerdas que cavar en el registro no es seguro?

Por lo tanto, recurrimos a un método parcialmente desprovisto de estos problemas.


La segunda forma, casi un adulto.


Hay una utilidad llamada NSSM - Administrador de servicios sin problemas , que puede traducirse como un administrador de servicios no defectuoso . A diferencia del anterior, es compatible con el desarrollador y el código fuente se publica en el sitio. Además del método habitual, la instalación también está disponible a través del administrador de paquetes Chocolately .


Puede crear un servicio desde la línea de comandos habitual, armado con documentación en el sitio del desarrollador. Pero usaremos PowerShell. Porque podemos, por supuesto.


 $nssm = (Get-Command ./nssm).Source $serviceName = 'WebServ' $powershell = (Get-Command powershell).Source $scriptPath = 'C:\temp\Polaris\server.ps1' $arguments = '-ExecutionPolicy Bypass -NoProfile -File "{0}"' -f $scriptPath & $nssm install $serviceName $powershell $arguments & $nssm status $serviceName Start-Service $serviceName Get-Service $serviceName 


Instalación a través de PowerShell.


Para variar, verificaremos el trabajo del servicio no con el navegador, sino también a través de PowerShell utilizando el comando Invoke-RestMethod.



Y realmente funciona.


A diferencia de srvany , este método le permite reiniciar la aplicación al inicio, redirigir stdin y stdout y mucho más. En particular, si no desea escribir comandos en la línea de comandos, simplemente ejecute la GUI e ingrese los parámetros necesarios a través de una interfaz conveniente.


La GUI se inicia con el comando:


 nssm.exe install ServiceName 


Incluso puede configurar la prioridad y el uso de núcleos de procesador.


De hecho, hay muchas más oportunidades que srvany y otros análogos . De los inconvenientes, el control insuficiente sobre todo el proceso es sorprendente.


Hay falta de estaño. Por lo tanto, pasaré al método más hardcore de todos los probados.


La tercera vía AutoIT


Como soy un antiguo amante de este lenguaje de script, no pude pasar de la biblioteca llamada _Services_UDF v4 . Está equipado con abundante documentación y ejemplos, por lo que bajo el spoiler le daré de inmediato el texto completo del script resultante.


Listado de guiones

Entonces, intentemos "envolver" nuestro servicio web en él:


 #NoTrayIcon #RequireAdmin #Region #AutoIt3Wrapper_Version=Beta #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_Compile_Both=y #AutoIt3Wrapper_UseX64=y #EndRegion Dim $MainLog = @ScriptDir & "\test_service.log" #include <services.au3> #include <WindowsConstants.au3> $sServiceName="WebServ" If $cmdline[0] > 0 Then Switch $cmdline[1] Case "install", "-i", "/i" InstallService() Case "remove", "-u", "/u", "uninstall" RemoveService() Case Else ConsoleWrite(" - - - Help - - - " & @CRLF) ConsoleWrite("params : " & @CRLF) ConsoleWrite(" -i : install service" & @CRLF) ConsoleWrite(" -u : remove service" & @CRLF) ConsoleWrite(" - - - - - - - - " & @CRLF) Exit EndSwitch Else _Service_init($sServiceName) Exit EndIf Func _main($iArg, $sArgs) If Not _Service_ReportStatus($SERVICE_RUNNING, $NO_ERROR, 0) Then _Service_ReportStatus($SERVICE_STOPPED, _WinAPI_GetLastError(), 0) Exit EndIf $bServiceRunning = True $PID=Run("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoProfile -File C:\temp\Polaris\server.ps1") While $bServiceRunning _sleep(1000) WEnd ProcessClose($PID) _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 1000) DllCallbackFree($tServiceMain) DllCallbackFree($tServiceCtrl) _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0) DllClose($hAdvapi32_DLL) DllClose($hKernel32_DLL) EndFunc Func _Sleep($delay) Local $result = DllCall($hKernel32_DLL, "none", "Sleep", "dword", $delay) EndFunc Func InstallService() #RequireAdmin Local $bDebug = True If $cmdline[0] > 1 Then $sServiceName = $cmdline[2] EndIf If $bDebug Then ConsoleWrite("InstallService("&$sServiceName &"): Installing service, please wait") _Service_Create($sServiceName, $sServiceName, $SERVICE_WIN32_OWN_PROCESS, $SERVICE_AUTO_START, $SERVICE_ERROR_SEVERE, '"' & @ScriptFullPath & '"');,"",False,"","NT AUTHORITY\NetworkService") If @error Then Msgbox("","","InstallService(): Problem installing service, Error number is " & @error & @CRLF & " message : " & _WinAPI_GetLastErrorMessage()) Else If $bDebug Then ConsoleWrite("InstallService(): Installation of service successful") EndIf Exit EndFunc Func RemoveService() _Service_Stop($sServiceName) _Service_Delete($sServiceName) If Not @error Then EndIf Exit EndFunc Func _exit() _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0); EndFunc Func StopTimer() _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, $iServiceCounter) $iServiceCounter += -100 EndFunc Func _Stopping() _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 3000) EndFunc 

Analizaré con más detalle el momento en que se inicie la aplicación. Comienza después de la operación $ bServiceRunning = True y se convierte en un bucle aparentemente infinito. De hecho, este proceso se interrumpirá tan pronto como el servicio reciba una señal de finalización, ya sea que se cierre la sesión o se detenga manualmente.


Dado que el programa para el script es externo (powershell.exe), luego de salir del bucle, necesitamos terminar su trabajo usando ProcessClose .


Para hacer esto, el script debe compilarse en .exe y luego instalar el servicio ejecutando exe con el modificador -i.



Funciona!


Por supuesto, este método no es el más conveniente, y todas las características adicionales deberán implementarse independientemente, ya sea que se reinicie la aplicación en caso de falla o rotación de registros. Pero él da el control total sobre lo que está sucediendo. Y al final, puede hacer mucho más, desde notificar a Telegram sobre una falla del servicio hasta la interacción de IPC con otros programas. Y además, en un lenguaje de script, sin instalar y aprender Visual Studio.


Díganos, ¿tuvo que convertir los scripts y las aplicaciones en servicios?

Source: https://habr.com/ru/post/es421019/


All Articles