diff --git a/README.md b/README.md index 69ca473..cc83144 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,34 @@ for those Environment variable / value pairs present in this list below. ## Usage +``` +ServiceMonitor.exe [ [application_pool]] [-st ] [-at ] +``` + +|Placeholder|Description| +|---|---| +|`windows_service_name`|Name of the Windows service to monitor| +|`application_pool`|Name of the application pool to monitor; defaults to DefaultAppPool| +|`service_timeout`|The time (in seconds) it is willing to wait for the service to start or stop| +|`appcmd_timeout`|The time (in seconds) it is willing to wait for the appcmd command to execute| + +### Examples + +``` +ServiceMonitor.exe +``` +``` +ServiceMonitor.exe -st 60 +``` +``` +ServiceMonitor.exe w3svc ApplicationPool +``` +``` +ServiceMonitor.exe -at 30 +``` +``` +ServiceMonitor.exe w3svc MyAppPool -st 60 -at 30 +``` ``` .\ServiceMonitor.exe w3svc ``` diff --git a/src/ServiceMonitor/IISConfigUtil.cpp b/src/ServiceMonitor/IISConfigUtil.cpp index 7342147..70a6e26 100644 --- a/src/ServiceMonitor/IISConfigUtil.cpp +++ b/src/ServiceMonitor/IISConfigUtil.cpp @@ -199,7 +199,7 @@ HRESULT IISConfigUtil::BuildAppCmdCommand(const vector>& } -HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError) +HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError, DWORD appcmdTimeoutSeconds) { HRESULT hr = S_OK; STARTUPINFO si = { sizeof(STARTUPINFO) }; @@ -226,9 +226,9 @@ HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError) } // - // wait for at most 5 seconds to allow APPCMD finish + // wait to allow APPCMD finish (default is 5 seconds) // - WaitForSingleObject(pi.hProcess, 5000); + WaitForSingleObject(pi.hProcess, appcmdTimeoutSeconds * 1000); if ((!GetExitCodeProcess(pi.hProcess, &dwStatus) || dwStatus != 0) && (!fIgnoreError)) { // @@ -246,7 +246,7 @@ HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError) return hr; } -HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName) +HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName, DWORD appcmdTimeoutSeconds) { HRESULT hr = S_OK; LPTCH lpvEnv = NULL; @@ -293,7 +293,6 @@ HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName) envVec.emplace_back(wstring(pstrName), wstring(pstrValue)); pEqualChar[0] = L'='; - } // // move to next environment variable @@ -304,7 +303,6 @@ HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName) envVecIter = envVec.begin(); while (fMoreData) { - pstrRmCmd.clear(); fMoreData = FALSE; hr = BuildAppCmdCommand(envVec, envVecIter, pstrAppPoolName, pstrRmCmd, APPCMD_RM); @@ -323,7 +321,7 @@ HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName) // //allow appcmd to fail if it is trying to remove environment variable // - RunCommand(pstrRmCmd, TRUE); + RunCommand(pstrRmCmd, TRUE, appcmdTimeoutSeconds); } fMoreData = TRUE; @@ -348,7 +346,7 @@ HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName) // //appcmd must succeed when add new environment variables // - hr = RunCommand(pstrAddCmd, FALSE); + hr = RunCommand(pstrAddCmd, FALSE, appcmdTimeoutSeconds); if (FAILED(hr)) { diff --git a/src/ServiceMonitor/IISConfigUtil.h b/src/ServiceMonitor/IISConfigUtil.h index 8bcafaa..022099a 100644 --- a/src/ServiceMonitor/IISConfigUtil.h +++ b/src/ServiceMonitor/IISConfigUtil.h @@ -19,10 +19,10 @@ class IISConfigUtil IISConfigUtil(); ~IISConfigUtil(); HRESULT Initialize(); - HRESULT UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName); + HRESULT UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName, DWORD appcmdTimeoutSeconds); private: - HRESULT RunCommand(std::wstring& pstrCmd, BOOL fIgnoreError); + HRESULT RunCommand(std::wstring& pstrCmd, BOOL fIgnoreError, DWORD appcmdTimeoutSeconds); void Replace(std::wstring& str, std::wstring oldValue, std::wstring newValue); HRESULT BuildAppCmdCommand(const std::vector>& vecSet, std::vector>::iterator& envVecIter, WCHAR* pstrAppPoolName, std::wstring& pStrCmd, APPCMD_CMD_TYPE appcmdType); BOOL FilterEnv(const std::unordered_map& filter, LPCTSTR strEnvName, LPCTSTR strEnvValue); diff --git a/src/ServiceMonitor/Main.cpp b/src/ServiceMonitor/Main.cpp index 26d6aa4..bb6b482 100644 --- a/src/ServiceMonitor/Main.cpp +++ b/src/ServiceMonitor/Main.cpp @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -#include"stdafx.h" +#include "stdafx.h" +#include "Version.h" HANDLE g_hStopEvent = INVALID_HANDLE_VALUE; @@ -26,25 +27,112 @@ BOOL WINAPI CtrlHandle(DWORD dwCtrlType) return TRUE; } +bool isNumber(LPCWSTR strpointer) { + + int index; + + for (index = 0; index < wcslen(strpointer); index++) { + if (iswdigit(strpointer[index]) == false) { + return false; + } + } + + return true; +} + int __cdecl _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = S_OK; Service_Monitor sm = Service_Monitor(); + int argIndex; + DWORD serviceTimeout = 20; //default value - in seconds + DWORD appcmdTimeout = 5; //default value - in seconds + bool invalidSyntax = false; + int nextPositionalArgument = 1; + + WCHAR buffDrive[3], buffDirName[1024], buffFileName[1024], buffExt[25]; + _wsplitpath_s(argv[0], buffDrive, 3, buffDirName, 1024, buffFileName, 1024, buffExt, 25); + + WCHAR fileName[1024]; + wcscpy_s(fileName, buffFileName); + wcscat_s(fileName, 1024, buffExt); + + WCHAR* pstrServiceName = L"w3svc"; //default service name + WCHAR* pstrAppPoolName = L"DefaultAppPool"; // default application pool name + + for (argIndex = 1; argIndex < argc; argIndex++) { + if ((argIndex == 1) && ( + (_wcsicmp(argv[argIndex], L"-?") == 0) || + (_wcsicmp(argv[argIndex], L"/?") == 0) || + (_wcsicmp(argv[argIndex], L"--help") == 0) + )) { + invalidSyntax = true; + break; + } + else if (_wcsicmp(argv[argIndex], L"-st") == 0) { + if (argIndex + 1 >= argc) { + invalidSyntax = true; + } + else if (isNumber(argv[argIndex + 1])) { + serviceTimeout = _wtoi(argv[argIndex + 1]); + argIndex++; + } + else { + invalidSyntax = true; + } + } + else if (_wcsicmp(argv[argIndex], L"-at") == 0) { + if (argIndex + 1 >= argc) { + invalidSyntax = true; + } + else if (isNumber(argv[argIndex + 1])) { + appcmdTimeout = _wtoi(argv[argIndex + 1]); + argIndex++; + } + else { + invalidSyntax = true; + } + } + else if (nextPositionalArgument == 1) { + pstrServiceName = argv[argIndex]; + nextPositionalArgument++; + } + else if (nextPositionalArgument == 2) { + pstrAppPoolName = argv[argIndex]; + nextPositionalArgument++; + } + else { + invalidSyntax = true; + } + } - if (argc <= 1) + if (invalidSyntax) { - _tprintf(L"\nUSAGE: %s [windows service name]", argv[0]); - _tprintf(L"\n %s w3svc [application pool]", argv[0]); - _tprintf(L"\n\nOptions:"); - _tprintf(L"\n windows service name Name of the Windows service to monitor"); - _tprintf(L"\n application pool Name of the application pool to monitor; defaults to DefaultAppPool\n"); + _tprintf(L"\nServiceMonitor Version %d.%d.%d.%d", SM_MAJORNUMBER, SM_MINORNUMBER, SM_BUILDNUMBER, SM_BUILDMINORVERSION); + _tprintf(L"\n\nUSAGE:"); + _tprintf(L"\n %s [ [application_pool]] [-st ] [-at ]", fileName); + _tprintf(L"\n\nOptions:"); + _tprintf(L"\n windows_service_name Name of the Windows service to monitor"); + _tprintf(L"\n application_pool Name of the application pool to monitor; defaults to DefaultAppPool"); + _tprintf(L"\n service_timeout The time (in seconds) it is willing to wait for the service to start or stop"); + _tprintf(L"\n appcmd_timeout The time (in seconds) it is willing to wait for the appcmd command to execute"); + _tprintf(L"\n\nExamples:"); + _tprintf(L"\n %s", fileName); + _tprintf(L"\n %s w3svc", fileName); + _tprintf(L"\n %s -st 60", fileName); + _tprintf(L"\n %s w3svc ApplicationPool", fileName); + _tprintf(L"\n %s -at 30", fileName); + _tprintf(L"\n %s w3svc MyAppPool -st 60 -at 30", fileName); + goto Finished; } + _tprintf(L"Executing %s: %s [timeout: %d], ApplicationPool: %s [timeout: %d]", fileName, pstrServiceName, serviceTimeout, pstrAppPoolName, appcmdTimeout); + // iis only allow monitor on w3svc - if (_wcsicmp(argv[1], L"w3svc") == 0) + if (_wcsicmp(pstrServiceName, L"w3svc") == 0) { - hr = sm.StopServiceByName(L"w3svc"); + hr = sm.StopServiceByName(L"w3svc", serviceTimeout); if (FAILED(hr)) { hr = HRESULT_FROM_WIN32(GetLastError()); @@ -55,14 +143,9 @@ int __cdecl _tmain(int argc, _TCHAR* argv[]) // iis scenario, update the environment variable // we hardcode this behavior for now. We can add an input switch later if needed // - WCHAR* pstrAppPoolName = L"DefaultAppPool"; - if (argc > 2) - { - pstrAppPoolName = argv[2]; - } IISConfigUtil configHelper = IISConfigUtil(); - if( FAILED(hr = configHelper.Initialize()) || - FAILED(hr = configHelper.UpdateEnvironmentVarsToConfig(pstrAppPoolName))) + if (FAILED(hr = configHelper.Initialize()) || + FAILED(hr = configHelper.UpdateEnvironmentVarsToConfig(pstrAppPoolName, appcmdTimeout))) { _tprintf(L"\nFailed to update IIS configuration\n"); goto Finished; @@ -73,7 +156,7 @@ int __cdecl _tmain(int argc, _TCHAR* argv[]) NULL, // default security attributes TRUE, // manual-reset event FALSE, // initial state is nonsignaled - argv[1] // object name + pstrServiceName // object name ); if (g_hStopEvent == NULL) @@ -91,7 +174,7 @@ int __cdecl _tmain(int argc, _TCHAR* argv[]) if (g_hStopEvent != INVALID_HANDLE_VALUE) { - hr = sm.StartServiceByName(argv[1]); + hr = sm.StartServiceByName(pstrServiceName, serviceTimeout); } if (SUCCEEDED(hr)) @@ -101,7 +184,7 @@ int __cdecl _tmain(int argc, _TCHAR* argv[]) // // will stop monitoring once the stop event is set // - hr = sm.MonitoringService(argv[1], + hr = sm.MonitoringService(pstrServiceName, SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_STOP_PENDING | SERVICE_NOTIFY_PAUSED | SERVICE_NOTIFY_PAUSE_PENDING, g_hStopEvent); } @@ -109,7 +192,7 @@ int __cdecl _tmain(int argc, _TCHAR* argv[]) // // don't capture the stop error // - sm.StopServiceByName(argv[1]); + sm.StopServiceByName(pstrServiceName, serviceTimeout); Finished: if (g_hStopEvent != INVALID_HANDLE_VALUE) diff --git a/src/ServiceMonitor/ServiceMonitor.cpp b/src/ServiceMonitor/ServiceMonitor.cpp index 541d720..6875cb4 100644 --- a/src/ServiceMonitor/ServiceMonitor.cpp +++ b/src/ServiceMonitor/ServiceMonitor.cpp @@ -67,7 +67,7 @@ HRESULT Service_Monitor::StartServiceByName(LPCTSTR pServiceName, DWORD dwTimeOu // // Query service status to make sure service is in running state // - while(dwRemainTime >0) + while (dwRemainTime > 0) { DWORD dwBytes = 0; SERVICE_STATUS_PROCESS sStatus; @@ -109,7 +109,7 @@ HRESULT Service_Monitor::StartServiceByName(LPCTSTR pServiceName, DWORD dwTimeOu } Finished: - if(SUCCEEDED(hr)) + if (SUCCEEDED(hr)) { _tprintf(L"\n Service '%s' started \n", pServiceName); } @@ -211,7 +211,7 @@ HRESULT Service_Monitor::MonitoringService(LPCTSTR pServiceName, DWORD dwStatus, SERVICE_NOTIFY sNotify; hr = GetServiceHandle(pServiceName, &hService); - if(FAILED(hr)) + if (FAILED(hr)) { goto Finished; } diff --git a/src/ServiceMonitor/ServiceMonitor.rc b/src/ServiceMonitor/ServiceMonitor.rc index 7f7545a..ad73388 100644 Binary files a/src/ServiceMonitor/ServiceMonitor.rc and b/src/ServiceMonitor/ServiceMonitor.rc differ diff --git a/src/ServiceMonitor/version.h b/src/ServiceMonitor/version.h index 91b864f..708baf1 100644 Binary files a/src/ServiceMonitor/version.h and b/src/ServiceMonitor/version.h differ