800 lines
19 KiB
C++
800 lines
19 KiB
C++
/**********************************************************************/
|
|
/** Microsoft Windows NT **/
|
|
/** Copyright(c) Microsoft Corp., 1993 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
main.cxx
|
|
|
|
This module contains the main startup code for the SMTP Service.
|
|
|
|
|
|
FILE HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
JohnL ????
|
|
MuraliK 11-July-1995 Used Ipc() functions from Inetsvcs.dll
|
|
|
|
*/
|
|
|
|
#define INCL_INETSRV_INCS
|
|
#include "smtpinc.h"
|
|
#include "inetsvcs.h"
|
|
#include <metacach.hxx>
|
|
#include <dbgutil.h>
|
|
|
|
|
|
// ATL Header files
|
|
#define _ATL_NO_DEBUG_CRT
|
|
#define _ASSERTE _ASSERT
|
|
#define _WINDLL
|
|
#include "atlbase.h"
|
|
extern CComModule _Module;
|
|
#include "atlcom.h"
|
|
#undef _WINDLL
|
|
|
|
#ifdef _ATL_STATIC_REGISTRY
|
|
#include <statreg.h>
|
|
#include <statreg.cpp>
|
|
#endif
|
|
|
|
#include <atlimpl.cpp>
|
|
|
|
//
|
|
// RPC related includes
|
|
//
|
|
|
|
extern "C" {
|
|
#include <inetinfo.h>
|
|
#include <smtpsvc.h>
|
|
};
|
|
|
|
#include <smtpinet.h>
|
|
|
|
extern DWORD g_cMaxConnectionObjs;
|
|
|
|
BOOL
|
|
InitializeSmtpServiceRpc(
|
|
IN LPCSTR pszServiceName,
|
|
IN RPC_IF_HANDLE hRpcInterface
|
|
);
|
|
|
|
BOOL CleanupSmtpServiceRpc(
|
|
VOID
|
|
);
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
BOOL fAnySecureFilters = FALSE;
|
|
|
|
//
|
|
// for PDC hack
|
|
//
|
|
|
|
#define VIRTUAL_ROOTS_KEY_A "Virtual Roots"
|
|
#define HTTP_EXT_MAPS "Script Map"
|
|
#define SMTP_MODULE_NAME "smtpsvc"
|
|
|
|
//
|
|
// Global startup named event
|
|
//
|
|
DWORD GlobalInitializeStatus = 0;
|
|
BOOL g_ServiceBooted = FALSE;
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
#define INITIALIZE_IPC 0x00000001
|
|
#define INITIALIZE_SOCKETS 0x00000002
|
|
#define INITIALIZE_ACCESS 0x00000004
|
|
#define INITIALIZE_SERVICE 0x00000008
|
|
#define INITIALIZE_CONNECTIONS 0x00000010
|
|
#define INITIALIZE_DISCOVERY 0x00000020
|
|
|
|
#define INITIALIZE_RPC 0x00000040
|
|
#define INITIALIZE_GLOBALS 0x00000080
|
|
#define INITIALIZE_COMMON_DLLS 0x00000100
|
|
#define INITIALIZE_ROUTE_SORT 0x00000200
|
|
#define INITIALIZE_FIO 0x00000400
|
|
|
|
|
|
DEFINE_TSVC_INFO_INTERFACE();
|
|
|
|
DECLARE_DEBUG_PRINTS_OBJECT();
|
|
#ifndef _NO_TRACING_
|
|
#include <initguid.h>
|
|
DEFINE_GUID(IisSmtpServerGuid,
|
|
0x784d8907, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
|
|
#else
|
|
DECLARE_DEBUG_VARIABLE();
|
|
#endif
|
|
|
|
//
|
|
// The following critical section synchronizes execution in ServiceEntry().
|
|
// This is necessary because the NT Service Controller may reissue a service
|
|
// start notification immediately after we have set our status to stopped.
|
|
// This can lead to an unpleasant race condition in ServiceEntry() as one
|
|
// thread cleans up global state as another thread is initializing it.
|
|
//
|
|
|
|
CRITICAL_SECTION g_csServiceEntryLock;
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
extern VOID SmtpOnConnect( IN SOCKET sNew,
|
|
IN SOCKADDR_IN * psockaddr, //Should be SOCKADDR *
|
|
IN PVOID pEndpointContext,
|
|
IN PVOID pAtqEndpointObject );
|
|
|
|
extern VOID
|
|
SmtpOnConnectEx(
|
|
VOID * patqContext,
|
|
DWORD cbWritten,
|
|
DWORD err,
|
|
OVERLAPPED * lpo
|
|
);
|
|
|
|
VOID
|
|
SmtpCompletion(
|
|
PVOID pvContext,
|
|
DWORD cbWritten,
|
|
DWORD dwCompletionStatus,
|
|
OVERLAPPED * lpo
|
|
);
|
|
|
|
APIERR InitializeService( LPVOID pContext );
|
|
APIERR TerminateService( LPVOID pContext );
|
|
|
|
VOID TerminateInstances( PSMTP_IIS_SERVICE pService);
|
|
|
|
/************************************************************
|
|
* Symbolic Constants
|
|
************************************************************/
|
|
|
|
static TCHAR szTcpipPath[] = TEXT("System\\CurrentControlSet\\Services\\Tcpip\\Parameters");
|
|
|
|
/************************************************************
|
|
* ATL Module
|
|
************************************************************/
|
|
CComModule _Module;
|
|
|
|
BEGIN_OBJECT_MAP(ObjectMap)
|
|
END_OBJECT_MAP()
|
|
|
|
#if 0
|
|
/************************************************************
|
|
* ATL Module
|
|
************************************************************/
|
|
CComModule _Module;
|
|
|
|
BEGIN_OBJECT_MAP(ObjectMap)
|
|
END_OBJECT_MAP()
|
|
#endif
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// DllEntryPoint
|
|
//
|
|
// Synopsis:
|
|
// Arguments:
|
|
// Returns:
|
|
// See Win32 SDK
|
|
//
|
|
// History:
|
|
//
|
|
// Richard Kamicar (rkamicar) 5 January 1996
|
|
//
|
|
// Notes:
|
|
//
|
|
// If we find we need this per service, we can move it out of here..
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL WINAPI
|
|
DllEntryPoint(HINSTANCE hInst, DWORD dwReason, LPVOID lpvContext)
|
|
{
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
#ifndef _NO_TRACING_
|
|
CREATE_DEBUG_PRINT_OBJECT( SMTP_MODULE_NAME, IisSmtpServerGuid);
|
|
#else
|
|
CREATE_DEBUG_PRINT_OBJECT( SMTP_MODULE_NAME);
|
|
SET_DEBUG_FLAGS( 0);
|
|
#endif
|
|
|
|
|
|
//
|
|
// To help performance, cancel thread attach and detach notifications
|
|
//
|
|
_Module.Init(ObjectMap, hInst);
|
|
DisableThreadLibraryCalls((HMODULE) hInst);
|
|
InitializeCriticalSection( &g_csServiceEntryLock );
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
// Shutdown ATL
|
|
_Module.Term();
|
|
|
|
#ifdef _NO_TRACING_
|
|
DBG_CLOSE_LOG_FILE();
|
|
#endif
|
|
DELETE_DEBUG_PRINT_OBJECT();
|
|
|
|
DeleteCriticalSection( &g_csServiceEntryLock );
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI DllMain (HANDLE hInst, ULONG dwReason, LPVOID lpvReserve)
|
|
{
|
|
return DllEntryPoint((HINSTANCE) hInst, dwReason, lpvReserve);
|
|
}
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
VOID
|
|
ServiceEntry(
|
|
DWORD cArgs,
|
|
LPSTR pArgs[],
|
|
PTCPSVCS_GLOBAL_DATA pGlobalData // unused
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
This is the "real" entrypoint for the service. When
|
|
the Service Controller dispatcher is requested to
|
|
start a service, it creates a thread that will begin
|
|
executing this routine.
|
|
|
|
Arguments:
|
|
cArgs - Number of command line arguments to this service.
|
|
|
|
pArgs - Pointers to the command line arguments.
|
|
|
|
Returns:
|
|
None. Does not return until service is stopped.
|
|
|
|
--*/
|
|
{
|
|
APIERR err = NO_ERROR;
|
|
BOOL fInitSvcObject = FALSE;
|
|
|
|
EnterCriticalSection( &g_csServiceEntryLock );
|
|
|
|
if ( !InitCommonDlls() )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[ServiceEntry] InitCommonDlls failed! Bailing\n" ));
|
|
|
|
err = GetLastError();
|
|
LeaveCriticalSection( &g_csServiceEntryLock );
|
|
goto notify_scm;
|
|
}
|
|
|
|
InitAsyncTrace();
|
|
|
|
GlobalInitializeStatus |= INITIALIZE_COMMON_DLLS;
|
|
|
|
if (!InitializeCache()) goto exit;
|
|
|
|
GlobalInitializeStatus |= INITIALIZE_FIO;
|
|
|
|
//
|
|
// Initialize Globals
|
|
//
|
|
|
|
err = InitializeGlobals();
|
|
if ( err != NO_ERROR )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
GlobalInitializeStatus |= INITIALIZE_GLOBALS;
|
|
|
|
// Initialize the service status structure.
|
|
//
|
|
g_pInetSvc = new SMTP_IIS_SERVICE(
|
|
SMTP_SERVICE_NAME_A,
|
|
SMTP_MODULE_NAME,
|
|
SMTP_PARAMETERS_KEY,
|
|
INET_SMTP_SVC_ID,
|
|
INET_SMTP_SVCLOC_ID,
|
|
TRUE,
|
|
0,
|
|
SmtpOnConnect,
|
|
SmtpOnConnectEx,
|
|
SmtpCompletion
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// If we couldn't allocate memory for the service info struct, then the
|
|
// machine is really hosed.
|
|
//
|
|
|
|
if( ( g_pInetSvc != NULL ) && g_pInetSvc->IsActive() )
|
|
|
|
{
|
|
|
|
err = ((SMTP_IIS_SERVICE *)g_pInetSvc)->LoadAdvancedQueueingDll();
|
|
if( err != NO_ERROR )
|
|
goto exit;
|
|
|
|
|
|
fInitSvcObject = TRUE;
|
|
|
|
err = g_pInetSvc->StartServiceOperation(
|
|
SERVICE_CTRL_HANDLER(),
|
|
InitializeService,
|
|
TerminateService
|
|
);
|
|
|
|
|
|
|
|
|
|
if ( err )
|
|
{
|
|
//
|
|
// The event has already been logged
|
|
//
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"SMTP ServiceEntry: StartServiceOperation returned %d\n",
|
|
err ));
|
|
}
|
|
}
|
|
else if (g_pInetSvc == NULL)
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
err = g_pInetSvc->QueryCurrentServiceError();
|
|
}
|
|
|
|
exit:
|
|
|
|
if ( g_pInetSvc != NULL )
|
|
{
|
|
g_pInetSvc->CloseService( );
|
|
}
|
|
|
|
TerminateGlobals( );
|
|
|
|
if( GlobalInitializeStatus & INITIALIZE_FIO)
|
|
{
|
|
TerminateCache();
|
|
}
|
|
|
|
if( GlobalInitializeStatus & INITIALIZE_COMMON_DLLS)
|
|
{
|
|
TerminateCommonDlls();
|
|
}
|
|
|
|
|
|
TermAsyncTrace();
|
|
|
|
LeaveCriticalSection( &g_csServiceEntryLock );
|
|
|
|
notify_scm:
|
|
//
|
|
// We need to tell the Service Control Manager that the service
|
|
// is stopped if we haven't called g_pInetSvc->StartServiceOperation.
|
|
// 1) InitCommonDlls fails, or
|
|
// 2) InitializeGlobals failed, or
|
|
// 3) new operator failed, or
|
|
// 4) SMTP_IIS_SERVICE constructor couldn't initialize properly
|
|
//
|
|
|
|
if ( !fInitSvcObject ) {
|
|
SERVICE_STATUS_HANDLE hsvcStatus;
|
|
SERVICE_STATUS svcStatus;
|
|
|
|
hsvcStatus = RegisterServiceCtrlHandler( SMTP_SERVICE_NAME,
|
|
SERVICE_CTRL_HANDLER() );
|
|
|
|
|
|
if ( hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) {
|
|
svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
svcStatus.dwCurrentState = SERVICE_STOPPED;
|
|
svcStatus.dwWin32ExitCode = err;
|
|
svcStatus.dwServiceSpecificExitCode = err;
|
|
svcStatus.dwControlsAccepted = 0;
|
|
svcStatus.dwCheckPoint = 0;
|
|
svcStatus.dwWaitHint = 0;
|
|
|
|
SetServiceStatus( hsvcStatus, (LPSERVICE_STATUS) &svcStatus );
|
|
}
|
|
}
|
|
|
|
} // ServiceEntry
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
APIERR
|
|
InitializeService(
|
|
LPVOID pContext
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
This function initializes the various SMTP Service components.
|
|
|
|
Arguments:
|
|
lpContext - Pointer to the service object
|
|
|
|
Returns:
|
|
NO_ERROR if successful, otherwise a Win32
|
|
status code.
|
|
|
|
--*/
|
|
{
|
|
APIERR err;
|
|
DWORD dwErr = NO_ERROR;
|
|
PSMTP_IIS_SERVICE psi = (PSMTP_IIS_SERVICE)pContext;
|
|
MB mb( (IMDCOM*) psi->QueryMDObject() );
|
|
STR TempString;
|
|
|
|
char szTcpipName[MAX_PATH + 1];
|
|
BOOL bUpdatedDomain;
|
|
BOOL bUpdatedFQDN;
|
|
HRESULT hr;
|
|
|
|
g_IsShuttingDown = FALSE;
|
|
|
|
TraceFunctEnter("InitializeService");
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"initializing Smtp service\n" ));
|
|
|
|
SetLastError(NO_ERROR);
|
|
|
|
psi->StartHintFunction();
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Cannot CoInitialize, error %lu\n",
|
|
hr ));
|
|
FatalTrace(0,"Cannot CoInitialize, error %d",hr);
|
|
// TraceFunctLeave();
|
|
// return hr;
|
|
}
|
|
|
|
g_ProductType = 5;
|
|
|
|
psi->StartHintFunction();
|
|
|
|
//g_ProductType = 0;
|
|
|
|
if ( !mb.Open( "/LM/SMTPSVC/",
|
|
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"InitializeService: Cannot open path %s, error %lu\n",
|
|
"/LM/SMTPSVC/", GetLastError() ));
|
|
g_pInetSvc->ShutdownService( );
|
|
TraceFunctLeave();
|
|
return ERROR_SERVICE_DISABLED;
|
|
}
|
|
|
|
g_ServiceBooted = TRUE;
|
|
|
|
//
|
|
// Initialize the Default Domain, Fully Qualified Domain Name (FQDN) settings.
|
|
// The service will use the default TCP/IP settings in the control panel for these
|
|
// values if the user has never modified the settings.
|
|
//
|
|
|
|
DWORD tmp;
|
|
|
|
if (!mb.GetDword("", MD_UPDATED_DEFAULT_DOMAIN, IIS_MD_UT_SERVER, &tmp))
|
|
{
|
|
bUpdatedDomain = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bUpdatedDomain = !!tmp;
|
|
}
|
|
|
|
if (!mb.GetDword("", MD_UPDATED_FQDN, IIS_MD_UT_SERVER, &tmp))
|
|
{
|
|
bUpdatedFQDN = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bUpdatedFQDN = !!tmp;
|
|
}
|
|
|
|
|
|
psi->StartHintFunction();
|
|
|
|
szTcpipName[0] = '\0';
|
|
lstrcpyn(szTcpipName, g_ComputerName, MAX_PATH);
|
|
|
|
//
|
|
// will need to check against TCP/IP settings
|
|
//
|
|
HKEY hkeyTcpipParam = NULL;
|
|
DWORD SizeOfBuffer = 0;
|
|
DWORD cbOffset;
|
|
DWORD dwType;
|
|
DWORD err2;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szTcpipPath, 0, KEY_QUERY_VALUE, &hkeyTcpipParam) == ERROR_SUCCESS)
|
|
{
|
|
SizeOfBuffer = MAX_PATH;
|
|
err2 = RegQueryValueEx(hkeyTcpipParam, "Hostname", 0, &dwType, (LPBYTE)szTcpipName, &SizeOfBuffer);
|
|
if (err2 != ERROR_SUCCESS || SizeOfBuffer <= 1 || dwType != REG_SZ)
|
|
{
|
|
lstrcpyn(szTcpipName, g_ComputerName, MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
cbOffset = SizeOfBuffer - 1;
|
|
szTcpipName[cbOffset] = '.';
|
|
SizeOfBuffer = MAX_PATH - (cbOffset);
|
|
err2 = RegQueryValueEx(hkeyTcpipParam, "Domain", 0, &dwType, (LPBYTE)szTcpipName + cbOffset + 1, &SizeOfBuffer);
|
|
if (err2 != ERROR_SUCCESS || SizeOfBuffer <= 1 || dwType != REG_SZ)
|
|
{
|
|
szTcpipName[cbOffset] = '\0';
|
|
}
|
|
}
|
|
|
|
_VERIFY(RegCloseKey(hkeyTcpipParam) == ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
((SMTP_IIS_SERVICE *) g_pInetSvc)->SetTcpipName(szTcpipName);
|
|
|
|
|
|
if (!bUpdatedDomain)
|
|
{
|
|
TempString.Reset();
|
|
if(! mb.GetStr("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, &TempString) ||
|
|
TempString.IsEmpty())
|
|
{
|
|
mb.SetString("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (lstrcmpi(szTcpipName,TempString.QueryStr()))
|
|
//
|
|
// no match, update
|
|
//
|
|
{
|
|
mb.SetString("", MD_DEFAULT_DOMAIN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bUpdatedFQDN)
|
|
{
|
|
TempString.Reset();
|
|
if(! mb.GetStr("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, &TempString) ||
|
|
TempString.IsEmpty())
|
|
{
|
|
mb.SetString("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (lstrcmpi(szTcpipName,TempString.QueryStr()))
|
|
//
|
|
// no match, update
|
|
//
|
|
{
|
|
mb.SetString("", MD_FQDN_VALUE, IIS_MD_UT_SERVER, szTcpipName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!mb.GetDword("", MD_MAX_MAIL_OBJECTS, IIS_MD_UT_SERVER, &g_cMaxConnectionObjs))
|
|
{
|
|
g_cMaxConnectionObjs = 5000;
|
|
}
|
|
|
|
mb.Close();
|
|
|
|
psi->StartHintFunction();
|
|
|
|
//
|
|
// Initialize various components. The ordering of the
|
|
// components is somewhat limited. Globals should be
|
|
// initialized first, then the event logger. After
|
|
// the event logger is initialized, the other components
|
|
// may be initialized in any order with one exception.
|
|
// InitializeSockets must be the last initialization
|
|
// routine called. It kicks off the main socket connection
|
|
// thread.
|
|
//
|
|
|
|
if( err = psi->InitializeDiscovery( ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"psi->InitializeDiscovery failed, error %lu\n",
|
|
err ));
|
|
|
|
FatalTrace(0,"psi->InitializeDiscovery failed %d\n",err);
|
|
TraceFunctLeave();
|
|
return err;
|
|
}
|
|
|
|
GlobalInitializeStatus |= INITIALIZE_DISCOVERY;
|
|
|
|
if( err = psi->InitializeSockets( ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot initialize service, error %lu\n",
|
|
err ));
|
|
|
|
FatalTrace(0,"psi->InitializeSockets failed %d\n",err);
|
|
|
|
TraceFunctLeave();
|
|
return err;
|
|
}
|
|
|
|
GlobalInitializeStatus |= INITIALIZE_SOCKETS;
|
|
|
|
psi->StartHintFunction();
|
|
|
|
if(!InitializeSmtpServiceRpc(SMTP_SERVICE_NAME, smtp_ServerIfHandle))
|
|
{
|
|
err = GetLastError();
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot initialize RPC service, error %lu\n",
|
|
err ));
|
|
|
|
FatalTrace(0,"InitializeSmtpServiceRpc failed %d\n",err);
|
|
TraceFunctLeave();
|
|
return err;
|
|
}
|
|
|
|
GlobalInitializeStatus |= INITIALIZE_RPC;
|
|
|
|
//
|
|
// Reset any Service Principal Names (for Kerberos) that may have been
|
|
// registered
|
|
//
|
|
|
|
if (psi->ResetServicePrincipalNames()) {
|
|
|
|
DebugTrace(
|
|
0,
|
|
"Unable to reset Kerberos Principal Names %lu, will try later",
|
|
GetLastError());
|
|
|
|
}
|
|
|
|
//
|
|
// Read and activate all the instances configured
|
|
//
|
|
|
|
InitializeInstances( psi );
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "SMTP Service initialized\n" ));
|
|
|
|
TraceFunctLeave();
|
|
|
|
return NO_ERROR;
|
|
|
|
} // InitializeService
|
|
|
|
|
|
APIERR TerminateService(IN LPVOID pContext)
|
|
/*++
|
|
|
|
Routine:
|
|
This function cleans up the various SMTP Service components.
|
|
|
|
Arguments:
|
|
pContext - Pointer to the service object
|
|
|
|
Returns:
|
|
NO_ERROR if successful, otherwise a Win32
|
|
status code.
|
|
|
|
--*/
|
|
{
|
|
PSMTP_IIS_SERVICE psi = (PSMTP_IIS_SERVICE)pContext;
|
|
DWORD err;
|
|
|
|
TraceFunctEnter("TerminateService");
|
|
|
|
if(!g_ServiceBooted)
|
|
{
|
|
ErrorTrace(NULL, "Smtp service not started, returning");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
g_ServiceBooted = FALSE ;
|
|
|
|
g_IsShuttingDown = TRUE;
|
|
|
|
DBG_ASSERT( pContext == g_pInetSvc);
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" SMTP terminating service\n" ));
|
|
|
|
//
|
|
// Components should be terminated in reverse
|
|
// initialization order.
|
|
//
|
|
|
|
|
|
//get an exclusive lock on the server.
|
|
//this will wait until all RPCs have
|
|
//exited out of the server and then
|
|
//return
|
|
|
|
psi->AcquireServiceExclusiveLock();
|
|
psi->ReleaseServiceExclusiveLock();
|
|
|
|
TerminateInstances(psi);
|
|
|
|
g_pInetSvc->ShutdownService( );
|
|
|
|
if( GlobalInitializeStatus & INITIALIZE_DISCOVERY)
|
|
{
|
|
if ( (err = psi->TerminateDiscovery()) != NO_ERROR)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "TerminateDiscovery() failed. Error = %u\n",
|
|
err));
|
|
}
|
|
}
|
|
|
|
if( GlobalInitializeStatus & INITIALIZE_SOCKETS)
|
|
{
|
|
psi->CleanupSockets( );
|
|
}
|
|
|
|
TsFlushMetaCache(METACACHE_SMTP_SERVER_ID,TRUE);
|
|
|
|
if( GlobalInitializeStatus & INITIALIZE_RPC)
|
|
{
|
|
CleanupSmtpServiceRpc();
|
|
}
|
|
|
|
CoFreeUnusedLibraries();
|
|
CoUninitialize();
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,"service terminated\n" ));
|
|
|
|
TraceFunctLeave();
|
|
return NO_ERROR;
|
|
|
|
} // TerminateService
|
|
|
|
|
|
|