/*++ 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 definitions (needed by chngnot.h) #include // NT runtime library definitions (needed by nturtl.h) #include // Needed to have windows.h and nt.h co-exist. #define NOMINMAX // Let stdlib.h define min and max #include // DWORD, ReadFile(), etc. #include // NET_API_STATUS, IN, OUT, etc. #include // ALERT_* defines #include // ReplSetupChangeNotify, REPL_CHANGE_NOTIFY_HANDLE, etc #include // REPL_KEYWORD_ equates. #include // I_NetPathCompare #include // LONG_MAX. #include // NO_ERROR, ERROR_, NERR_ equates. #include // NELOG_* defines #include // SERVICE_WORKSTATION. #include // DBGSTATIC .. #include // NetpMemoryAllocate(), NetpIsServerStarted(). #include // Lock data types, functions, and macros. #include // PREFIX_ equates. #include // NetpReplTimeNow(). #include // rand() #include // memset(). #include // FORMAT_NET_THREAD_ID, NetpCurrentThread(). #include // STRLEN(), etc. #include // SERVICE_ equates, etc. // // Local include files // #include // IF_DEBUG(), etc. #include // ReplGlobal and ReplConfig variables. #include // LOCK_LEVEL equates. #define CLIENT_ALLOCATE // Allocate storage for all client global variables #include // // 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