Taming stubborn services made easy
Batch scripts. Why on Earth do we still care about batch scripts? I could give you a lengthy explanation about why I don’t like Powershell, but it’s a hot day and I want to make it short: because it’s there and it’s okay if it gets your job done. So what’s the deal?
You sometimes have to restart services. Perhaps at night, when you’re asleep. Most of the time a simple sc stop <servicename> will do, but there also are services that start several processes (which wouldn’t probably stop if you just stop their caller), and some services just act awkwardly when you stop them: they stop and stop and stop… and never terminate, so you have to kill them. Bad time if you want to happen this automatically.
Here’s a remedy: control-service takes up to five parameters:
- -super (interactive mode only): reruns the batch as superuser, taming processes you normally wouldn’t be able to stop with your logon account
- -restart: restarts the service after stopping it
- -killall: if -killall is not set, control-service just stops the service. If it “stalls” and keeps in a “stopping” state without terminating, control-service kills the service using taskkill. If -killall is set, the service and every other task of the account specified with -account (see below) is killed. This is useful for processes that are started by the service you want to stop. Use -killall only with -account.
- -service
: specifies the name (not display name) of the service to be stopped (and re-started if -restart is set) - -account
: kills all processes running as . Use this only with -killall
Any order of command line args is valid. I didn’t invent this all by myself; the parsing of command line parameters was inspired by this discussion, and entering elevated mode was discussed here. I hope you find this useful.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
@echo off :: call: :: control-service.cmd [-super] [-restart] [-killall] -service <servicename> -account <accountname> :: parsing: see http://stackoverflow.com/questions/14286457/using-parameters-in-batch-files-at-dos-command-line :: elevated mode: see http://stackoverflow.com/questions/1894967/how-to-request-administrator-access-inside-a-batch-file/10052222#10052222 :: save script name with path; necessary due to the shifts below (otherwise %~s0 becomes tle last param) set script=%~s0 :parse if "%~1"=="" goto endparse if "%~1"=="-super" set Mode=super if "%~1"=="-restart" set Restart=yes if "%~1"=="-killall" set Killall=yes if "%~1"=="-service" set ServiceName=%2 if "%~1"=="-account" set Serviceaccount=%2 shift goto parse :endparse if "%Mode%"=="super" ( goto elevate ) else ( goto stopservice ) :elevate :: Check for permissions >nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" :: If error flag set, we do not have admin privileges. if '%errorlevel%' NEQ '0' ( echo Requesting administrative privileges... goto UACPrompt ) else ( goto gotAdmin ) :UACPrompt echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\get-admin.vbs" set params=%* echo %params% echo UAC.ShellExecute "cmd.exe", "/c %script% %params%", "", "runas", 1 >> "%temp%\get-admin.vbs" "%temp%\get-admin.vbs" del "%temp%\get-admin.vbs" exit /B :gotAdmin pushd "%CD%" CD /D "%~dp0" :stopservice sc config %ServiceName% start= demand ping -n 10 localhost>nul :LOOP1 SET /A COUNTER+=1 IF %COUNTER% GTR 3 GOTO :ERRORTEST99 echo stopping service # %COUNTER% #, please wait echo. sc stop %ServiceName% ping -n 15 localhost>nul SC query %ServiceName% | FIND "STATE" | FIND "STOPPED" If %ERRORLEVEL% EQU 1 GOTO :LOOP1 GOTO :GOON9876 :ERRORTEST99 SC query %ServiceName% | FIND "STATE" | FIND "STOP_PENDING" If %ERRORLEVEL% EQU 1 GOTO :ERROR if %Killall%=="yes" ( echo Service hangs on "stopping", now killing %Serviceaccount% processes ... taskkill /f /fi "USERNAME eq %Serviceaccount%" ) else ( echo Service hangs on "stopping", now killing %ServiceName% taskkill /f /fi "SERVICE eq %ServiceName%" ) echo. ping -n 5 localhost>nul echo. SC query %ServiceName% | FIND "STATE" | FIND "STOPPED" If %ERRORLEVEL% EQU 1 GOTO :ERROR :GOON9876 if "%Restart%"=="yes" ( goto restartservice ) else ( exit ) :RESTARTSERVICE sc start %ServiceName% ping -n 30 localhost>nul sc config %ServiceName% start= auto set Mode= set Restart= set Killall= set ServiceName= set Serviceaccount= exit :ERROR echo. echo Error stopping service. Aborting. echo. |
Download this code from GitHub.