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.
1200 lines
33 KiB
1200 lines
33 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
client.c
|
|
|
|
Abstract:
|
|
|
|
Contains main module of the REPL Client.
|
|
|
|
Author:
|
|
|
|
Ported from Lan Man 2.1
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
03-Apr-1989 (yuv)
|
|
Initial Coding.
|
|
|
|
03-Oct-1991 (cliffv)
|
|
Ported to NT. Converted to NT style.
|
|
20-Jan-1992 JohnRo
|
|
Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
|
|
Added global flag for ReplTest use.
|
|
Changed to use NetLock.h (allow shared locks, for one thing).
|
|
ReportStatus() should add thread ID to status being reported.
|
|
Added RCGlobalClientListCount for use by NetrReplImportDirEnum().
|
|
Made changes suggested by PC-LINT.
|
|
27-Jan-1992 JohnRo
|
|
Changed to use LPTSTR etc.
|
|
09-Feb-1992 JohnRo
|
|
Set up to dynamically change role.
|
|
Use FORMAT equates.
|
|
15-Feb-1992 JohnRo
|
|
Added a little debug output.
|
|
22-Feb-1992 JohnRo
|
|
Mention mailslot name when _open of it fails.
|
|
PC-LINT found a bug.
|
|
Made other changes suggested by PC-LINT.
|
|
05-Mar-1992 JohnRo
|
|
Changed ReplMain's interface to match new service controller.
|
|
06-Mar-1992 JohnRo
|
|
Avoid starting RPC server too soon.
|
|
24-Mar-1992 JohnRo
|
|
Renamed many ReplGlobal vars to ReplConfig vars.
|
|
Added more comments about which thread is which.
|
|
Fixed bug where startup event wasn't being set.
|
|
Got rid of useless master and client termination codes.
|
|
01-Apr-1992 JohnRo
|
|
Avoid assertion if import startup fails.
|
|
Improve error code if wksta not started.
|
|
CliffV told me about overlapped I/O vs. termination events.
|
|
Got rid of last use of NT-specific stuff (RtlZeroMemory).
|
|
21-Aug-1992 JohnRo
|
|
RAID 3607: REPLLOCK.RP$ is being created during tree copy.
|
|
Use PREFIX_ equates.
|
|
25-Sep-1992 JohnRo
|
|
RAID 5494: repl svc does not maintain time stamp on import startup.
|
|
22-Oct-1992 jimkel
|
|
Add ClientInitImpList() to init and canon the list of machines
|
|
and domains that the importer will accept replications for.
|
|
ClinetInitImpList() is called from ReplClientInitialze().
|
|
Also, added a shared lock in NameOfValidMaster()
|
|
25-Oct-1992 jimkel
|
|
Rename masters_count and master_list[] to RCGlobalExportCount
|
|
and RCGlobalExportList[]
|
|
18-Nov-1992 JohnRo
|
|
RAID 1537: repl APIs in wrong role kill service.
|
|
More debug code...
|
|
03-Dec-1992 JohnRo
|
|
RAID 4526: Repl svc has rare memory leak (changing role).
|
|
16-Dec-1992 JohnRo
|
|
RAID 1513: Repl does not maintain ACLs (also fix timestamps).
|
|
Made changes suggested by PC-LINT 5.0
|
|
05-Jan-1993 JohnRo
|
|
RAID 6763: Repl WAN support (get rid of repl name list limits).
|
|
17-Feb-1993 JohnRo
|
|
RAID 11365: Fixed various mailslot size problems.
|
|
Minor debug output changes here and there.
|
|
One more change suggested by PC-LINT 5.0
|
|
26-Mar-1993 JohnRo
|
|
RAID 4267: Replicator has problems when work queue gets large.
|
|
Actually _close the mailslot handle on exit.
|
|
Added yet another assert.
|
|
30-Mar-1993 JohnRo
|
|
Reduce checksum frequency by using change notify on import tree.
|
|
11-May-1993 JohnRo
|
|
RAID 895: Repl error 298 (ERROR_TOO_MANY_POSTS).
|
|
24-May-1993 JohnRo
|
|
RAID 10587: repl could deadlock with changed NetpStopRpcServer(), so
|
|
just call ExitProcess() instead.
|
|
Made changes suggested by PC-LINT 5.0
|
|
22-Nov-1994 JonN
|
|
RAID 27287: Memory leak in Replicator (LMREPL.EXE)
|
|
ReplEnableChangeNotify is being called even though there is still
|
|
an outstanding notify request on ChangeHandle, this causes multiple
|
|
requests to build up and leaks paged pool. Added boolean
|
|
NotifyPending to keep this at only a single request at a time.
|
|
|
|
--*/
|
|
|
|
|
|
#include <nt.h> // NT definitions (needed by chngnot.h)
|
|
#include <ntrtl.h> // NT runtime library definitions (needed by nturtl.h)
|
|
#include <nturtl.h> // Needed to have windows.h and nt.h co-exist.
|
|
#define NOMINMAX // Let stdlib.h define min and max
|
|
#include <windows.h> // DWORD, ReadFile(), etc.
|
|
#include <lmcons.h> // NET_API_STATUS, IN, OUT, etc.
|
|
|
|
#include <alertmsg.h> // ALERT_* defines
|
|
#include <chngnot.h> // ReplSetupChangeNotify, REPL_CHANGE_NOTIFY_HANDLE, etc
|
|
#include <confname.h> // REPL_KEYWORD_ equates.
|
|
#include <icanon.h> // I_NetPathCompare
|
|
#include <limits.h> // LONG_MAX.
|
|
#include <lmerr.h> // NO_ERROR, ERROR_, NERR_ equates.
|
|
#include <lmerrlog.h> // NELOG_* defines
|
|
#include <lmsname.h> // SERVICE_WORKSTATION.
|
|
#include <netdebug.h> // DBGSTATIC ..
|
|
#include <netlib.h> // NetpMemoryAllocate(), NetpIsServerStarted().
|
|
#include <netlock.h> // Lock data types, functions, and macros.
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <replp.h> // NetpReplTimeNow().
|
|
#include <stdlib.h> // rand()
|
|
#include <string.h> // memset().
|
|
#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
|
|
#include <tstr.h> // STRLEN(), etc.
|
|
#include <winsvc.h> // SERVICE_ equates, etc.
|
|
|
|
//
|
|
// Local include files
|
|
//
|
|
#include <repldefs.h> // IF_DEBUG(), etc.
|
|
#include <replgbl.h> // ReplGlobal and ReplConfig variables.
|
|
#include <repllock.h> // LOCK_LEVEL equates.
|
|
|
|
#define CLIENT_ALLOCATE // Allocate storage for all client global variables
|
|
|
|
#include <client.h>
|
|
|
|
//
|
|
// Global Data Declarations:
|
|
//
|
|
|
|
DBGSTATIC HANDLE ReplGlobalClientMailslotHandle = (HANDLE)(-1);
|
|
|
|
|
|
|
|
DBGSTATIC VOID
|
|
ReplClientCleanup(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes care of graceful termination.
|
|
Gets rid of all resources owned.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
HANDLE WaitHandles[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
|
|
DWORD HandleCount = 0;
|
|
|
|
//
|
|
// Tell the syncer and watchd threads that they should stop.
|
|
//
|
|
|
|
(VOID) SetEvent( ReplGlobalClientTerminateEvent );
|
|
|
|
//
|
|
// Wait for all our child threads to complete.
|
|
//
|
|
|
|
if ( RCGlobalSyncerThread != NULL ) {
|
|
|
|
WaitHandles[HandleCount++] = RCGlobalSyncerThread;
|
|
|
|
}
|
|
|
|
if ( RCGlobalWatchdThread != NULL ) {
|
|
|
|
WaitHandles[HandleCount++] = RCGlobalWatchdThread;
|
|
|
|
}
|
|
|
|
if ( HandleCount != 0 ) {
|
|
|
|
(VOID) WaitForMultipleObjects( HandleCount, WaitHandles, TRUE,
|
|
(DWORD) -1 );
|
|
|
|
}
|
|
|
|
|
|
// close thread handles
|
|
|
|
if ( RCGlobalSyncerThread != NULL ) {
|
|
|
|
(void) CloseHandle( RCGlobalSyncerThread );
|
|
|
|
}
|
|
|
|
if ( RCGlobalWatchdThread != NULL ) {
|
|
|
|
(void) CloseHandle( RCGlobalWatchdThread );
|
|
|
|
}
|
|
|
|
//
|
|
// Close the mailslot handle
|
|
//
|
|
|
|
if ( ReplGlobalClientMailslotHandle != (HANDLE)(-1) ) {
|
|
|
|
(VOID) CloseHandle( ReplGlobalClientMailslotHandle );
|
|
|
|
}
|
|
|
|
|
|
if ( RCGlobalWorkQueueSemaphore != INVALID_HANDLE_VALUE ) {
|
|
|
|
(VOID) CloseHandle( RCGlobalWorkQueueSemaphore );
|
|
|
|
}
|
|
|
|
// clean export list memory
|
|
|
|
if ( RCGlobalExportCount != 0 ) {
|
|
|
|
NetpAssert( (RCGlobalExportList[0]) != NULL );
|
|
NetpMemoryFree( RCGlobalExportList[0] );
|
|
|
|
}
|
|
|
|
if( RCGlobalLockInitialized ) {
|
|
|
|
NetpDeleteLock( RCGlobalWorkQueueLock );
|
|
NetpDeleteLock( RCGlobalPoolLock );
|
|
NetpDeleteLock( RCGlobalDelayListLock );
|
|
|
|
}
|
|
|
|
// free pool resource
|
|
ReplClientFreePools();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGSTATIC DWORD
|
|
Randomize(
|
|
IN DWORD limit
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a random # in the range 0 - limit.
|
|
|
|
Arguments:
|
|
|
|
limit - maximum value to return
|
|
|
|
Return Value:
|
|
|
|
Returns a random # in the range 0 - limit.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD j;
|
|
|
|
if ((limit) && (j = ( ((DWORD)rand()) % limit))) {
|
|
return j;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DBGSTATIC BOOL
|
|
NameOfValidMaster(
|
|
IN LPTSTR sender,
|
|
IN LPTSTR domain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if incoming message should be accepted.
|
|
|
|
Arguments:
|
|
|
|
sender - sender's computer name
|
|
|
|
domain - sender's domain name.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Message should be accepted.
|
|
|
|
FALSE - Message should not be accepted.
|
|
|
|
Threads:
|
|
|
|
Only called by client thread.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
//
|
|
// If a list of masters was configured,
|
|
// allow this master if either the sender name or domain name is on
|
|
// the list.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock ); // get shared lock on list
|
|
|
|
if ( RCGlobalExportCount != 0 ) {
|
|
|
|
for (i = 0; i < RCGlobalExportCount; i++) {
|
|
|
|
if ( ReplNetNameCompare(NULL,
|
|
sender,
|
|
RCGlobalExportList[i],
|
|
NAMETYPE_COMPUTER,
|
|
0L
|
|
) == 0 ||
|
|
ReplNetNameCompare(NULL,
|
|
domain,
|
|
RCGlobalExportList[i],
|
|
NAMETYPE_DOMAIN,
|
|
0L) == 0) {
|
|
|
|
// Name found, so release lock and exit
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If no list was configured, just allow masters from our primary domain.
|
|
//
|
|
|
|
} else if ( ReplNetNameCompare(NULL,
|
|
ReplGlobalDomainName,
|
|
domain,
|
|
NAMETYPE_DOMAIN,
|
|
0L
|
|
) == 0 ) {
|
|
|
|
// name found release lock and exit
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
return TRUE;
|
|
}
|
|
|
|
RELEASE_LOCK( ReplConfigLock ); // name not found release lock and exit
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
ReplClientInitialize(
|
|
IN BOOL ServiceIsStarting
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialization for the REPL service client.
|
|
|
|
Arguments:
|
|
|
|
ServiceIsStarting - TRUE iff we're still starting the service.
|
|
|
|
Return Value:
|
|
|
|
return NO_ERROR if initialization is successful.
|
|
error code otherwise
|
|
|
|
Threads:
|
|
|
|
Only called by client thread.
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME time;
|
|
DWORD ThreadId;
|
|
|
|
NET_API_STATUS NetStatus;
|
|
DWORD State;
|
|
TCHAR MailslotName[FULL_SLOT_NAME_SIZE];
|
|
|
|
IF_DEBUG( REPL ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientInitialize: thread ID is "
|
|
FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
|
|
}
|
|
|
|
if (ServiceIsStarting) {
|
|
State = SERVICE_START_PENDING;
|
|
} else {
|
|
State = SERVICE_RUNNING;
|
|
}
|
|
|
|
//
|
|
// Initialize all globals to their initial value.
|
|
// This must be done at runtime since this address space can
|
|
// be shared by other services.
|
|
//
|
|
|
|
RCGlobalClientListHeader = NULL;
|
|
RCGlobalClientListCount = 0;
|
|
RCGlobalWorkQueueSemaphore = NULL;
|
|
RCGlobalWorkQueueHead = NULL;
|
|
RCGlobalWorkQueueTail = NULL;
|
|
RCGlobalSyncerThread = NULL;
|
|
RCGlobalWatchdThread = NULL;
|
|
RCGlobalDelayListHeader = NULL;
|
|
RCGlobalTimeOfLastChangeNotify = NetpReplTimeNow();
|
|
|
|
RCGlobalLockInitialized = FALSE;
|
|
|
|
RCGlobalExportCount = 0;
|
|
|
|
//
|
|
// Initialize the state of the service.
|
|
//
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR, // exit code
|
|
REPL_WAIT_HINT,
|
|
21 ); // check point
|
|
|
|
//
|
|
// Creates Client mailslot - where Client receives Masters messages.
|
|
//
|
|
// Creates mailslot \MAILSLOT\NET\REPL_CLI,
|
|
//
|
|
// If a one exists with the same name
|
|
// or any other system NetStatus, Exits.
|
|
//
|
|
|
|
(void) STRCPY( MailslotName, SLASH_SLASH );
|
|
(void) STRCAT( MailslotName, DOT );
|
|
(void) STRCAT( MailslotName, (LPTSTR) CLIENT_SLOT_NAME );
|
|
|
|
IF_DEBUG( CLIENT ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientInitialize: opening " FORMAT_LPTSTR ".\n",
|
|
MailslotName ));
|
|
}
|
|
|
|
|
|
ReplGlobalClientMailslotHandle = CreateMailslot(
|
|
MailslotName,
|
|
MAX_2_MSLOT_SIZE,
|
|
(DWORD) MAILSLOT_WAIT_FOREVER, // readtimeout
|
|
NULL ); // security attributes
|
|
|
|
if ( ReplGlobalClientMailslotHandle == (HANDLE)(-1) ) {
|
|
|
|
// Find out why mailslot failed. Perhaps wksta not started?
|
|
if ( !NetpIsServiceStarted( (LPTSTR) SERVICE_WORKSTATION ) ) {
|
|
NetStatus = NERR_WkstaNotStarted;
|
|
} else {
|
|
NetStatus = (NET_API_STATUS) GetLastError();
|
|
}
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
ReplFinish( NetStatus );
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
22 ); // checkpoint
|
|
|
|
|
|
//
|
|
// Creates Work Queue - written by ClientMain and Watchd, read by Syncer.
|
|
//
|
|
|
|
// Initialize all locks at one place. See ReplLock.h and NetLock.h for
|
|
// details.
|
|
|
|
RCGlobalWorkQueueLock = NetpCreateLock(
|
|
WORK_QUEUE_LOCK_LEVEL,
|
|
(LPTSTR) TEXT("work queue") );
|
|
NetpAssert( RCGlobalWorkQueueLock != NULL );
|
|
|
|
RCGlobalPoolLock = NetpCreateLock(
|
|
POOL_LOCK_LEVEL,
|
|
(LPTSTR) TEXT("pool") );
|
|
NetpAssert( RCGlobalPoolLock != NULL );
|
|
|
|
RCGlobalDelayListLock = NetpCreateLock(
|
|
DELAY_LIST_LOCK_LEVEL,
|
|
(LPTSTR) TEXT("delay list") );
|
|
NetpAssert( RCGlobalDelayListLock != NULL );
|
|
|
|
RCGlobalLockInitialized = TRUE;
|
|
|
|
RCGlobalWorkQueueSemaphore = CreateSemaphoreW(
|
|
NULL, // No security attributes
|
|
0, // Initially no entries in queue
|
|
LONG_MAX, // Allow queue to be big enough
|
|
NULL ); // No name
|
|
|
|
if ( RCGlobalWorkQueueSemaphore == NULL ) {
|
|
NetStatus = GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
ReplFinish( NetStatus );
|
|
return NetStatus;
|
|
}
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
23 ); // checkpoint
|
|
|
|
|
|
//
|
|
// Initialize the Delay List
|
|
//
|
|
|
|
RCGlobalDelayListHeader = NULL;
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
24 ); // checkpoint
|
|
|
|
//
|
|
// Initialize the client importer list. The list of
|
|
// machines and domains that the client will accept import
|
|
// messages from.
|
|
//
|
|
|
|
|
|
// NOTE: ReplInitAnyList() assumes caller has config data locked.
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
NetStatus = ReplInitAnyList(
|
|
(LPCTSTR) ReplConfigImportList, // uncanon
|
|
& RCGlobalExportList, // canon list: alloc and set ptr
|
|
(LPCTSTR) REPL_KEYWORD_IMPLIST, // config keyword name
|
|
& RCGlobalExportCount ); // set entry count too
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
|
|
if (NetStatus != NO_ERROR) {
|
|
|
|
return (NetStatus);
|
|
|
|
}
|
|
|
|
//
|
|
// Init random number generator and file system resolution.
|
|
//
|
|
|
|
GetSystemTime( &time );
|
|
srand( time.wMilliseconds );
|
|
|
|
RCGlobalFsTimeResolutionSecs =
|
|
ReplGetFsTimeResolutionSecs( ReplConfigImportPath );
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
25 ); // checkpoint
|
|
|
|
|
|
//
|
|
// Clean up following crash in the middle of a sync.
|
|
//
|
|
|
|
NetStatus = ReplCrashRecovery();
|
|
if ( NetStatus != NO_ERROR ) {
|
|
// ReplCrashRecovery has already called ReplFinish.
|
|
return (NetStatus);
|
|
}
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
26 ); // checkpoint
|
|
|
|
|
|
//
|
|
// ReplClientInitLists allocates buffers and sets state to NO MASTER
|
|
// for each dir under IMPORT. This must be done after calling
|
|
// ReplCrashRecovery, so we get state for the recovered directory
|
|
// intead of TMPTREE[X].RP$
|
|
//
|
|
|
|
NetStatus = ReplClientInitLists();
|
|
if (NetStatus != NO_ERROR) {
|
|
ReplFinish( NetStatus );
|
|
return (NetStatus);
|
|
}
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
27 ); // checkpoint
|
|
|
|
|
|
//
|
|
// Creates Syncer thread.
|
|
//
|
|
|
|
RCGlobalSyncerThread = CreateThread( NULL, // No Security
|
|
CLIENT_SYNCER_STACK_SIZE,
|
|
ReplSyncerThread,
|
|
NULL,
|
|
0, // No creation flags
|
|
&ThreadId );
|
|
|
|
if ( RCGlobalSyncerThread == NULL ) {
|
|
|
|
NetStatus = GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
ReplFinish( NetStatus );
|
|
return NetStatus;
|
|
}
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
28 ); // checkpoint
|
|
|
|
|
|
|
|
//
|
|
// Creates Watchdog thread.
|
|
//
|
|
|
|
RCGlobalWatchdThread = CreateThread( NULL, // No Security
|
|
CLIENT_WATCHD_STACK_SIZE,
|
|
ReplWatchdThread,
|
|
NULL,
|
|
0, // No creation flags
|
|
&ThreadId );
|
|
|
|
if ( RCGlobalWatchdThread == NULL ) {
|
|
|
|
NetStatus = GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
ReplFinish( NetStatus );
|
|
return NetStatus;
|
|
}
|
|
|
|
ReportStatus(
|
|
State,
|
|
NO_ERROR,
|
|
REPL_WAIT_HINT,
|
|
29 ); // checkpoint
|
|
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ReplClientMain(
|
|
IN LPVOID parm
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main entry point for the Replicator Client.
|
|
|
|
Arguments:
|
|
|
|
parm - NULL iff service is starting (non-NULL if role is changing).
|
|
|
|
Return Value:
|
|
|
|
exit error.
|
|
|
|
Threads:
|
|
|
|
Only called by client thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
LPREPL_CHANGE_NOTIFY_HANDLE ChangeHandle = NULL;
|
|
BOOL NotifyPending = FALSE;
|
|
BOOL ReadPending = FALSE;
|
|
DWORD EventTriggered;
|
|
BOOL ServiceIsStarting;
|
|
HANDLE WaitHandles[3];
|
|
|
|
ServiceIsStarting = (parm == NULL);
|
|
|
|
//
|
|
// Perform all Client side initialization.
|
|
//
|
|
|
|
ApiStatus = ReplClientInitialize(ServiceIsStarting);
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientMain: ReplClientInitialize failed, stat="
|
|
FORMAT_API_STATUS ".\n", ApiStatus ));
|
|
|
|
goto Cleanup; // Don't forget to close handles.
|
|
}
|
|
|
|
//
|
|
// Arrange to use change notify on import path.
|
|
//
|
|
ApiStatus = ReplSetupChangeNotify(
|
|
ReplConfigImportPath,
|
|
&ChangeHandle );
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
NetpAssert( ChangeHandle != NULL );
|
|
|
|
|
|
#if DBG
|
|
RCGlobalClientThreadInit = TRUE; // only used by ReplTest stuff
|
|
#endif
|
|
|
|
// Tell ReplChangeRole/ImportDirStartRepl that the client is done with setup
|
|
|
|
IF_DEBUG( CLIENT ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientMain----------> setting startup event\n" ));
|
|
}
|
|
|
|
if( !SetEvent(ReplGlobalImportStartupEvent) ) {
|
|
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
AlertLogExit(ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
EXIT);
|
|
|
|
goto Cleanup; // Don't forget to close handles.
|
|
}
|
|
|
|
|
|
WaitHandles[0] = ReplGlobalClientTerminateEvent ;
|
|
WaitHandles[1] = ReplGlobalClientMailslotHandle;
|
|
WaitHandles[2] = ChangeHandle->WaitableHandle;
|
|
|
|
//
|
|
// Loop forever reading the ClientMailslot and waiting terminate event.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
DWORD bytes_read;
|
|
DWORD bytes_read2;
|
|
BYTE msg_buf[ MAX_2_MSLOT_SIZE ];
|
|
DWORD msg_buf2[
|
|
(MAX_REPL_MAILSLOT_EXPANSION * MAX_2_MSLOT_SIZE / sizeof(DWORD))
|
|
+ 1
|
|
];
|
|
// align to DWORD boundary
|
|
|
|
PSYNCMSG msg;
|
|
|
|
PBIGBUF_REC que_buf;
|
|
OVERLAPPED OverLapped;
|
|
|
|
// Enable this pass of change notify...
|
|
|
|
if (!NotifyPending) {
|
|
ApiStatus = ReplEnableChangeNotify( ChangeHandle );
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientMain got " FORMAT_API_STATUS
|
|
" from ReplEnableChangeNotify.\n", ApiStatus ));
|
|
NetpAssert( FALSE ); // enable can't fail!
|
|
goto Cleanup;
|
|
}
|
|
NotifyPending = TRUE;
|
|
}
|
|
|
|
// make an asynchronous read request.
|
|
|
|
bytes_read = bytes_read2 = 0;
|
|
|
|
(VOID) memset( &OverLapped, '\0', sizeof(OverLapped) );
|
|
OverLapped.Offset = 0;
|
|
|
|
if ( (!ReadPending) &&
|
|
!ReadFile( ReplGlobalClientMailslotHandle,
|
|
msg_buf,
|
|
sizeof( msg_buf ),
|
|
&bytes_read,
|
|
&OverLapped )) { // Overlapped I/O
|
|
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
|
|
if( ApiStatus != ERROR_IO_PENDING ) {
|
|
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientMain: ReadFile (mailslot) error "
|
|
FORMAT_API_STATUS ".\n", ApiStatus ));
|
|
|
|
AlertLogExit( ALERT_ReplNetErr,
|
|
NELOG_ReplNetErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
EXIT);
|
|
|
|
break;
|
|
|
|
}
|
|
else
|
|
{
|
|
ReadPending = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// wait on multiple events ..
|
|
EventTriggered = WaitForMultipleObjects(
|
|
3, // number of entries in array
|
|
WaitHandles, // array of handles
|
|
FALSE,
|
|
(DWORD) -1 );
|
|
|
|
if(EventTriggered == 0) { // ReplGlobalClientTerminateEvent
|
|
// terminate event
|
|
|
|
ApiStatus = NO_ERROR;
|
|
goto Cleanup; // Don't forget to close handles.
|
|
|
|
} else if(EventTriggered == 1) { // ReplGlobalClientMailslotHandle
|
|
// mailslot event
|
|
|
|
// get bytes_read info
|
|
|
|
ReadPending = FALSE; // GetOverlappedResult will complete IRP
|
|
if( !GetOverlappedResult( ReplGlobalClientMailslotHandle,
|
|
&OverLapped,
|
|
&bytes_read,
|
|
TRUE ) ) {
|
|
|
|
ApiStatus = GetLastError();
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Repl client got error "
|
|
FORMAT_API_STATUS " reading mailslot, "
|
|
"buf size=" FORMAT_DWORD ".\n",
|
|
ApiStatus, MAX_2_MSLOT_SIZE ));
|
|
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
|
|
// error read mailslot
|
|
AlertLogExit( ALERT_ReplNetErr,
|
|
NELOG_ReplNetErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
continue;
|
|
}
|
|
|
|
if(bytes_read == 0) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Received an empty mail.\n" ));
|
|
|
|
continue;
|
|
}
|
|
|
|
IF_DEBUG( CLIENT ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Received mailslot msg, size=" FORMAT_DWORD ":\n",
|
|
bytes_read ));
|
|
NetpDbgHexDump( msg_buf, NetpDbgReasonable( bytes_read ));
|
|
}
|
|
|
|
|
|
//
|
|
// Unmarshall message to make it internal format
|
|
//
|
|
|
|
if( (ApiStatus = ReplUnmarshallMessage(msg_buf,
|
|
bytes_read,
|
|
(LPBYTE) msg_buf2,
|
|
MAX_REPL_MAILSLOT_EXPANSION * MAX_2_MSLOT_SIZE,
|
|
&bytes_read2)) != NO_ERROR ) {
|
|
|
|
// the unmarshall routine will fail only iff the system doesn't
|
|
// have sufficient memory or the message is bogus ...
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Invalid mailslot message received, stat="
|
|
FORMAT_API_STATUS "\n", ApiStatus ));
|
|
|
|
AlertLogExit( ALERT_ReplNetErr,
|
|
NELOG_ReplNetErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
|
|
continue;
|
|
}
|
|
|
|
msg = (PSYNCMSG) (LPVOID) msg_buf2;
|
|
|
|
// successful mailslot read
|
|
|
|
//
|
|
// check if it is an expected sender.
|
|
//
|
|
|
|
if (!NameOfValidMaster(msg->header.sender,
|
|
msg->header.senders_domain)){
|
|
IF_DEBUG(CLIENT) { // debug code
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Ignoring message received, "
|
|
"unexpected sender or domain:\n"
|
|
" sender-> " FORMAT_LPTSTR "\n"
|
|
" domain-> " FORMAT_LPTSTR "\n",
|
|
msg->header.sender,
|
|
msg->header.senders_domain ));
|
|
//NetpDbgHexDump( msg_buf, NetpDbgReasonable( bytes_read ));
|
|
}
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
IF_DEBUG( MAJOR ) { // debug code
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Message received, type: "
|
|
FORMAT_DWORD ", from: " FORMAT_LPTSTR "\n",
|
|
msg->header.msg_type,
|
|
msg->header.sender ));
|
|
}
|
|
|
|
|
|
//
|
|
|
|
switch (msg->header.msg_type) {
|
|
|
|
case DIR_SUPPORTED: /*FALLTHROUGH*/
|
|
case DIR_NOT_SUPPORTED: /*FALLTHROUGH*/
|
|
case MASTER_DIR: /*FALLTHROUGH*/
|
|
case NOT_MASTER_DIR:
|
|
|
|
//
|
|
// Handle this query (handshake) message right now.
|
|
// This might involve updating structures, adding another
|
|
// timeout message, and/or sending a message to one or more
|
|
// masters.
|
|
//
|
|
// Note: This code used to pass the handshake messages off
|
|
// to the syncer thread. As we began to use the replicator
|
|
// for over a gigabyte, this introduced delays which made it
|
|
// seem like the master was timed-out when it was not. So,
|
|
// we process handshake messages right away instead.
|
|
//
|
|
|
|
ReplClientRespondToQueryMsg( (LPVOID) msg );
|
|
|
|
break;
|
|
|
|
//
|
|
// Handle the message type which simply gets posted to the
|
|
// syncer thread. No delay is needed for these messages.
|
|
//
|
|
// See the ReplSyncerThread procedure for comments on where these
|
|
// message types came from.
|
|
//
|
|
case PULSE_MSG:
|
|
|
|
//
|
|
// Copy msg into a buffer so it can be put in que and msg_buf
|
|
// freed for next message.
|
|
//
|
|
|
|
if ((que_buf = ReplClientGetPoolEntry( QUEBIG_POOL )) == NULL) {
|
|
|
|
//
|
|
// out of memory
|
|
//
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Can't get client pool entry, "
|
|
"pool out-of-memory, message type " FORMAT_HEX_DWORD
|
|
"\n", msg->header.msg_type ));
|
|
|
|
break;
|
|
}
|
|
|
|
NetpMoveMemory( que_buf->data, msg_buf2, bytes_read2 );
|
|
|
|
//
|
|
// Insert the message into the Work Queue.
|
|
//
|
|
|
|
ReplInsertWorkQueue( que_buf );
|
|
|
|
IF_DEBUG(CLIENT) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Message is queued in work queue\n" ));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
// Master's update message,
|
|
//
|
|
//
|
|
// To avoid having all clients send requests (consequence of an
|
|
// update) to Master at the same time, each client waits a random
|
|
// time (in the range [0..RANDOM]) before handling the update.
|
|
// Delay time is stored in delay field of the mesaage and put on
|
|
// the special delay list, managed by the WatchDog
|
|
// thread. When time's up WatchDog gets the message out of delay
|
|
// list and writes it into the Work Queue for Syncer thread to do
|
|
// the actual work (update).
|
|
//
|
|
|
|
case SYNC_MSG:
|
|
case GUARD_MSG:
|
|
|
|
//
|
|
// Copy into own buffer -> free mailsot buffer.
|
|
//
|
|
|
|
if ((que_buf = ReplClientGetPoolEntry( QUEBIG_POOL )) == NULL) {
|
|
|
|
//
|
|
// out of memory.
|
|
//
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Can't get client pool entry, "
|
|
"pool out-of-memory, message type "
|
|
FORMAT_HEX_DWORD "\n", msg->header.msg_type ));
|
|
|
|
break;
|
|
}
|
|
|
|
NetpMoveMemory( que_buf->data, msg_buf2, bytes_read2 );
|
|
|
|
//
|
|
// Get random delay (in units of 10 seconds).
|
|
//
|
|
|
|
if (!(que_buf->delay = Randomize(msg->info.random) / 10)) {
|
|
que_buf->delay = 1;
|
|
}
|
|
|
|
//
|
|
// Put on delay linked list.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalDelayListLock );
|
|
|
|
que_buf->next_p = RCGlobalDelayListHeader;
|
|
RCGlobalDelayListHeader = que_buf;
|
|
|
|
RELEASE_LOCK( RCGlobalDelayListLock );
|
|
|
|
IF_DEBUG(CLIENT) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Message is queued in delay queue\n" ));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// All other message types are ignored.
|
|
//
|
|
|
|
default:
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Bad Message, Type = " FORMAT_HEX_DWORD
|
|
"\n", msg->header.msg_type ));
|
|
|
|
AlertLogExit(0, NELOG_ReplBadMsg, 0, NULL, NULL, NO_EXIT);
|
|
|
|
}
|
|
|
|
} else if (EventTriggered == 2) { // ChangeHandle->WaitableHandle
|
|
|
|
NotifyPending = FALSE;
|
|
|
|
ACQUIRE_LOCK( RCGlobalClientListLock );
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Change of import tree detected...\n" ));
|
|
}
|
|
|
|
RCGlobalTimeOfLastChangeNotify = NetpReplTimeNow();
|
|
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
} else {
|
|
|
|
// error on WaitForMultipleObjects
|
|
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"WaitForMultipleObjects() in error, "
|
|
"EventTriggered = " FORMAT_DWORD ", ",
|
|
"ApiStatus = " FORMAT_API_STATUS ".\n",
|
|
EventTriggered, ApiStatus ));
|
|
|
|
AlertLogExit( ALERT_ReplNetErr,
|
|
NELOG_ReplNetErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
EXIT);
|
|
|
|
goto Cleanup; // Don't forget to close handles.
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (ChangeHandle != NULL) {
|
|
(VOID) ReplCloseChangeNotify( ChangeHandle );
|
|
}
|
|
|
|
if ( ReplGlobalClientMailslotHandle != (HANDLE)(-1) ) {
|
|
|
|
// Prevent stale use of handle (overlapped use of ex-stack space).
|
|
(VOID) CloseHandle( ReplGlobalClientMailslotHandle );
|
|
|
|
ReplGlobalClientMailslotHandle = (HANDLE)(-1);
|
|
}
|
|
|
|
ReplClientCleanup();
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplClientMain: exiting, status " FORMAT_API_STATUS
|
|
", thread ID " FORMAT_NET_THREAD_ID ".\n",
|
|
ApiStatus, NetpCurrentThread() ));
|
|
|
|
ReplFinish( ApiStatus );
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
return ((DWORD) ApiStatus);
|
|
|
|
} // ReplClientMain
|