You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
764 lines
20 KiB
764 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1992-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
startup.c
|
|
|
|
Abstract:
|
|
|
|
Contains routines for starting SNMP master agent.
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
10-Feb-1997 DonRyan
|
|
Rewrote to implement SNMPv2 support.
|
|
|
|
--*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Include files //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "globals.h"
|
|
#include "startup.h"
|
|
#include "network.h"
|
|
#include "registry.h"
|
|
#include "snmpthrd.h"
|
|
#include "regthrd.h"
|
|
#include "trapthrd.h"
|
|
#include "args.h"
|
|
#include "mem.h"
|
|
#include "snmpmgmt.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Global variables //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE g_hAgentThread = NULL;
|
|
HANDLE g_hRegistryThread = NULL; // Used to track registry changes
|
|
CRITICAL_SECTION g_RegCriticalSectionA;
|
|
CRITICAL_SECTION g_RegCriticalSectionB;
|
|
CRITICAL_SECTION g_RegCriticalSectionC; // protect the generation of trap from
|
|
// registry changes
|
|
// All CriticalSection inited or not, this flag is only used in this file.
|
|
static BOOL g_fCriticalSectionsInited = FALSE;
|
|
|
|
// privileges that we want to keep
|
|
static const LPCWSTR c_arrszPrivilegesToKeep[] = {
|
|
L"SeChangeNotifyPrivilege",
|
|
L"SeSecurityPrivilege"
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Private procedures //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
LoadWinsock(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Startup winsock.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Returns true if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
WSADATA WsaData;
|
|
WORD wVersionRequested = MAKEWORD(2,0);
|
|
INT nStatus;
|
|
|
|
// attempt to startup winsock
|
|
nStatus = WSAStartup(wVersionRequested, &WsaData);
|
|
|
|
// validate return code
|
|
if (nStatus == SOCKET_ERROR) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d starting winsock.\n",
|
|
WSAGetLastError()
|
|
));
|
|
|
|
// failure
|
|
return FALSE;
|
|
}
|
|
|
|
// success
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
UnloadWinsock(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shutdown winsock.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Returns true if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT nStatus;
|
|
|
|
// cleanup
|
|
nStatus = WSACleanup();
|
|
|
|
// validate return code
|
|
if (nStatus == SOCKET_ERROR) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d stopping winsock.\n",
|
|
WSAGetLastError()
|
|
));
|
|
|
|
// failure
|
|
return FALSE;
|
|
}
|
|
|
|
// success
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get all privileges to be removed except the ones specified in
|
|
c_arrszPrivilegesToKeep.
|
|
|
|
Arguments:
|
|
|
|
hToken [IN] An opened access token
|
|
ppPrivs [OUT] An array of privileges to be returned
|
|
pdwNumPrivs [OUT] Number of privileges in ppPrivs
|
|
|
|
|
|
Return Values:
|
|
|
|
Returns ERROR_SUCCESS if successful.
|
|
|
|
Note: It is caller's responsibility to call AgentMemFree on *ppPrivs if
|
|
ERROR_SUCCESS is returned and *ppPrivs is not NULL
|
|
|
|
--*/
|
|
static DWORD GetAllPrivilegesToRemoveExceptNeeded(
|
|
HANDLE hToken,
|
|
PLUID_AND_ATTRIBUTES* ppPrivs,
|
|
PDWORD pdwNumPrivs)
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
PTOKEN_PRIVILEGES pTokenPrivs = NULL;
|
|
PLUID_AND_ATTRIBUTES pPrivsToRemove = NULL;
|
|
DWORD dwPrivsToDel = 0;
|
|
DWORD dwPrivNameSize = 0;
|
|
|
|
// init return values
|
|
*ppPrivs = NULL;
|
|
*pdwNumPrivs = 0;
|
|
|
|
do
|
|
{
|
|
LPWSTR pszPrivName = NULL;
|
|
DWORD i, dwSize = 0;
|
|
|
|
GetTokenInformation(hToken,
|
|
TokenPrivileges,
|
|
NULL,
|
|
0,
|
|
&dwSize);
|
|
|
|
if (0 == dwSize)
|
|
{
|
|
// process has no privileges to be removed
|
|
return dwRet;
|
|
}
|
|
|
|
pTokenPrivs = (PTOKEN_PRIVILEGES) AgentMemAlloc(dwSize);
|
|
if (NULL == pTokenPrivs)
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
if (!GetTokenInformation(
|
|
hToken,
|
|
TokenPrivileges,
|
|
pTokenPrivs,
|
|
dwSize, &dwSize))
|
|
{
|
|
dwRet = GetLastError();
|
|
break;
|
|
}
|
|
|
|
pPrivsToRemove = (PLUID_AND_ATTRIBUTES) AgentMemAlloc(
|
|
sizeof(LUID_AND_ATTRIBUTES) * pTokenPrivs->PrivilegeCount);
|
|
|
|
if (NULL == pPrivsToRemove)
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
// LookupPrivilegeName need the buffer size in char and NOT including
|
|
// the NULL terminator
|
|
dwPrivNameSize = MAX_PATH;
|
|
pszPrivName = (LPWSTR) AgentMemAlloc((dwPrivNameSize + 1) * sizeof(WCHAR));
|
|
if (NULL == pszPrivName)
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
for (i=0; i < pTokenPrivs->PrivilegeCount; i++)
|
|
{
|
|
BOOL bFound;
|
|
DWORD j;
|
|
DWORD dwTempSize = dwPrivNameSize;
|
|
|
|
ZeroMemory(pszPrivName, (dwPrivNameSize + 1) * sizeof(WCHAR));
|
|
if (!LookupPrivilegeNameW(NULL,
|
|
&pTokenPrivs->Privileges[i].Luid,
|
|
pszPrivName,
|
|
&dwTempSize))
|
|
{
|
|
dwRet = GetLastError();
|
|
if (ERROR_INSUFFICIENT_BUFFER == dwRet && dwTempSize > dwPrivNameSize)
|
|
{
|
|
//reallocate a bigger buffer
|
|
dwRet = ERROR_SUCCESS;
|
|
AgentMemFree(pszPrivName);
|
|
pszPrivName = (LPWSTR) AgentMemAlloc((dwTempSize + 1) * sizeof(WCHAR));
|
|
if (NULL == pszPrivName)
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
// AgentMemAlloc zero'ed the allocated memory
|
|
dwPrivNameSize = dwTempSize;
|
|
|
|
//try it again
|
|
if (!LookupPrivilegeNameW(NULL,
|
|
&pTokenPrivs->Privileges[i].Luid,
|
|
pszPrivName,
|
|
&dwTempSize))
|
|
{
|
|
dwRet = GetLastError();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
bFound = FALSE;
|
|
for (j = 0;
|
|
j < sizeof(c_arrszPrivilegesToKeep)/sizeof(c_arrszPrivilegesToKeep[0]);
|
|
++j)
|
|
{
|
|
if (0 == lstrcmpiW(pszPrivName, c_arrszPrivilegesToKeep[j]))
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
continue;
|
|
|
|
pPrivsToRemove[dwPrivsToDel] = pTokenPrivs->Privileges[i];
|
|
dwPrivsToDel++;
|
|
}
|
|
|
|
// free memory if necessary
|
|
AgentMemFree(pszPrivName);
|
|
|
|
} while (FALSE);
|
|
|
|
// free memory if necessary
|
|
AgentMemFree(pTokenPrivs);
|
|
|
|
if (ERROR_SUCCESS == dwRet)
|
|
{
|
|
// transfer values
|
|
*pdwNumPrivs = dwPrivsToDel;
|
|
*ppPrivs = pPrivsToRemove;
|
|
}
|
|
else if (pPrivsToRemove)
|
|
{
|
|
AgentMemFree(pPrivsToRemove);
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
BuildTokenPrivileges
|
|
|
|
Arguments:
|
|
|
|
pPrivs [IN] An array of privileges
|
|
dwNumPrivs [IN] Number of privileges in pPrivs
|
|
ppTokenPrivs [OUT] Pointer to TOKEN_PRIVILEGES to be returned
|
|
pdwTokenPrivsBufferSize [OUT] Buffer size in bytes that *ppTokenPrivs has
|
|
|
|
Return Values:
|
|
|
|
Returns ERROR_SUCCESS if successful.
|
|
|
|
Note: It's caller's responsibility to call AgentMemFree on *ppPrivs if
|
|
ERROR_SUCCESS is returned and *ppPrivs is not NULL
|
|
|
|
--*/
|
|
static DWORD BuildTokenPrivileges(
|
|
PLUID_AND_ATTRIBUTES pPrivs,
|
|
DWORD dwNumPrivs,
|
|
DWORD dwAttributes,
|
|
PTOKEN_PRIVILEGES* ppTokenPrivs,
|
|
PDWORD pdwTokenPrivsBufferSize)
|
|
{
|
|
PTOKEN_PRIVILEGES pTokenPrivs = NULL;
|
|
DWORD i, dwBufferSize;
|
|
|
|
|
|
// design by contract, parameters have to be valid. e.g.
|
|
// dwNumPrivs > 0
|
|
// *ppTokenPrivs == NULL
|
|
// *pdwTokenPrivsBufferSize == 0
|
|
|
|
// allocate the privilege buffer
|
|
dwBufferSize = sizeof(TOKEN_PRIVILEGES) +
|
|
((dwNumPrivs-1) * sizeof(LUID_AND_ATTRIBUTES));
|
|
pTokenPrivs = (PTOKEN_PRIVILEGES) AgentMemAlloc(dwBufferSize);
|
|
if (NULL == pTokenPrivs)
|
|
{
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
// build the desired TOKEN_PRIVILEGES
|
|
pTokenPrivs->PrivilegeCount = dwNumPrivs;
|
|
for (i = 0; i < dwNumPrivs; ++i)
|
|
{
|
|
pTokenPrivs->Privileges[i].Luid = pPrivs[i].Luid;
|
|
pTokenPrivs->Privileges[i].Attributes = dwAttributes;
|
|
}
|
|
|
|
// transfer values
|
|
*ppTokenPrivs = pTokenPrivs;
|
|
*pdwTokenPrivsBufferSize = dwBufferSize;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RemoveUnnecessaryTokenPrivileges
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Values:
|
|
|
|
Returns TRUE if successful.
|
|
|
|
--*/
|
|
static BOOL RemoveUnnecessaryTokenPrivileges()
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
HANDLE hProcessToken = NULL;
|
|
PLUID_AND_ATTRIBUTES pPrivsToRemove = NULL;
|
|
DWORD dwNumPrivs = 0;
|
|
PTOKEN_PRIVILEGES pTokenPrivs = NULL;
|
|
DWORD dwTokenPrivsBufferSize = 0;
|
|
|
|
do
|
|
{
|
|
if (!OpenProcessToken( GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hProcessToken ))
|
|
{
|
|
dwRet = GetLastError();
|
|
break;
|
|
}
|
|
|
|
dwRet = GetAllPrivilegesToRemoveExceptNeeded(hProcessToken,
|
|
&pPrivsToRemove,
|
|
&dwNumPrivs);
|
|
if (ERROR_SUCCESS != dwRet )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Assert: dwRet == ERROR_SUCCESS
|
|
if ( (NULL==pPrivsToRemove) || (0==dwNumPrivs) )
|
|
{
|
|
SNMPDBG((
|
|
SNMP_LOG_VERBOSE,
|
|
"SNMP: SVC: No privileges need to be removed.\n"
|
|
));
|
|
|
|
break;
|
|
}
|
|
|
|
dwRet = BuildTokenPrivileges(pPrivsToRemove,
|
|
dwNumPrivs,
|
|
SE_PRIVILEGE_REMOVED,
|
|
&pTokenPrivs,
|
|
&dwTokenPrivsBufferSize);
|
|
if (ERROR_SUCCESS != dwRet )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!AdjustTokenPrivileges(hProcessToken,
|
|
FALSE,
|
|
pTokenPrivs,
|
|
dwTokenPrivsBufferSize,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
dwRet = GetLastError();
|
|
break;
|
|
}
|
|
} while(FALSE);
|
|
|
|
// free resources if necessary
|
|
if (hProcessToken)
|
|
CloseHandle(hProcessToken);
|
|
AgentMemFree(pPrivsToRemove);
|
|
AgentMemFree(pTokenPrivs);
|
|
|
|
if (dwRet != ERROR_SUCCESS)
|
|
{
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: RemoveUnnecessaryTokenPrivileges failed 0x%x\n",
|
|
dwRet));
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Public procedures //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
StartupAgent(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs essential initialization of master agent.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Returns true if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fOk = TRUE;
|
|
DWORD dwThreadId = 0;
|
|
DWORD regThreadId = 0;
|
|
INT nCSOk = 0; // counts the number of CS that were successfully initialized
|
|
|
|
// initialize management variables
|
|
mgmtInit();
|
|
|
|
// initialize list heads
|
|
InitializeListHead(&g_Subagents);
|
|
InitializeListHead(&g_SupportedRegions);
|
|
InitializeListHead(&g_ValidCommunities);
|
|
InitializeListHead(&g_TrapDestinations);
|
|
InitializeListHead(&g_PermittedManagers);
|
|
InitializeListHead(&g_IncomingTransports);
|
|
InitializeListHead(&g_OutgoingTransports);
|
|
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&g_RegCriticalSectionA); nCSOk++;
|
|
InitializeCriticalSection(&g_RegCriticalSectionB); nCSOk++;
|
|
InitializeCriticalSection(&g_RegCriticalSectionC); nCSOk++;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
if (nCSOk == 1)
|
|
DeleteCriticalSection(&g_RegCriticalSectionA);
|
|
if (nCSOk == 2)
|
|
{
|
|
DeleteCriticalSection(&g_RegCriticalSectionA);
|
|
DeleteCriticalSection(&g_RegCriticalSectionB);
|
|
}
|
|
// nCSOk can't be 3 as far as we are here
|
|
|
|
fOk = FALSE;
|
|
}
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Initialize critical sections...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
if (fOk)
|
|
{
|
|
g_fCriticalSectionsInited = TRUE;
|
|
}
|
|
|
|
fOk = fOk &&
|
|
(g_hRegistryEvent = CreateEvent(NULL, FALSE, TRUE, NULL)) != NULL;
|
|
|
|
|
|
g_dwUpTimeReference = SnmpSvcInitUptime();
|
|
// retreive system uptime reference
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Getting system uptime...%d\n", g_dwUpTimeReference));
|
|
|
|
// allocate essentials
|
|
fOk = fOk && AgentHeapCreate();
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Creating agent heap...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
if (fOk)
|
|
{
|
|
// Remove unnecessary privileges from the service
|
|
RemoveUnnecessaryTokenPrivileges();
|
|
// any error is ignored
|
|
}
|
|
|
|
fOk = fOk && LoadWinsock();
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Loading Winsock stack...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
fOk = fOk && LoadIncomingTransports();
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Loading Incoming transports...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
fOk = fOk && LoadOutgoingTransports();
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Loading Outgoing transports...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
fOk = fOk &&
|
|
// attempt to start main thread
|
|
(g_hAgentThread = CreateThread(
|
|
NULL, // lpThreadAttributes
|
|
0, // dwStackSize
|
|
ProcessSnmpMessages,
|
|
NULL, // lpParameter
|
|
CREATE_SUSPENDED, // dwCreationFlags
|
|
&dwThreadId
|
|
)) != NULL;
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Starting ProcessSnmpMessages thread...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
fOk = fOk &&
|
|
// attempt to start registry listener thread
|
|
(g_hRegistryThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
ProcessRegistryMessage,
|
|
NULL,
|
|
CREATE_SUSPENDED,
|
|
®ThreadId)) != NULL;
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Starting ProcessRegistryMessages thread...%s\n", fOk? "Ok" : "Failed"));
|
|
|
|
return fOk;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ShutdownAgent(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs final cleanup of master agent.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Returns true if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fOk;
|
|
DWORD dwStatus;
|
|
|
|
// make sure shutdown signalled
|
|
fOk = SetEvent(g_hTerminationEvent);
|
|
|
|
if (!fOk) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d signalling termination.\n",
|
|
GetLastError()
|
|
));
|
|
}
|
|
|
|
// check if thread created
|
|
if ((g_hAgentThread != NULL) && (g_hRegistryThread != NULL)) {
|
|
HANDLE hEvntArray[2];
|
|
|
|
hEvntArray[0] = g_hAgentThread;
|
|
hEvntArray[1] = g_hRegistryThread;
|
|
|
|
dwStatus = WaitForMultipleObjects(2, hEvntArray, TRUE, SHUTDOWN_WAIT_HINT);
|
|
|
|
// validate return status
|
|
if ((dwStatus != WAIT_OBJECT_0) &&
|
|
(dwStatus != WAIT_OBJECT_0 + 1) &&
|
|
(dwStatus != WAIT_TIMEOUT)) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d waiting for thread(s) termination.\n",
|
|
GetLastError()
|
|
));
|
|
}
|
|
} else if (g_hAgentThread != NULL) {
|
|
|
|
// wait for pdu processing thread to terminate
|
|
dwStatus = WaitForSingleObject(g_hAgentThread, SHUTDOWN_WAIT_HINT);
|
|
|
|
// validate return status
|
|
if ((dwStatus != WAIT_OBJECT_0) &&
|
|
(dwStatus != WAIT_TIMEOUT)) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d waiting for main thread termination.\n",
|
|
GetLastError()
|
|
));
|
|
}
|
|
} else if (g_hRegistryThread != NULL) {
|
|
|
|
// wait for registry processing thread to terminate
|
|
dwStatus = WaitForSingleObject(g_hRegistryThread, SHUTDOWN_WAIT_HINT);
|
|
|
|
// validate return status
|
|
if ((dwStatus != WAIT_OBJECT_0) &&
|
|
(dwStatus != WAIT_TIMEOUT)) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d waiting for registry thread termination.\n",
|
|
GetLastError()
|
|
));
|
|
}
|
|
}
|
|
|
|
if (g_fCriticalSectionsInited)
|
|
{
|
|
// in case registry processing thread hasn't terminated yet, we need
|
|
// to make sure critical section are safe for deletion and
|
|
// common resources in UnloadRegistryParameters() are still protected.
|
|
|
|
EnterCriticalSection(&g_RegCriticalSectionA);
|
|
EnterCriticalSection(&g_RegCriticalSectionB);
|
|
EnterCriticalSection(&g_RegCriticalSectionC);
|
|
}
|
|
|
|
// unload incoming transports
|
|
UnloadIncomingTransports();
|
|
|
|
// unload outgoing transports
|
|
UnloadOutgoingTransports();
|
|
|
|
// unload registry info
|
|
UnloadRegistryParameters();
|
|
|
|
// unload the winsock stack
|
|
UnloadWinsock();
|
|
|
|
// cleanup the internal management buffers
|
|
mgmtCleanup();
|
|
|
|
// nuke private heap
|
|
AgentHeapDestroy();
|
|
|
|
if (g_fCriticalSectionsInited)
|
|
{
|
|
LeaveCriticalSection(&g_RegCriticalSectionC);
|
|
LeaveCriticalSection(&g_RegCriticalSectionB);
|
|
LeaveCriticalSection(&g_RegCriticalSectionA);
|
|
|
|
// clean up critical section resources
|
|
DeleteCriticalSection(&g_RegCriticalSectionA);
|
|
DeleteCriticalSection(&g_RegCriticalSectionB);
|
|
DeleteCriticalSection(&g_RegCriticalSectionC);
|
|
}
|
|
|
|
ReportSnmpEvent(
|
|
SNMP_EVENT_SERVICE_STOPPED,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
return TRUE;
|
|
}
|