mirror of https://github.com/lianthony/NT4.0
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.
925 lines
25 KiB
925 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
repl.c
|
|
|
|
Abstract:
|
|
|
|
Contains ReplMain thread which will be started by the service manager.
|
|
|
|
Author:
|
|
|
|
10/31/91 madana
|
|
initial coding
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
Tab size is set to 4.
|
|
|
|
Revision History:
|
|
|
|
16-Jan-1992 JohnRo
|
|
Avoid using private logon functions.
|
|
Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
|
|
Added config list lock.
|
|
ReportStatus() should add thread ID to status being reported.
|
|
Changed ReplMain() prototype to match svc controller's needs.
|
|
PC-LINT found a bug calling ReplFinish().
|
|
Made other changes suggested by PC-LINT.
|
|
Use NetpGet{ComputerName,DomainName} to make our life easier.
|
|
24-Jan-1992 JohnRo
|
|
Changed to use LPTSTR etc.
|
|
Call ReplConfigRead() instead of Parse().
|
|
Moved current role from P_repl_sw to ReplGlobalRole.
|
|
Use REPL_ROLE_ equates just like the APIs do.
|
|
04-Feb-1992 JohnRo
|
|
Made changes suggested by PC-LINT.
|
|
09-Feb-1992 JohnRo
|
|
Use ReplChangeRole(), which allows dynamic role changes.
|
|
More changes suggested by PC-LINT.
|
|
Use FORMAT equates.
|
|
15-Feb-1992 JohnRo
|
|
ReplMain() must wait while service is running.
|
|
Added more debug output.
|
|
05-Mar-1992 JohnRo
|
|
Changed ReplMain's interface to match new service controller.
|
|
06-Mar-1992 JohnRo
|
|
Service controller sets handles to NULL on error, not -1.
|
|
06-Mar-1992 JohnRo
|
|
Avoid starting RPC server too soon.
|
|
14-Mar-1992 JohnRo
|
|
Moved trace bits from here into common/data.c
|
|
20-Mar-1992 JohnRo
|
|
Make sure UIC code is set for service controller.
|
|
24-Mar-1992 JohnRo
|
|
Tell service controller that we're pending stop, then stop.
|
|
Workaround a service controller bug (broken pipe on service stop).
|
|
Stopping service should be done via ReplChangeRole().
|
|
Fixed names to check for in ReplControlRoutine().
|
|
01-Apr-1992 JohnRo
|
|
When start fails, call ReplChangeRole() so svc ctrl stat gets updated.
|
|
More service controller workaround attempts.
|
|
20-Jul-1992 JohnRo
|
|
RAID 2252: repl should prevent export on Windows/NT.
|
|
Use PREFIX_ equates.
|
|
19-Aug-1992 JohnRo
|
|
RAID 2115: repl svc should wait while stopping or changing roles.
|
|
23-Sep-1992 JohnRo
|
|
RAID 1091: net start replicator causes breakpoint due to bad heap
|
|
address. Also set wait hint > 0.
|
|
18-Nov-1992 JohnRo
|
|
RAID 1537: Repl APIs in wrong role kill svc.
|
|
Various debug msgs cleaned-up.
|
|
04-Dec-1992 JohnRo
|
|
RAID 3844: remote NetReplSetInfo uses local machine type.
|
|
RAID 3316: Fix hang at every svc stop (was lock conflict between
|
|
ReplChangeRole and ReplStopService).
|
|
08-Dec-1992 JohnRo
|
|
RAID 3316: access violation while stopping the replicator.
|
|
05-Jan-1993 JohnRo
|
|
Repl WAN support (get rid of repl name list limits).
|
|
Made changes suggested by PC-LINT 5.0
|
|
29-Jan-1993 JohnRo
|
|
RAID 8913: repl svc should avoid hard-error popups.
|
|
04-Feb-1993 JohnRo
|
|
RAID 9914: internal error stopping repl svc.
|
|
11-Mar-1993 JohnRo
|
|
RAID 12100: stopping repl sometimes goes into infinite loop.
|
|
24-Mar-1993 JohnRo
|
|
RAID 4267: Replicator has problems when work queue gets large.
|
|
30-Mar-1993 JohnRo
|
|
Repl svc should use DBFlag in registry.
|
|
06-Apr-1993 JohnRo
|
|
More changes suggested by PC-LINT 5.0
|
|
28-Apr-1993 JohnRo
|
|
Use NetpKdPrint() where possible.
|
|
24-May-1993 JohnRo
|
|
RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
|
|
just call ExitProcess() instead.
|
|
|
|
--*/
|
|
|
|
|
|
// These must be included first:
|
|
|
|
#include <windows.h> // DWORD, CreateThread(), WaitForMultipleObjects(), etc.
|
|
#include <lmcons.h> // IN, NET_API_STATUS.
|
|
#include <rpc.h> // Needed by <rpcutil.h>.
|
|
|
|
// These may be included in any order:
|
|
|
|
#include <client.h> // RCGlobalClientListLock.
|
|
#include <config.h> // LPNET_CONFIG_HANDLE, etc.
|
|
#include <confname.h> // SECT_NT_REPLICATOR, REPL_KEYWORD_ equates.
|
|
#include <expdir.h> // ExportDir{Start,Stop}Repl routines.
|
|
#include <impdir.h> // ImportDir{Start,Stop}Repl routines.
|
|
#include <lmapibuf.h>
|
|
#include <lmerr.h> // NO_ERROR, NERR_ equates.
|
|
#include <lmrepl.h> // REPL_ROLE_ equates.
|
|
#include <lmsname.h> // SERVICE_REPL.
|
|
#include <master.h> // RMGlobalListLock.
|
|
#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
|
|
#include <netlib.h>
|
|
#include <netlock.h> // NetpCreateLock(), etc.
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <replconf.h> // ReplConfig routines.
|
|
#include <repldefs.h> // IF_DEBUG(), needed by <replgbl.h>, etc.
|
|
#include <replgbl.h> // ReplGlobal and ReplConfig variables.
|
|
#include <repllock.h> // CONFIG_DATA_LOCK_LEVEL.
|
|
#include <rpcutil.h> // NetpStartRpcServer(), etc.
|
|
#include <tstring.h> // NetpAlloc{type}From{type}(), etc.
|
|
#include <winsvc.h> // SERVICE_STATUS_HANDLE, etc.
|
|
|
|
|
|
// global "config" variables
|
|
|
|
LPNET_LOCK ReplConfigLock = NULL;
|
|
DWORD ReplConfigRole = REPL_ROLE_STOPPED; // Locked by ReplConfigLock.
|
|
TCHAR ReplConfigExportPath[PATHLEN+1]; // Ditto.
|
|
LPTSTR ReplConfigExportList = NULL; // Ditto.
|
|
TCHAR ReplConfigImportPath[PATHLEN+1]; // Ditto.
|
|
LPTSTR ReplConfigImportList = NULL; // Ditto.
|
|
TCHAR ReplConfigLogonUserName[UNLEN+1]; // Ditto.
|
|
DWORD ReplConfigInterval; // Ditto.
|
|
DWORD ReplConfigPulse; // Ditto.
|
|
DWORD ReplConfigGuardTime; // Ditto.
|
|
DWORD ReplConfigRandom; // Ditto.
|
|
|
|
HANDLE ReplGlobalClientTerminateEvent = NULL;
|
|
HANDLE ReplGlobalMasterTerminateEvent = NULL;
|
|
|
|
|
|
//
|
|
// Variables to control service startup.
|
|
//
|
|
|
|
HANDLE ReplGlobalExportStartupEvent = NULL;
|
|
HANDLE ReplGlobalImportStartupEvent = NULL;
|
|
|
|
//
|
|
// Variables to control service stop.
|
|
//
|
|
|
|
BOOL ReplGlobalIsServiceStopping = FALSE;
|
|
DWORD ReplGlobalCheckpoint = 1;
|
|
|
|
//
|
|
// client and master thread handles
|
|
//
|
|
HANDLE ReplGlobalClientThreadHandle = NULL;
|
|
HANDLE ReplGlobalMasterThreadHandle = NULL;
|
|
|
|
|
|
//
|
|
// Variables to control service error report.
|
|
//
|
|
SERVICE_STATUS_HANDLE ReplGlobalServiceHandle = (SERVICE_STATUS_HANDLE) NULL;
|
|
DWORD ReplGlobalUninstallUicCode = 0;
|
|
|
|
//
|
|
// We talk to both downlevel and NT clients, who want ANSI and Unicode
|
|
// strings respectively. So, let's maintain copies of this (presumably
|
|
// constant) data in both forms:
|
|
//
|
|
WCHAR ReplGlobalUnicodeComputerName[CNLEN+1];
|
|
WCHAR ReplGlobalUnicodeDomainName[DNLEN+1];
|
|
|
|
CHAR ReplGlobalAnsiComputerName[CNLEN+1];
|
|
CHAR ReplGlobalAnsiDomainName[DNLEN+1];
|
|
|
|
LPTSTR ReplGlobalComputerName; // points to one of the above.
|
|
LPTSTR ReplGlobalDomainName; // points to one of the above.
|
|
|
|
|
|
//
|
|
// proto defs
|
|
//
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
ReplInit(
|
|
VOID
|
|
);
|
|
|
|
DBGSTATIC VOID
|
|
ReplControlRoutine(
|
|
IN DWORD dwControl
|
|
);
|
|
|
|
DBGSTATIC VOID
|
|
ReplCleanup(
|
|
VOID
|
|
);
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
GetLocalInfo(
|
|
VOID
|
|
);
|
|
|
|
//
|
|
// main code start
|
|
//
|
|
|
|
VOID
|
|
ReplMain(
|
|
IN DWORD dwNumServicesArgs,
|
|
IN LPTSTR *lpServiceArgVectors
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Talk to service controller, read registry, and change role (which
|
|
starts import and/or export parts).
|
|
|
|
Arguments:
|
|
|
|
Ignored. (Required by service controller interface.)
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
BOOL LockedConfigData = FALSE;
|
|
DWORD NewRole;
|
|
|
|
UNREFERENCED_PARAMETER( dwNumServicesArgs );
|
|
UNREFERENCED_PARAMETER( lpServiceArgVectors );
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"**************************** STARTING REPL "
|
|
"****************************\n" ));
|
|
}
|
|
|
|
// DbgBreakPoint();
|
|
|
|
|
|
// Register control routine.
|
|
// This must be done before any calls to ReportStatus().
|
|
|
|
ReplGlobalServiceHandle = RegisterServiceCtrlHandler(
|
|
(LPTSTR) SERVICE_REPL,
|
|
ReplControlRoutine );
|
|
|
|
if (ReplGlobalServiceHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
|
|
|
// can't start service
|
|
|
|
NetpKdPrint(( PREFIX_REPL "can't start repl service, "
|
|
"RegisterServiceCtrlHandler is in error.\n" ));
|
|
|
|
// Haven't done anything to clean up, so don't.
|
|
return;
|
|
|
|
}
|
|
|
|
ReportStatus(
|
|
SERVICE_START_PENDING,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
1 ); // checkpoint
|
|
|
|
|
|
// initialize global data
|
|
|
|
ApiStatus = ReplInit();
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
ReportStatus(
|
|
SERVICE_START_PENDING,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
2 ); // checkpoint
|
|
|
|
|
|
// Read config data from registry. We just read the stuff controlling the
|
|
// service as a whole here; the client and master threads each read their
|
|
// own config data as well.
|
|
|
|
ApiStatus = ReplConfigRead(
|
|
NULL, // no server name
|
|
& NewRole,
|
|
ReplConfigExportPath,
|
|
&ReplConfigExportList, // Alloc and set ptr.
|
|
ReplConfigImportPath,
|
|
&ReplConfigImportList, // Alloc and set ptr.
|
|
ReplConfigLogonUserName,
|
|
& ReplConfigInterval,
|
|
& ReplConfigPulse,
|
|
& ReplConfigGuardTime,
|
|
& ReplConfigRandom );
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
LPNET_CONFIG_HANDLE SectionHandle = NULL;
|
|
LPWSTR ValueT = NULL;
|
|
|
|
ApiStatus = NetpOpenConfigData(
|
|
&SectionHandle,
|
|
NULL, // no server name.
|
|
(LPTSTR) SECT_NT_REPLICATOR, // section name
|
|
TRUE ); // we only want readonly access
|
|
NetpAssert( ApiStatus == NO_ERROR );
|
|
|
|
ApiStatus = NetpGetConfigValue (
|
|
SectionHandle,
|
|
(LPTSTR) REPL_KEYWORD_DBFLAG,
|
|
&ValueT );
|
|
(VOID) NetpCloseConfigData( SectionHandle );
|
|
if (ApiStatus == NO_ERROR) {
|
|
|
|
NetpAssert( ValueT != NULL );
|
|
ReplGlobalTrace = NetpAtoX( ValueT );
|
|
(VOID) NetApiBufferFree( ValueT );
|
|
|
|
} else if (ApiStatus == NERR_CfgParamNotFound) {
|
|
|
|
//
|
|
// copy default value in here.
|
|
//
|
|
|
|
// ReplGlobalTrace = 0;
|
|
} else {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplMain: unexpected error " FORMAT_API_STATUS
|
|
" from NetpGetConfigValue.\n", ApiStatus ));
|
|
// Probably harmless, so let's continue...
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
//
|
|
// Make sure role in registry is allowed with current product type.
|
|
//
|
|
if ( !ReplConfigIsRoleAllowed( NULL, NewRole ) ) {
|
|
// BUGBUG: Log this!
|
|
NetpKdPrint(( PREFIX_REPL "Role inconsistent with product type; "
|
|
" assuming role of " FORMAT_DWORD ".\n", REPL_ROLE_IMPORT ));
|
|
|
|
NewRole = REPL_ROLE_IMPORT;
|
|
|
|
// Update registry so we don't get this every time.
|
|
ApiStatus = ReplConfigWrite(
|
|
NULL, // no server name
|
|
NewRole,
|
|
ReplConfigExportPath,
|
|
ReplConfigExportList,
|
|
ReplConfigImportPath,
|
|
ReplConfigImportList,
|
|
ReplConfigLogonUserName,
|
|
ReplConfigInterval,
|
|
ReplConfigPulse,
|
|
ReplConfigGuardTime,
|
|
ReplConfigRandom );
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
ReportStatus(
|
|
SERVICE_START_PENDING,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
3 ); // checkpoint
|
|
|
|
// ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
|
|
ACQUIRE_LOCK( ReplConfigLock );
|
|
LockedConfigData = TRUE;
|
|
|
|
//
|
|
// Change the role to the one. This is where most of the work gets done,
|
|
// like starting other threads and so on. This is also where ReplConfigRole
|
|
// gets set. Also, the RPC server will be started or stopped as necessary.
|
|
// Last and not least, ReplChngRole() will inform the service controller.
|
|
// NOTE: ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
|
|
//
|
|
|
|
ApiStatus = ReplChangeRole( NewRole );
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
IF_DEBUG(REPL) {
|
|
NetpKdPrint(( PREFIX_REPL "ReplMain: changed role OK.\n" ));
|
|
}
|
|
|
|
if (LockedConfigData) {
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
LockedConfigData = FALSE;
|
|
}
|
|
|
|
IF_DEBUG( REPL ) {
|
|
NetpKdPrint(( PREFIX_REPL "ReplMain: exiting thread!!!!!!!!!!!!!!\n" ));
|
|
}
|
|
|
|
//
|
|
// That's all, folks. When somebody decides to stop the service,
|
|
// ReplControlRoutine will create the Stopper and Staller threads to do it.
|
|
//
|
|
|
|
return;
|
|
|
|
|
|
Cleanup :
|
|
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplMain: forced to cleanup; shutting down svc...; status "
|
|
FORMAT_API_STATUS "\n", ApiStatus ));
|
|
|
|
ReplGlobalUninstallUicCode = ApiStatus; // So service ctrl gets told.
|
|
|
|
if ( !LockedConfigData ) {
|
|
// Get lock for ReplChangeRole().
|
|
ACQUIRE_LOCK( ReplConfigLock );
|
|
LockedConfigData = TRUE;
|
|
}
|
|
|
|
//
|
|
// Make sure nothing is left running...
|
|
//
|
|
// Change role back to stopped, and do all other cleanup.
|
|
// NOTE: ReplChangeRole assumes caller has exclusive lock on ReplConfigLock.
|
|
(VOID) ReplChangeRole( REPL_ROLE_STOPPED );
|
|
|
|
if (LockedConfigData) {
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
}
|
|
|
|
ReplCleanup();
|
|
|
|
// Report status. This needs ReplGlobalServiceHandle one last time.
|
|
|
|
ReportStatus(
|
|
SERVICE_STOPPED,
|
|
ReplGlobalUninstallUicCode,
|
|
0, // wait hint
|
|
0 ); // checkpoint
|
|
|
|
// BUGBUG: Close ReplGlobalServiceHandle here someday?
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"**************************** ENDING REPL "
|
|
"****************************\n" ));
|
|
}
|
|
|
|
return;
|
|
|
|
} // ReplMain
|
|
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
ReplInit(
|
|
VOID
|
|
)
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
// init global variable ..
|
|
|
|
// this needs to be done because the service can be stopped and started;
|
|
// still the service .exe running and the global data live forever.
|
|
|
|
// BUGBUG: memory leak of old values?
|
|
ReplConfigExportList = NULL;
|
|
ReplConfigImportList = NULL;
|
|
|
|
ReplGlobalClientTerminateEvent = NULL;
|
|
ReplGlobalMasterTerminateEvent = NULL;
|
|
|
|
ReplGlobalExportStartupEvent = NULL;
|
|
ReplGlobalImportStartupEvent = NULL;
|
|
|
|
ReplGlobalClientThreadHandle = NULL;
|
|
ReplGlobalMasterThreadHandle = NULL;
|
|
|
|
ReplGlobalUninstallUicCode = 0;
|
|
|
|
ReplGlobalIsServiceStopping = FALSE;
|
|
ReplGlobalCheckpoint = 1;
|
|
|
|
// Init lock for config data.
|
|
ReplConfigLock = NetpCreateLock(
|
|
CONFIG_DATA_LOCK_LEVEL, (LPTSTR) TEXT("config data") );
|
|
NetpAssert( ReplConfigLock != NULL );
|
|
|
|
// Init client list lock (needed by import lock/unlock APIs even if
|
|
// import side not running). Ditto for master list lock.
|
|
RCGlobalClientListLock = NetpCreateLock(
|
|
CLIENT_LIST_LOCK_LEVEL,
|
|
(LPTSTR) TEXT("client list") );
|
|
NetpAssert( RCGlobalClientListLock != NULL );
|
|
|
|
RCGlobalDuplListLock = NetpCreateLock(
|
|
DUPL_LIST_LOCK_LEVEL,
|
|
(LPTSTR) TEXT("dupl list") );
|
|
NetpAssert( RCGlobalDuplListLock != NULL );
|
|
|
|
RMGlobalListLock = NetpCreateLock(
|
|
MASTER_LIST_LOCK_LEVEL,
|
|
(LPTSTR) TEXT("master list") );
|
|
NetpAssert( RMGlobalListLock != NULL); // BUGBUG: out of memory?
|
|
|
|
// Create startup events.
|
|
|
|
ReplGlobalExportStartupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if( ReplGlobalExportStartupEvent == NULL) {
|
|
|
|
return (GetLastError());
|
|
|
|
}
|
|
|
|
ReplGlobalImportStartupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if( ReplGlobalImportStartupEvent == NULL) {
|
|
|
|
return (GetLastError());
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Create termination events.
|
|
//
|
|
|
|
ReplGlobalClientTerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if( ReplGlobalClientTerminateEvent == NULL) {
|
|
|
|
return (GetLastError());
|
|
|
|
}
|
|
|
|
ReplGlobalMasterTerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (ReplGlobalMasterTerminateEvent == NULL) {
|
|
|
|
return (GetLastError());
|
|
|
|
}
|
|
|
|
//
|
|
// Disable the dreaded "net name deleted" popup (and all other hard
|
|
// error popups).
|
|
//
|
|
(VOID) SetErrorMode( SEM_FAILCRITICALERRORS );
|
|
|
|
//
|
|
// Get local domain name, computer name, etc.
|
|
//
|
|
|
|
NetStatus = GetLocalInfo();
|
|
if (NetStatus != NO_ERROR) {
|
|
return (NetStatus);
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
|
|
}
|
|
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
GetLocalInfo(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Retrieves (via net config helpers) local info and stores
|
|
it in a global variable. This info includes the computer name and the
|
|
domain name.
|
|
|
|
Arguments :
|
|
|
|
Return Value :
|
|
|
|
--*/
|
|
{
|
|
LPTSTR TempTStr;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
|
|
//
|
|
// First get computer name...
|
|
//
|
|
NetStatus = NetpGetComputerName( & TempTStr ); // alloc and set ptr.
|
|
if (NetStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER "GetLocalInfo() is in trouble calling "
|
|
"NetpGetComputerName(), NetStatus is "
|
|
FORMAT_API_STATUS "\n", NetStatus ));
|
|
|
|
ReplFinish( NetStatus );
|
|
|
|
return NetStatus;
|
|
}
|
|
NetpAssert( TempTStr != NULL );
|
|
|
|
NetpCopyTStrToWStr( ReplGlobalUnicodeComputerName, TempTStr );
|
|
|
|
#if defined(DBCS) && defined(UNICODE) // GetLocalInfo()
|
|
NetpCopyWStrToStrDBCS( ReplGlobalAnsiComputerName, TempTStr );
|
|
#else
|
|
NetpCopyTStrToStr( ReplGlobalAnsiComputerName, TempTStr );
|
|
#endif // defined(DBCS) && defined(UNICODE)
|
|
|
|
(void) NetApiBufferFree( TempTStr );
|
|
|
|
|
|
//
|
|
// Domain name...
|
|
//
|
|
NetStatus = NetpGetDomainName( & TempTStr ); // alloc and set ptr.
|
|
if (NetStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER "GetLocalInfo() is in trouble calling "
|
|
"NetpGetDomainName(), NetStatus is " FORMAT_API_STATUS "\n",
|
|
NetStatus ));
|
|
ReplFinish( NetStatus );
|
|
|
|
// BUGBUG: memory leak here? Not a problem 'cos we're dying?
|
|
return NetStatus;
|
|
}
|
|
NetpAssert( TempTStr != NULL );
|
|
|
|
NetpCopyTStrToWStr( ReplGlobalUnicodeDomainName, TempTStr );
|
|
|
|
#if defined(DBCS) && defined(UNICODE) // GetLocalInfo()
|
|
NetpCopyWStrToStrDBCS( ReplGlobalAnsiDomainName, TempTStr );
|
|
#else
|
|
NetpCopyTStrToStr( ReplGlobalAnsiDomainName, TempTStr );
|
|
#endif // defined(DBCS) && defined(UNICODE)
|
|
|
|
(void) NetApiBufferFree( TempTStr );
|
|
|
|
//
|
|
// Let's do a TCHAR version of each name, to make life easy.
|
|
//
|
|
#ifdef UNICODE
|
|
ReplGlobalComputerName = ReplGlobalUnicodeComputerName;
|
|
ReplGlobalDomainName = ReplGlobalUnicodeDomainName;
|
|
#else
|
|
ReplGlobalComputerName = ReplGlobalAnsiComputerName;
|
|
ReplGlobalDomainName = ReplGlobalAnsiDomainName;
|
|
#endif
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
|
|
|
|
DBGSTATIC VOID
|
|
ReplControlRoutine(
|
|
IN DWORD Control
|
|
)
|
|
{
|
|
NET_API_STATUS ApiStatus = NO_ERROR;
|
|
DWORD State = SERVICE_RUNNING;
|
|
HANDLE StallerThreadHandle = NULL;
|
|
HANDLE StopperThreadHandle = NULL;
|
|
DWORD ThreadID;
|
|
|
|
IF_DEBUG( SVCCTRL ) {
|
|
NetpKdPrint(( PREFIX_REPL "ReplControlRoutine: got control of "
|
|
FORMAT_DWORD ".\n", Control ));
|
|
}
|
|
|
|
switch (Control) {
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
State = SERVICE_STOP_PENDING;
|
|
|
|
//
|
|
// Tell service controller quickly what's going on...
|
|
//
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
1 ); // checkpoint
|
|
|
|
//
|
|
// Set global vars for stopper and staller threads.
|
|
//
|
|
|
|
ReplGlobalIsServiceStopping = TRUE;
|
|
ReplGlobalCheckpoint = 2; // Already used 1 above.
|
|
|
|
//
|
|
// Create stopper thread, which will do the real work.
|
|
// Do this BEFORE creating the staller thread, in case we don't have
|
|
// enough memory or whatever. (Otherwise, we'd have a staller which
|
|
// would run forever.)
|
|
//
|
|
IF_DEBUG( SVCCTRL ) {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplControlRoutine: creating stopper thread...\n" ));
|
|
}
|
|
StopperThreadHandle = CreateThread(
|
|
NULL, // no security attributes
|
|
(20 * 1024), // stack size in bytes (wild guess)
|
|
ReplStopper, // routine to call
|
|
NULL, // no thread parm
|
|
0, // creation flags: normal
|
|
&ThreadID);
|
|
if (StopperThreadHandle == NULL) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplControlRoutine: unable to create stopper thread!, "
|
|
"API status " FORMAT_API_STATUS "\n", ApiStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create staller thread.
|
|
//
|
|
IF_DEBUG( SVCCTRL ) {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplControlRoutine: creating staller thread...\n" ));
|
|
}
|
|
|
|
StallerThreadHandle = CreateThread(
|
|
NULL, // no security attributes
|
|
(10 * 1024), // stack size in bytes (wild guess)
|
|
ReplStaller, // routine to call
|
|
NULL, // no thread parm
|
|
0, // creation flags: normal
|
|
&ThreadID);
|
|
if (StallerThreadHandle == NULL) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplControlRoutine: unable to create staller thread!, "
|
|
"API status " FORMAT_API_STATUS "\n", ApiStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
goto Cleanup;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
|
|
//
|
|
// Report service installed
|
|
//
|
|
// Note: This depends on the service controller never doing an
|
|
// interrogate during a start_pending or stop_pending state.
|
|
// RitaW assures me (JohnRo) that this is OK.
|
|
//
|
|
// BUGBUG: Lock ReplCOnfigRole here?
|
|
if (ReplConfigRole == REPL_ROLE_STOPPED) {
|
|
State = SERVICE_STOPPED;
|
|
} else {
|
|
State = SERVICE_RUNNING;
|
|
}
|
|
|
|
ApiStatus = NO_ERROR;
|
|
goto ReportStatusAndCleanup;
|
|
|
|
default:
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
ReportStatusAndCleanup:
|
|
|
|
ReportStatus(
|
|
State,
|
|
ApiStatus,
|
|
0, // wait hint
|
|
0 ); // checkpoint
|
|
|
|
Cleanup:
|
|
|
|
if (StallerThreadHandle != NULL) {
|
|
(VOID) CloseHandle( StallerThreadHandle );
|
|
}
|
|
|
|
if (StopperThreadHandle != NULL) {
|
|
(VOID) CloseHandle( StopperThreadHandle );
|
|
}
|
|
|
|
IF_DEBUG( SVCCTRL ) {
|
|
NetpKdPrint(( PREFIX_REPL "ReplControlRoutine: returning.\n" ));
|
|
}
|
|
|
|
}
|
|
|
|
DBGSTATIC VOID
|
|
ReplCleanup(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
// Close startup event handles.
|
|
|
|
if (ReplGlobalExportStartupEvent != NULL) {
|
|
|
|
(void) CloseHandle( ReplGlobalExportStartupEvent );
|
|
|
|
}
|
|
|
|
if (ReplGlobalImportStartupEvent != NULL) {
|
|
|
|
(void) CloseHandle( ReplGlobalImportStartupEvent );
|
|
|
|
}
|
|
|
|
|
|
// Close termination event handles.
|
|
|
|
if (ReplGlobalClientTerminateEvent != NULL) {
|
|
|
|
(void) CloseHandle( ReplGlobalClientTerminateEvent );
|
|
|
|
}
|
|
|
|
if (ReplGlobalMasterTerminateEvent != NULL) {
|
|
|
|
(void) CloseHandle( ReplGlobalMasterTerminateEvent );
|
|
|
|
}
|
|
|
|
} // ReplCleanup
|
|
|
|
|
|
VOID // Never returns!
|
|
ReplStopService (
|
|
VOID
|
|
)
|
|
{
|
|
IF_DEBUG(REPL) {
|
|
NetpKdPrint(( PREFIX_REPL "ReplStopService: beginning...\n" ));
|
|
}
|
|
|
|
//
|
|
// We used to try to stop accepting RPC (API) calls here, by calling
|
|
// NetpStopRpcServer(). But that could introduce a deadlock, since
|
|
// NetpStopRpcServer() now waits for pending RPC calls to end, and one
|
|
// of the pending calls could be causing the service to stop!
|
|
//
|
|
// So, we now take advantage of the fact that we're the only service
|
|
// in this process. Why not just exit the process?
|
|
//
|
|
|
|
//
|
|
// I don't know of any code that cares, but just in case...
|
|
//
|
|
|
|
ReplGlobalIsServiceStopping = TRUE;
|
|
|
|
//
|
|
// Make sure service controller gets: stop pending and stopped.
|
|
//
|
|
|
|
ReportStatus(
|
|
SERVICE_STOP_PENDING,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
1 ); // checkpoint
|
|
|
|
ReportStatus(
|
|
SERVICE_STOPPED,
|
|
ReplGlobalUninstallUicCode,
|
|
0, // wait hint
|
|
0 ); // checkpoint
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"**************************** ENDING REPL (STATUS="
|
|
FORMAT_API_STATUS ")"
|
|
"****************************\n",
|
|
ReplGlobalUninstallUicCode
|
|
));
|
|
}
|
|
|
|
//
|
|
// Ka-boom!
|
|
//
|
|
ExitProcess( (UINT) ReplGlobalUninstallUicCode );
|
|
|
|
/*NOTREACHED*/
|
|
|
|
} // ReplStopService
|