campo-sirio/SSAservice/SSAservice.cpp

250 lines
7.7 KiB
C++

#pragma region Includes
#include "SSAservice.h"
#include "ThreadPool.h"
#include "PathName.h"
#include <string>
#include <psapi.h>
#pragma comment(lib, "Psapi.lib")
#pragma endregion
///////////////////////////////////////////////////////////
// CSSAservice
///////////////////////////////////////////////////////////
BOOL CSSAservice::FindAgent(HANDLE& hFound, DWORD& pid) const
{
DWORD* aProcesses = NULL;
DWORD nItems = 0, nFound = 0, i = 0;
hFound = 0;
pid = 0;
for (nItems = 256; ; nItems *= 2)
{
DWORD cbNeeded = 0;
free(aProcesses);
aProcesses = (DWORD*)calloc(nItems, sizeof(DWORD));
if (!EnumProcesses(aProcesses, nItems*sizeof(DWORD), &cbNeeded))
{
free(aProcesses);
return FALSE;
}
nFound = cbNeeded / sizeof(DWORD);
if (nFound < nItems)
break;
}
for (i = 0; i < nFound && hFound == NULL; i++) if (aProcesses[i])
{
HANDLE hProcess = ::OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i] );
if (hProcess != NULL)
{
HMODULE hMod;
DWORD cbNeeded;
if (::EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
{
TCHAR szProcessName[MAX_PATH] = { 0 };
::GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) );
if (wcsstr(szProcessName, _agent) != NULL)
{
hFound = ::OpenProcess( SYNCHRONIZE, FALSE, aProcesses[i] ); // Waitable process handle
pid = aProcesses[i];
}
}
// Release the handle to the process.
CloseHandle( hProcess );
}
}
free(aProcesses);
return hFound != NULL;
}
BOOL CSSAservice::FindOrCreateAgent(HANDLE& hProcess, DWORD& pid) const
{
if (!FindAgent(hProcess, pid))
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si); // si.wShowWindow = SW_HIDE;
std::wstring str;
str = L"Creating instance of "; str += _agent;
WriteEventLogEntry(str.c_str());
if (CreateProcess(_agent,
NULL, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // no Handle inheritance
CREATE_NO_WINDOW | IDLE_PRIORITY_CLASS, // creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi))
{
hProcess = pi.hProcess;
pid = pi.dwProcessId;
}
else
WriteErrorLogEntry(TEXT("CreateProcess"));
}
else
{
std::wstring str;
str = L"Attaching to instance of "; str += _agent;
WriteEventLogEntry(str.c_str());
}
return hProcess != 0;
}
void CSSAservice::CheckDirectory() const
{
char full[_MAX_PATH] = {0}; ::GetModuleFileNameA(NULL, full, sizeof(full));
CPathName pn = full;
::SetCurrentDirectoryA(pn.Path());
}
CSSAservice::CSSAservice(LPCTSTR pszServiceName,
BOOL fCanStop, BOOL fCanShutdown, BOOL fCanPauseContinue)
: CServiceBase(pszServiceName, fCanStop, fCanShutdown, fCanPauseContinue), _agent(NULL)
{
m_fStopping = FALSE;
// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
throw GetLastError();
}
CSSAservice::~CSSAservice(void)
{
if (m_hStoppedEvent)
{
CloseHandle(m_hStoppedEvent);
m_hStoppedEvent = NULL;
}
}
//
// FUNCTION: CSSAservice::OnStart(DWORD, LPWSTR *)
//
// PURPOSE: The function is executed when a Start command is sent to the
// service by the SCM or when the operating system starts (for a service
// that starts automatically). It specifies actions to take when the
// service starts. In this code sample, OnStart logs a service-start
// message to the Application log, and queues the main service function for
// execution in a thread pool worker thread.
//
// PARAMETERS:
// * dwArgc - number of command line arguments
// * lpszArgv - array of command line arguments
//
// NOTE: A service application is designed to be long running. Therefore,
// it usually polls or monitors something in the system. The monitoring is
// set up in the OnStart method. However, OnStart does not actually do the
// monitoring. The OnStart method must return to the operating system after
// the service's operation has begun. It must not loop forever or block. To
// set up a simple monitoring mechanism, one general solution is to create
// a timer in OnStart. The timer would then raise events in your code
// periodically, at which time your service could do its monitoring. The
// other solution is to spawn a new thread to perform the main service
// functions, which is demonstrated in this code sample.
//
void CSSAservice::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
{
// Log a service start message to the Application log.
WriteEventLogEntry(TEXT("SSAservice Starting"));
// Queue the main service function for execution in a worker thread.
CThreadPool::QueueUserWorkItem(&CSSAservice::ServiceLoop, this);
}
//
// FUNCTION: CSSAservice::ServiceLoop(void)
//
// PURPOSE: The method performs the main function of the service. It runs
// on a thread pool worker thread.
//
void CSSAservice::ServiceLoop(void)
{
const int nSeconds = 5;
CheckDirectory();
WIN32_FIND_DATA fd = { 0 };
HANDLE hf = ::FindFirstFile(TEXT("*.ssa"), &fd);
if (hf)
{
_agent = TEXT("SSAagent.exe");
::FindClose(hf);
}
else
_agent = TEXT("Authoriz.exe");
HANDLE hProcess = 0;
DWORD pid = 0;
FindOrCreateAgent(hProcess, pid);
// Periodically check if the service is stopping.
while (hProcess && !m_fStopping)
{
DWORD ret = ::WaitForSingleObject(hProcess, nSeconds * 1000);
if (ret != WAIT_TIMEOUT)
{
WriteEventLogEntry(TEXT("Unexpected agent termination"), EVENTLOG_WARNING_TYPE);
::CloseHandle(hProcess);
FindOrCreateAgent(hProcess, pid);
}
}
if (hProcess)
{
::CloseHandle(hProcess);
HANDLE hAgent = ::OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hAgent)
{
if (::TerminateProcess(hAgent, 0))
_agent = NULL;
else
WriteErrorLogEntry(TEXT("TerminateProcess"));
::CloseHandle(hAgent);
}
else
WriteErrorLogEntry(TEXT("OpenProcess(PROCESS_TERMINATE)"));
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
//
// FUNCTION: CSSAservice::OnStop(void)
//
// PURPOSE: The function is executed when a Stop command is sent to the
// service by SCM. It specifies actions to take when a service stops
// running. In this code sample, OnStop logs a service-stop message to the
// Application log, and waits for the finish of the main service function.
//
// COMMENTS:
// Be sure to periodically call ReportServiceStatus() with
// SERVICE_STOP_PENDING if the procedure is going to take long time.
//
void CSSAservice::OnStop()
{
// Log a service stop message to the Application log.
WriteEventLogEntry(L"SSAservice Stopping");
// Indicate that the service is stopping and wait for the finish of the
// main service function (ServiceWorkerThread).
m_fStopping = TRUE;
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
throw GetLastError();
}