Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,34 @@ for those Environment variable / value pairs present in this list below.

## Usage

```
ServiceMonitor.exe [<windows_service_name> [application_pool]] [-st <service_timeout>] [-at <appcmd_timeout>]
```

|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
```
Expand Down
14 changes: 6 additions & 8 deletions src/ServiceMonitor/IISConfigUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ HRESULT IISConfigUtil::BuildAppCmdCommand(const vector<pair<wstring, wstring>>&
}


HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError)
HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError, DWORD appcmdTimeoutSeconds)
{
HRESULT hr = S_OK;
STARTUPINFO si = { sizeof(STARTUPINFO) };
Expand All @@ -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))
{
//
Expand All @@ -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;
Expand Down Expand Up @@ -293,7 +293,6 @@ HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName)
envVec.emplace_back(wstring(pstrName), wstring(pstrValue));

pEqualChar[0] = L'=';

}
//
// move to next environment variable
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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))
{
Expand Down
4 changes: 2 additions & 2 deletions src/ServiceMonitor/IISConfigUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::pair<std::wstring, std::wstring>>& vecSet, std::vector<std::pair<std::wstring, std::wstring>>::iterator& envVecIter, WCHAR* pstrAppPoolName, std::wstring& pStrCmd, APPCMD_CMD_TYPE appcmdType);
BOOL FilterEnv(const std::unordered_map<std::wstring, LPTSTR>& filter, LPCTSTR strEnvName, LPCTSTR strEnvValue);
Expand Down
123 changes: 103 additions & 20 deletions src/ServiceMonitor/Main.cpp
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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 [<windows_service_name> [application_pool]] [-st <service_timeout>] [-at <appcmd_timeout>]", 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());
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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))
Expand All @@ -101,15 +184,15 @@ 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);
}
}
//
// don't capture the stop error
//
sm.StopServiceByName(argv[1]);
sm.StopServiceByName(pstrServiceName, serviceTimeout);

Finished:
if (g_hStopEvent != INVALID_HANDLE_VALUE)
Expand Down
6 changes: 3 additions & 3 deletions src/ServiceMonitor/ServiceMonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down
Binary file modified src/ServiceMonitor/ServiceMonitor.rc
Binary file not shown.
Binary file modified src/ServiceMonitor/version.h
Binary file not shown.