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.
1714 lines
38 KiB
1714 lines
38 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
worker.c
|
|
|
|
Abstract:
|
|
|
|
Special Local groups replication to down level system
|
|
implementation.
|
|
|
|
This file contains the code required for the change log worker
|
|
thread. The worker thread maintains the list special local groups of
|
|
the BUILTIN database system and the list global groups that are
|
|
members of the special local group. Once a membership change is
|
|
found in one of the maintained groups, this thread simulate a user
|
|
delta for the new member and thus a change is reflected to the DL
|
|
system.
|
|
|
|
Author:
|
|
|
|
Madan Appiah
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
13-Dec-1992 (Madana)
|
|
Created this file
|
|
|
|
--*/
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include <nt.h> // LARGE_INTEGER definition
|
|
#include <ntrtl.h> // LARGE_INTEGER definition
|
|
#include <nturtl.h> // LARGE_INTEGER definition
|
|
#include <ntlsa.h> // needed by changelg.h
|
|
|
|
#define NOMINMAX // Avoid redefinition of min and max in stdlib.h
|
|
#include <rpc.h> // Needed by logon.h
|
|
#include <logon_s.h>// includes lmcons.h, lmaccess.h, netlogon.h,
|
|
// ssi.h, windef.h
|
|
#include <winbase.h>
|
|
#include <stdio.h> // sprintf ...
|
|
|
|
//
|
|
// BEWARE: Be careful about adding netlogon.dll specific include files here.
|
|
// This module is call by SAM and LSA. The netlogon service may not yet
|
|
// be running. Therefore, guard against referencing netlogon.dll globals
|
|
// other than those defined in changelg.h.
|
|
//
|
|
|
|
#include <samrpc.h> // Needed by samisrv.h
|
|
#include <samisrv.h> // Needed by changelg.h
|
|
#include <lsarpc.h> // Needed by lsrvdata.h and logonsrv.h
|
|
#include <lsaisrv.h> // LsaI routines
|
|
#include <changelg.h> // Local procedure definitions
|
|
|
|
#include <lmerr.h> // NERR_Success defined here ..
|
|
#include <lmerrlog.h> // NELOG_* defined here ..
|
|
#include <lmapibuf.h> // NetApiBufferFree defined here ..
|
|
#include <netlib.h> // NetpMemoryAllocate
|
|
#include <netlibnt.h> // NetpNtStatusToApiStatus
|
|
|
|
#define DEBUG_ALLOCATE
|
|
#include <nldebug.h> // Netlogon debugging
|
|
#undef DEBUG_ALLOCATE
|
|
#include <align.h>
|
|
#include <string.h> // strncmp
|
|
#include <nlp.h> // NlpWriteEventlog defined here.
|
|
|
|
#include <nlrepl.h> // I_Net* definitions
|
|
|
|
#define WORKER_ALLOCATE
|
|
#include <chworker.h>
|
|
#undef WORKER_ALLOCATE
|
|
|
|
//
|
|
// Special Local ID array
|
|
//
|
|
|
|
ULONG NlGlobalSpecialLocalGroupIDs[] = {
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
DOMAIN_ALIAS_RID_USERS,
|
|
DOMAIN_ALIAS_RID_GUESTS,
|
|
DOMAIN_ALIAS_RID_ACCOUNT_OPS,
|
|
DOMAIN_ALIAS_RID_SYSTEM_OPS,
|
|
DOMAIN_ALIAS_RID_PRINT_OPS } ;
|
|
|
|
#define NUM_SP_LOCAL_GROUPS \
|
|
(sizeof(NlGlobalSpecialLocalGroupIDs) / \
|
|
sizeof(NlGlobalSpecialLocalGroupIDs[0]))
|
|
|
|
|
|
|
|
BOOLEAN
|
|
IsSpecialLocalGroup(
|
|
ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure determines that the given RID is one among the
|
|
special local group RID.
|
|
|
|
Arguments:
|
|
|
|
Rid : Rid to test.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the given RID is special local group.
|
|
|
|
FALSE : otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < NUM_SP_LOCAL_GROUPS; i++ ) {
|
|
|
|
if( NlGlobalSpecialLocalGroupIDs[i] == Rid ) {
|
|
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
NlSimulateUserDelta(
|
|
ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure calls SAM service to generate change to a user record
|
|
and increment the serial number.
|
|
|
|
Arguments:
|
|
|
|
Rid : Rid of the new delta.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = SamINotifyDelta(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
SecurityDbChange,
|
|
SecurityDbObjectSamUser,
|
|
Rid,
|
|
NULL,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL, "SamINotifyDelta failed %lx\n", Status ) );
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NlAddWorkerQueueEntry(
|
|
enum WORKER_QUEUE_ENTRY_TYPE EntryType,
|
|
ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure adds a new entry to worker queue. It sets queue event
|
|
so that worker thread wakes up to process this new entry.
|
|
|
|
Enter with NlGlobalChangeLogCritSect locked.
|
|
|
|
Arguments:
|
|
|
|
EntryType : type of the new entry.
|
|
|
|
Rid : Rid of the member that caused this new entry.
|
|
|
|
Return Value:
|
|
|
|
STATUS_NO_MEMORY - if no memory available for the new entry.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWORKER_QUEUE_ENTRY Entry;
|
|
|
|
Entry = (PWORKER_QUEUE_ENTRY)NetpMemoryAllocate(
|
|
sizeof(WORKER_QUEUE_ENTRY) );
|
|
|
|
if( Entry == NULL ) {
|
|
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Entry->EntryType = EntryType;
|
|
Entry->Rid = Rid;
|
|
|
|
//
|
|
// add to list
|
|
//
|
|
|
|
InsertTailList( &NlGlobalChangeLogWorkerQueue, &Entry->Next );
|
|
|
|
//
|
|
// awake worker thread.
|
|
//
|
|
|
|
if ( !SetEvent( NlGlobalChangeLogWorkerQueueEvent ) ) {
|
|
DWORD WinError;
|
|
|
|
WinError = GetLastError();
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Cannot set ChangeLog worker queue event: %lu\n",
|
|
WinError ));
|
|
|
|
Status = NetpApiStatusToNtStatus( WinError );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL, "NlAddWorkerQueueEntry failed %lx\n",Status ) );
|
|
}
|
|
|
|
return( Status );
|
|
|
|
}
|
|
|
|
|
|
PGLOBAL_GROUP_ENTRY
|
|
NlGetGroupEntry(
|
|
PLIST_ENTRY GroupList,
|
|
ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure returns a group entry from the list. It returns NULL
|
|
if the requested entry is not in the list.
|
|
|
|
Enter with NlGlobalChangeLogCritSect locked if GroupList points to
|
|
NlGlobalSpecialServerGroupList.
|
|
|
|
Arguments:
|
|
|
|
GroupList : list to search.
|
|
|
|
Rid : Rid of the entry wanted.
|
|
|
|
Return Value:
|
|
|
|
NULL : if there is no entry.
|
|
otherwise : pointer to the entry.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PGLOBAL_GROUP_ENTRY GroupEntry;
|
|
|
|
for ( ListEntry = GroupList->Flink;
|
|
ListEntry != GroupList;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
GroupEntry =
|
|
CONTAINING_RECORD( ListEntry, GLOBAL_GROUP_ENTRY, Next );
|
|
|
|
if( GroupEntry->Rid == Rid ) {
|
|
|
|
return( GroupEntry );
|
|
}
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NlAddGroupEntry(
|
|
PLIST_ENTRY GroupList,
|
|
ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure adds a group entry to a global group list.
|
|
|
|
Enter with NlGlobalChangeLogCritSect locked if GroupList points to
|
|
NlGlobalSpecialServerGroupList.
|
|
|
|
Arguments:
|
|
|
|
GroupList : List to modify.
|
|
|
|
Rid : Rid of the new group entry.
|
|
|
|
Name : Name of the new group entry.
|
|
|
|
Return Value:
|
|
|
|
STATUS_NO_MEMORY - if no memory available for the new entry.
|
|
|
|
--*/
|
|
{
|
|
PGLOBAL_GROUP_ENTRY Entry;
|
|
|
|
//
|
|
// If the entry already exists,
|
|
// don't add it again.
|
|
//
|
|
Entry = NlGetGroupEntry( GroupList, Rid );
|
|
|
|
if ( Entry != NULL) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// get memory for this entry
|
|
//
|
|
|
|
Entry = (PGLOBAL_GROUP_ENTRY)NetpMemoryAllocate(
|
|
sizeof(GLOBAL_GROUP_ENTRY) );
|
|
|
|
if( Entry == NULL ) {
|
|
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
Entry->Rid = Rid;
|
|
|
|
//
|
|
// add to list
|
|
//
|
|
|
|
InsertTailList( GroupList, &Entry->Next );
|
|
|
|
return( STATUS_SUCCESS );
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NlAddGlobalGroupsToList(
|
|
PLIST_ENTRY GroupList,
|
|
ULONG LocalGroupID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure adds the global groups that are member of the given
|
|
alias.
|
|
|
|
Arguments:
|
|
|
|
GroupList : List to munch.
|
|
|
|
LocalGroupId : Rid of the local group.
|
|
|
|
Return Value:
|
|
|
|
Return NT Status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
SAMPR_HANDLE AliasHandle = NULL;
|
|
|
|
SAMPR_PSID_ARRAY Members = {0, NULL};
|
|
PULONG RidArray = NULL;
|
|
DWORD RidArrayLength = 0;
|
|
SAMPR_RETURNED_USTRING_ARRAY Names = {0, NULL};
|
|
SAMPR_ULONG_ARRAY Use = {0, NULL};
|
|
DWORD i;
|
|
|
|
//
|
|
// Open Local Group
|
|
//
|
|
|
|
Status = SamrOpenAlias(
|
|
NlGlobalChWorkerBuiltinDBHandle,
|
|
0, // No desired access
|
|
LocalGroupID,
|
|
&AliasHandle );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
AliasHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Enumerate members in this local group.
|
|
//
|
|
|
|
Status = SamrGetMembersInAlias(
|
|
AliasHandle,
|
|
&Members );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
Members.Sids = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Determine the SIDs that belong to the Account Domain and get the
|
|
// RIDs of them.
|
|
//
|
|
|
|
if( Members.Count == 0) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate the maximum size RID array required.
|
|
//
|
|
|
|
RidArray = (PULONG)NetpMemoryAllocate( Members.Count * sizeof(ULONG) );
|
|
|
|
if( RidArray == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for( i = 0; i < Members.Count; i++) {
|
|
|
|
PUCHAR SubAuthorityCount;
|
|
BOOLEAN EqualSid;
|
|
|
|
SubAuthorityCount =
|
|
RtlSubAuthorityCountSid(Members.Sids[i].SidPointer);
|
|
|
|
(*SubAuthorityCount)--;
|
|
EqualSid = RtlEqualSid( NlGlobalChWorkerSamDomainSid,
|
|
Members.Sids[i].SidPointer );
|
|
(*SubAuthorityCount)++;
|
|
|
|
if( EqualSid ) {
|
|
|
|
RidArray[RidArrayLength] =
|
|
*RtlSubAuthoritySid( Members.Sids[i].SidPointer,
|
|
(*SubAuthorityCount) -1 );
|
|
RidArrayLength++;
|
|
}
|
|
}
|
|
|
|
if( RidArrayLength == 0) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get Group RIDs and add them to list.
|
|
//
|
|
|
|
Status = SamrLookupIdsInDomain( NlGlobalChWorkerSamDBHandle,
|
|
RidArrayLength,
|
|
RidArray,
|
|
&Names,
|
|
&Use );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
Names.Element = NULL;
|
|
Use.Element = NULL;
|
|
|
|
if( Status == STATUS_NONE_MAPPED ) {
|
|
|
|
//
|
|
// if no SID is mapped, we can't do much here.
|
|
//
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"NlAddGlobalGroupsToList could not map any SID from "
|
|
"local group, RID = %lx \n", LocalGroupID ));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
NlAssert( Names.Count == RidArrayLength );
|
|
NlAssert( Names.Element != NULL );
|
|
NlAssert( Use.Count == RidArrayLength );
|
|
NlAssert( Use.Element != NULL );
|
|
|
|
//
|
|
// Find groups and add them to list.
|
|
//
|
|
|
|
for( i = 0; i < RidArrayLength; i++ ) {
|
|
|
|
if( Use.Element[i] == SidTypeGroup ) {
|
|
|
|
//
|
|
// we found a group, add it to the list if it is not there
|
|
// already.
|
|
//
|
|
|
|
if( NlGetGroupEntry( GroupList, RidArray[i] ) != NULL ) {
|
|
|
|
//
|
|
// entry already in the list.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// add an entry to the list.
|
|
//
|
|
|
|
Status = NlAddGroupEntry( GroupList, RidArray[i] );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if( Names.Element != NULL ) {
|
|
SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &Names );
|
|
}
|
|
|
|
if( Use.Element != NULL ) {
|
|
SamIFree_SAMPR_ULONG_ARRAY( &Use );
|
|
}
|
|
|
|
if( RidArray != NULL ) {
|
|
NetpMemoryFree( RidArray );
|
|
}
|
|
|
|
if ( Members.Sids != NULL ) {
|
|
SamIFree_SAMPR_PSID_ARRAY( (PSAMPR_PSID_ARRAY)&Members );
|
|
}
|
|
|
|
if( AliasHandle != NULL ) {
|
|
SamrCloseHandle( &AliasHandle );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL, "NlAddGlobalGroupsToList failed %lx\n",
|
|
Status ));
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NlInitSpecialGroupList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine browses through the following special local groups and
|
|
forms a list global groups that are members of the local groups.
|
|
|
|
Special Local groups :
|
|
|
|
1. DOMAIN_ALIAS_RID_ADMINS
|
|
2. DOMAIN_ALIAS_RID_USERS
|
|
3. DOMAIN_ALIAS_RID_GUESTS
|
|
4. DOMAIN_ALIAS_RID_ACCOUNT_OPS
|
|
5. DOMAIN_ALIAS_RID_SYSTEM_OPS
|
|
6. DOMAIN_ALIAS_RID_PRINT_OPS
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Return NT Status code.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
LIST_ENTRY SpecialServerGroupList;
|
|
DWORD i;
|
|
|
|
InitializeListHead(&SpecialServerGroupList);
|
|
|
|
for (i = 0; i < NUM_SP_LOCAL_GROUPS; i++ ) {
|
|
|
|
Status = NlAddGlobalGroupsToList(
|
|
&SpecialServerGroupList,
|
|
NlGlobalSpecialLocalGroupIDs[i] );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// install new list in global data.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
NlAssert( IsListEmpty(&NlGlobalSpecialServerGroupList) );
|
|
|
|
//
|
|
// install list in global data
|
|
//
|
|
|
|
NlGlobalSpecialServerGroupList = SpecialServerGroupList;
|
|
(SpecialServerGroupList.Flink)->Blink = &NlGlobalSpecialServerGroupList;
|
|
(SpecialServerGroupList.Blink)->Flink = &NlGlobalSpecialServerGroupList;
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
BOOL
|
|
NlIsServersGroupEmpty(
|
|
ULONG ServersGroupRid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure determines whether the Servers group is empty or not.
|
|
|
|
Arguments:
|
|
|
|
Rid : Rid of the SERVERS group. If it is zero then determine the RID
|
|
by lookup.
|
|
|
|
Return Value:
|
|
|
|
FALSE : If the servers group exist and it is non-empty.
|
|
TRUE : otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
SAMPR_ULONG_ARRAY RelativeIdArray = {0, NULL};
|
|
SAMPR_ULONG_ARRAY UseArray = {0, NULL};
|
|
RPC_UNICODE_STRING GroupNameString;
|
|
SAMPR_HANDLE GroupHandle = NULL;
|
|
BOOL ReturnValue = TRUE;
|
|
|
|
PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
|
|
|
|
if ( ServersGroupRid == 0 ) {
|
|
|
|
//
|
|
// Convert the group name to a RelativeId.
|
|
//
|
|
|
|
RtlInitUnicodeString( (PUNICODE_STRING)&GroupNameString,
|
|
SSI_SERVER_GROUP_W );
|
|
|
|
Status = SamrLookupNamesInDomain(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
1,
|
|
&GroupNameString,
|
|
&RelativeIdArray,
|
|
&UseArray );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
RelativeIdArray.Element = NULL;
|
|
UseArray.Element = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// we should get back exactly one entry of info back.
|
|
//
|
|
|
|
NlAssert( UseArray.Count == 1 );
|
|
NlAssert( UseArray.Element != NULL );
|
|
NlAssert( RelativeIdArray.Count == 1 );
|
|
NlAssert( RelativeIdArray.Element != NULL );
|
|
|
|
if ( UseArray.Element[0] != SidTypeGroup ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
ServersGroupRid = RelativeIdArray.Element[0];
|
|
}
|
|
|
|
//
|
|
// Open the SERVERS group
|
|
//
|
|
|
|
Status = SamrOpenGroup( NlGlobalChWorkerSamDBHandle,
|
|
0, // No desired access
|
|
ServersGroupRid,
|
|
&GroupHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
GroupHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// enumerate members in the group.
|
|
//
|
|
|
|
Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
MembersBuffer = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( MembersBuffer->MemberCount != 0 ) {
|
|
|
|
//
|
|
// atleast a member in there.
|
|
//
|
|
|
|
ReturnValue = FALSE;
|
|
|
|
//
|
|
// Save the list of LmBdcs
|
|
|
|
NlLmBdcListSet( MembersBuffer->MemberCount,
|
|
MembersBuffer->Members );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
|
|
SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
|
|
|
|
if ( MembersBuffer != NULL ) {
|
|
SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
|
|
}
|
|
|
|
if( GroupHandle != NULL ) {
|
|
(VOID) SamrCloseHandle( &GroupHandle );
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NlProcessQueueEntry(
|
|
PWORKER_QUEUE_ENTRY Entry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure processes an entry that has come from the worker
|
|
queue.
|
|
|
|
Arguments:
|
|
|
|
WorkerQueueEntry : pointer to worker structure.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if we need to continue processing more entries.
|
|
FALSE : if we need to terminate the worker.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Rid = Entry->Rid;
|
|
BOOLEAN ReturnValue = TRUE;
|
|
SAMPR_RETURNED_USTRING_ARRAY Names = {0, NULL};
|
|
SAMPR_ULONG_ARRAY Use = {0, NULL};
|
|
SAMPR_HANDLE GroupHandle = NULL;
|
|
PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
|
|
SAMPR_HANDLE UserHandle = NULL;
|
|
|
|
PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
|
|
PGLOBAL_GROUP_ENTRY GroupEntry;
|
|
|
|
DWORD i;
|
|
|
|
|
|
//
|
|
// The membership of a special local group is being changed,
|
|
// force each lanman BDC to re-sync with each user that's being
|
|
// added-to/removed-from the local group.
|
|
//
|
|
switch ( Entry->EntryType ) {
|
|
case ChangeLogAliasMembership :
|
|
|
|
//
|
|
// determine Rid Type.
|
|
//
|
|
|
|
Status = SamrLookupIdsInDomain(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
1,
|
|
&Rid,
|
|
&Names,
|
|
&Use );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
Names.Element = NULL;
|
|
Use.Element = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NlAssert( Names.Count == 1 );
|
|
NlAssert( Names.Element != NULL );
|
|
NlAssert( Use.Count == 1 );
|
|
NlAssert( Use.Element != NULL );
|
|
|
|
if( Use.Element[0] == SidTypeUser ) {
|
|
|
|
NlSimulateUserDelta( Rid );
|
|
|
|
//
|
|
// if this users is added unknowingly to the global group
|
|
// list, remove it now.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
GroupEntry = NlGetGroupEntry (
|
|
&NlGlobalSpecialServerGroupList,
|
|
Rid );
|
|
|
|
if( GroupEntry != NULL ) {
|
|
|
|
RemoveEntryList( &GroupEntry->Next );
|
|
NetpMemoryFree( GroupEntry );
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
|
|
} else if( Use.Element[0] == SidTypeGroup ) {
|
|
|
|
DWORD i;
|
|
|
|
//
|
|
// simulate deltas for all members in this group.
|
|
//
|
|
|
|
Status = SamrOpenGroup( NlGlobalChWorkerSamDBHandle,
|
|
0, // No desired access
|
|
Rid,
|
|
&GroupHandle );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
GroupHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
MembersBuffer = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for( i = 0; i < MembersBuffer->MemberCount; i++) {
|
|
|
|
NlSimulateUserDelta( MembersBuffer->Members[i] );
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Ensure the change log thread already added this group
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
GroupEntry = NlGetGroupEntry (
|
|
&NlGlobalSpecialServerGroupList,
|
|
Rid );
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
NlAssert( GroupEntry != NULL );
|
|
#endif // DBG
|
|
|
|
} else {
|
|
|
|
//
|
|
// ignore any other changes
|
|
//
|
|
|
|
NlAssert( FALSE );
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// The group membership of the special group has changed.
|
|
// Force Lanman BDCs to re-sync the user being added-to/removed-from
|
|
// the domain.
|
|
//
|
|
case ChangeLogGroupMembership :
|
|
|
|
//
|
|
// determine Rid Type.
|
|
//
|
|
|
|
Status = SamrLookupIdsInDomain(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
1,
|
|
&Rid,
|
|
&Names,
|
|
&Use );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
Names.Element = NULL;
|
|
Use.Element = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NlAssert( Names.Count == 1 );
|
|
NlAssert( Names.Element != NULL );
|
|
NlAssert( Use.Count == 1 );
|
|
NlAssert( Use.Element != NULL );
|
|
|
|
NlAssert( Use.Element[0] == SidTypeUser );
|
|
|
|
|
|
if( Use.Element[0] == SidTypeUser ) {
|
|
|
|
NlSimulateUserDelta( Rid );
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// A member was deleted from the SERVERS group.
|
|
// Check to see if this thread can terminate.
|
|
//
|
|
case ServersGroupDel :
|
|
|
|
//
|
|
// if the server group is empty then terminate worker thread.
|
|
//
|
|
|
|
if ( NlGlobalLmBdcCount == 0 ) {
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// Rename user is handled as multiple deltas:
|
|
// 1) Delete old user and
|
|
// 2) Add new user.
|
|
// 3) Update membership of each group the user is a member of
|
|
//
|
|
case ChangeLogRenameUser :
|
|
|
|
//
|
|
// simulate a user change so that an user account with
|
|
// new name will be created on the down level system.
|
|
//
|
|
|
|
NlSimulateUserDelta( Rid );
|
|
|
|
//
|
|
// create deltas to make his group membership correct on the
|
|
// down level machine.
|
|
//
|
|
|
|
Status = SamrOpenUser( NlGlobalChWorkerSamDBHandle,
|
|
0, // No desired access
|
|
Rid,
|
|
&UserHandle );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
UserHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = SamrGetGroupsForUser( UserHandle, &GroupsBuffer );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
GroupsBuffer = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for( i = 0; i < GroupsBuffer->MembershipCount; i++) {
|
|
|
|
Status = SamINotifyDelta(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
SecurityDbChangeMemberAdd,
|
|
SecurityDbObjectSamGroup,
|
|
GroupsBuffer->Groups[i].RelativeId,
|
|
NULL,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// A newly added user needs to have it's membership in "Domain Users" updated, too.
|
|
//
|
|
// Here we simply supply a corresponding change user membership delta which
|
|
// ends up as a AddOrChangeUser delta with the CHANGELOG_DOMAINUSERS_CHANGED
|
|
// flag set. NetrAccountDeltas interprets that flag to mean
|
|
// "send the membership of this user".
|
|
//
|
|
case ChangeLogAddUser:
|
|
|
|
Status = SamINotifyDelta(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
SecurityDbChangeMemberAdd,
|
|
SecurityDbObjectSamUser,
|
|
Rid,
|
|
NULL,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
break;
|
|
|
|
|
|
//
|
|
// Rename group is handled as three deltas:
|
|
// 1) Delete old group,
|
|
// 2) Add new group and
|
|
// 3) Changemembership of new group.
|
|
//
|
|
case ChangeLogRenameGroup :
|
|
|
|
//
|
|
// simulate a group change so that a group account with
|
|
// new name will be created on the down level system. Also
|
|
// simulate a changemembership delta so that the members are
|
|
// added to the new group appropriately.
|
|
//
|
|
|
|
Status = SamINotifyDelta(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
SecurityDbChange,
|
|
SecurityDbObjectSamGroup,
|
|
Rid,
|
|
NULL,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
Status = SamINotifyDelta(
|
|
NlGlobalChWorkerSamDBHandle,
|
|
SecurityDbChangeMemberAdd,
|
|
SecurityDbObjectSamGroup,
|
|
Rid,
|
|
NULL,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL, "SamINotifyDelta failed %lx\n", Status ) );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"NlProcessQueueEntry found unknown queue entry : %lx\n",
|
|
Entry->EntryType ));
|
|
break;
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if( Names.Element != NULL ) {
|
|
SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &Names );
|
|
}
|
|
|
|
if( Use.Element != NULL ) {
|
|
SamIFree_SAMPR_ULONG_ARRAY( &Use );
|
|
}
|
|
|
|
if ( MembersBuffer != NULL ) {
|
|
SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
|
|
}
|
|
|
|
if( GroupHandle != NULL ) {
|
|
(VOID) SamrCloseHandle( &GroupHandle );
|
|
}
|
|
|
|
if ( GroupsBuffer != NULL ) {
|
|
SamIFree_SAMPR_GET_GROUPS_BUFFER( GroupsBuffer );
|
|
}
|
|
|
|
if( UserHandle != NULL ) {
|
|
(VOID) SamrCloseHandle( &UserHandle );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"NlProcessQueueEntry failed : %lx\n",
|
|
Status ));
|
|
|
|
}
|
|
|
|
return ReturnValue;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
NlChangeLogWorker(
|
|
IN LPVOID ChangeLogWorkerParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This thread performs the special operations that are required to
|
|
replicate the special local groups such as Administrator, Server
|
|
Operartors, etc., in the NT BUILTIN database to the
|
|
down level systems.
|
|
|
|
This thread comes up first time during system bootup and initializes
|
|
required global data. If this NT (PDC) System is replicating to any
|
|
down level system then it stays back, otherwise it terminates. Also
|
|
when a down level system is added to the domain, this thread is
|
|
created if it is not running on the system before.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Return when there is no down level system on the domain.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
#if DBG
|
|
DWORD Count;
|
|
#endif
|
|
|
|
|
|
NlPrint((NL_CHANGELOG, "ChangeLogWorker Thread is starting \n"));
|
|
|
|
//
|
|
// check if have initialize the global data before
|
|
//
|
|
|
|
if ( !NlGlobalChangeLogWorkInit ) {
|
|
|
|
PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
|
|
DWORD DomainSidLength;
|
|
|
|
//
|
|
// wait for SAM service to start.
|
|
//
|
|
|
|
if( !NlWaitForSamService(FALSE) ) {
|
|
|
|
NlPrint((NL_CRITICAL, "Sam server failed start."));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open Sam Server
|
|
//
|
|
|
|
Status = SamIConnect( NULL, // No server name
|
|
&NlGlobalChWorkerSamServerHandle,
|
|
0, // Ignore desired access
|
|
(BOOLEAN) TRUE );
|
|
// Indicate we are privileged
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to connect to SAM server %lx\n", Status ));
|
|
|
|
NlGlobalChWorkerSamServerHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open Policy Domain
|
|
//
|
|
|
|
Status = LsaIOpenPolicyTrusted( &NlGlobalChWorkerPolicyHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to Open LSA database %lx\n", Status ));
|
|
|
|
NlGlobalChWorkerPolicyHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open BuiltIn Domain database
|
|
//
|
|
// Note, build in domain SID is made during dll init time.
|
|
//
|
|
|
|
|
|
Status = SamrOpenDomain( NlGlobalChWorkerSamServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
NlGlobalChWorkerBuiltinDomainSid,
|
|
&NlGlobalChWorkerBuiltinDBHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to Open BUILTIN database %lx\n", Status ));
|
|
NlGlobalChWorkerBuiltinDBHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Query account domain SID.
|
|
//
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
NlGlobalChWorkerPolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
&PolicyAccountDomainInfo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to Query Account domain Sid from LSA %lx\n",
|
|
Status ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
|
|
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyAccountDomainInformation,
|
|
PolicyAccountDomainInfo );
|
|
|
|
NlPrint((NL_CRITICAL, "Account domain info from LSA invalid.\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// copy domain SID to global data.
|
|
//
|
|
|
|
DomainSidLength = RtlLengthSid( PolicyAccountDomainInfo->
|
|
PolicyAccountDomainInfo.DomainSid );
|
|
|
|
NlGlobalChWorkerSamDomainSid = (PSID)NetpMemoryAllocate( DomainSidLength );
|
|
|
|
if( NlGlobalChWorkerSamDomainSid == NULL ) {
|
|
|
|
Status = STATUS_NO_MEMORY;
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"NlChangeLogWorker is out of memory.\n"));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlCopySid(
|
|
DomainSidLength,
|
|
NlGlobalChWorkerSamDomainSid,
|
|
PolicyAccountDomainInfo->
|
|
PolicyAccountDomainInfo.DomainSid );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to copy SAM Domain sid %lx\n", Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Free up Account domain info, we don't need any more.
|
|
//
|
|
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyAccountDomainInformation,
|
|
PolicyAccountDomainInfo );
|
|
|
|
//
|
|
// Open Account domain
|
|
//
|
|
|
|
Status = SamrOpenDomain( NlGlobalChWorkerSamServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
NlGlobalChWorkerSamDomainSid,
|
|
&NlGlobalChWorkerSamDBHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to Open SAM database %lx\n", Status ));
|
|
NlGlobalChWorkerSamDBHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialization done. Never do it again.
|
|
//
|
|
|
|
NlGlobalChangeLogWorkInit = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// If SERVERS global group is empty then it implies that we don't
|
|
// have any down level system on this domain. so we can stop this
|
|
// thread.
|
|
//
|
|
|
|
if ( NlIsServersGroupEmpty( 0 ) ) {
|
|
|
|
NlPrint((NL_CHANGELOG, "Servers Group is empty \n "));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize NlGlobalSpecialServerGroupList.
|
|
//
|
|
|
|
Status = NlInitSpecialGroupList();
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Failed to initialize Special group list %lx\n", Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// process worker queue forever, terminate when we are asked to do
|
|
// so or when the SERVERS group goes empty.
|
|
//
|
|
|
|
for( ;; ) {
|
|
|
|
DWORD WaitStatus;
|
|
|
|
//
|
|
// wait on the queue to become non-empty
|
|
//
|
|
|
|
WaitStatus = WaitForSingleObject(
|
|
NlGlobalChangeLogWorkerQueueEvent,
|
|
(DWORD)(-1) );
|
|
|
|
if ( WaitStatus != 0 ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"Change log worker failed, "
|
|
"WaitForSingleObject error: %ld\n",
|
|
WaitStatus));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// empty worker queue.
|
|
//
|
|
|
|
#if DBG
|
|
Count = 0;
|
|
#endif
|
|
for (;;) {
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
PWORKER_QUEUE_ENTRY WorkerQueueEntry;
|
|
|
|
//
|
|
// if we are asked to leave, do so.
|
|
//
|
|
|
|
if( NlGlobalChangeLogWorkerTerminate ) {
|
|
|
|
NlPrint((NL_CHANGELOG,
|
|
"ChangeLogWorker is asked to leave \n"));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
if( IsListEmpty( &NlGlobalChangeLogWorkerQueue ) ) {
|
|
|
|
UNLOCK_CHANGELOG();
|
|
break;
|
|
}
|
|
|
|
ListEntry = RemoveHeadList( &NlGlobalChangeLogWorkerQueue );
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
WorkerQueueEntry = CONTAINING_RECORD( ListEntry,
|
|
WORKER_QUEUE_ENTRY,
|
|
Next );
|
|
|
|
//
|
|
// process an queue entry.
|
|
//
|
|
|
|
if( !NlProcessQueueEntry( WorkerQueueEntry ) ) {
|
|
|
|
NlPrint((NL_CHANGELOG, "Servers group becomes empty \n"));
|
|
|
|
NetpMemoryFree( WorkerQueueEntry );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Free this entry.
|
|
//
|
|
|
|
NetpMemoryFree( WorkerQueueEntry );
|
|
#if DBG
|
|
Count++;
|
|
#endif
|
|
}
|
|
|
|
NlPrint((NL_CHANGELOG,
|
|
"Changelog worker processed %lu entries.\n", Count) );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// empty worker queue and group list
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
#if DBG
|
|
Count = 0;
|
|
#endif
|
|
while ( !IsListEmpty( &NlGlobalChangeLogWorkerQueue ) ) {
|
|
PLIST_ENTRY ListEntry;
|
|
PWORKER_QUEUE_ENTRY WorkerQueueEntry;
|
|
|
|
ListEntry = RemoveHeadList( &NlGlobalChangeLogWorkerQueue );
|
|
|
|
WorkerQueueEntry = CONTAINING_RECORD( ListEntry,
|
|
WORKER_QUEUE_ENTRY,
|
|
Next );
|
|
NetpMemoryFree( WorkerQueueEntry );
|
|
#if DBG
|
|
Count++;
|
|
#endif
|
|
}
|
|
|
|
#if DBG
|
|
if ( Count != 0 ) {
|
|
NlPrint((NL_CHANGELOG,
|
|
"Changelog worker did not process %lu entries.\n", Count) );
|
|
}
|
|
#endif
|
|
|
|
while ( !IsListEmpty( &NlGlobalSpecialServerGroupList ) ) {
|
|
PLIST_ENTRY ListEntry;
|
|
PGLOBAL_GROUP_ENTRY ServerEntry;
|
|
|
|
ListEntry = RemoveHeadList( &NlGlobalSpecialServerGroupList );
|
|
|
|
ServerEntry = CONTAINING_RECORD( ListEntry,
|
|
GLOBAL_GROUP_ENTRY,
|
|
Next );
|
|
NetpMemoryFree( ServerEntry );
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
NlPrint((NL_CHANGELOG, "ChangeLogWorker Thread is exiting \n"));
|
|
|
|
return;
|
|
UNREFERENCED_PARAMETER( ChangeLogWorkerParam );
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
NlStartChangeLogWorkerThread(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start the Change Log Worker thread if it is not already running.
|
|
|
|
Enter with NlGlobalChangeLogCritSect locked.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD ThreadHandle;
|
|
|
|
//
|
|
// If the worker thread is already running, do nothing.
|
|
//
|
|
|
|
if ( IsChangeLogWorkerRunning() ) {
|
|
return FALSE;
|
|
}
|
|
|
|
NlGlobalChangeLogWorkerTerminate = FALSE;
|
|
|
|
NlGlobalChangeLogWorkerThreadHandle = CreateThread(
|
|
NULL, // No security attributes
|
|
THREAD_STACKSIZE,
|
|
(LPTHREAD_START_ROUTINE)
|
|
NlChangeLogWorker,
|
|
NULL,
|
|
0, // No special creation flags
|
|
&ThreadHandle );
|
|
|
|
if ( NlGlobalChangeLogWorkerThreadHandle == NULL ) {
|
|
|
|
//
|
|
// ?? Shouldn't we do something in non-debug case
|
|
//
|
|
|
|
NlPrint((NL_CRITICAL, "Can't create change log worker thread %lu\n",
|
|
GetLastError() ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
NlStopChangeLogWorker(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stops the worker thread if it is running and waits for it to stop.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Ask the worker to stop running.
|
|
//
|
|
|
|
NlGlobalChangeLogWorkerTerminate = TRUE;
|
|
|
|
//
|
|
// Determine if the worker thread is already running.
|
|
//
|
|
|
|
if ( NlGlobalChangeLogWorkerThreadHandle != NULL ) {
|
|
|
|
//
|
|
// awake worker thread.
|
|
//
|
|
|
|
if ( !SetEvent( NlGlobalChangeLogWorkerQueueEvent ) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"Cannot set ChangeLog worker queue event: %lu\n",
|
|
GetLastError() ));
|
|
}
|
|
|
|
//
|
|
// We've asked the worker to stop. It should do so soon.
|
|
// Wait for it to stop.
|
|
//
|
|
|
|
NlWaitForSingleObject( "Wait for worker to stop",
|
|
NlGlobalChangeLogWorkerThreadHandle );
|
|
|
|
|
|
CloseHandle( NlGlobalChangeLogWorkerThreadHandle );
|
|
NlGlobalChangeLogWorkerThreadHandle = NULL;
|
|
|
|
}
|
|
|
|
NlGlobalChangeLogWorkerTerminate = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsChangeLogWorkerRunning(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Test if the change log worker thread is running
|
|
|
|
Enter with NlGlobalChangeLogCritSect locked.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the worker thread is running.
|
|
|
|
FALSE - if the worker thread is not running.
|
|
|
|
--*/
|
|
{
|
|
DWORD WaitStatus;
|
|
|
|
//
|
|
// Determine if the worker thread is already running.
|
|
//
|
|
|
|
if ( NlGlobalChangeLogWorkerThreadHandle != NULL ) {
|
|
|
|
//
|
|
// Time out immediately if the worker is still running.
|
|
//
|
|
|
|
WaitStatus = WaitForSingleObject(
|
|
NlGlobalChangeLogWorkerThreadHandle, 0 );
|
|
|
|
if ( WaitStatus == WAIT_TIMEOUT ) {
|
|
return TRUE;
|
|
|
|
} else if ( WaitStatus == 0 ) {
|
|
CloseHandle( NlGlobalChangeLogWorkerThreadHandle );
|
|
NlGlobalChangeLogWorkerThreadHandle = NULL;
|
|
return FALSE;
|
|
|
|
} else {
|
|
NlPrint((NL_CRITICAL,
|
|
"Cannot WaitFor Change Log Worker thread: %ld\n",
|
|
WaitStatus ));
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|