windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/lls/server/perseat.c
2020-09-26 16:20:57 +08:00

3388 lines
87 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
perseat.c
Abstract:
Routines to handle per-seat licensing. Handles the in-memory cache
of useage via the Rtl Generic table functions (these are a generic
splay tree package).
There can be up to three tables kept. The first table is a username
table and is the main table. The second table is for SID's, which will
be converted into usernames when replicated.
The SID and username trees are handled in this module as they are used
by all modes of the server.
Author:
Arthur Hanson (arth) 03-Jan-1995
Revision History:
Jeff Parham (jeffparh) 12-Jan-1996
o Fixed possible infinite loop in UserListLicenseDelete().
o In FamilyLicenseUpdate(), now rescans for BackOffice upgrades
regardless of whether the family being updated was BackOffice.
This fixes a problem wherein a freed BackOffice license was
not being assigned to a user that needed it. (Bug #3299.)
o Added support for maintaining the SUITE_USE flag when adding
users to the AddCache.
--*/
#include <stdlib.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <lm.h>
#include <dsgetdc.h>
#include "debug.h"
#include "llsutil.h"
#include "llssrv.h"
#include "mapping.h"
#include "msvctbl.h"
#include "svctbl.h"
#include "perseat.h"
#include "llsevent.h"
#include "llsrtl.h"
#define NO_LLS_APIS
#include "llsapi.h"
//
// At what # of product do we switch to BackOffice
//
#define BACKOFFICE_SWITCH 3
NTSTATUS GetDCInfo(
DWORD ccDomain,
WCHAR wszDomain[],
DOMAIN_CONTROLLER_INFO ** ppDCInfo);
/////////////////////////////////////////////////////////////////////////
//
// Actual User and SID Lists, and their access locks
//
ULONG UserListNumEntries = 0;
static ULONG SidListNumEntries = 0;
LLS_GENERIC_TABLE UserList;
static LLS_GENERIC_TABLE SidList;
RTL_RESOURCE UserListLock;
static RTL_RESOURCE SidListLock;
/////////////////////////////////////////////////////////////////////////
//
// The AddCache itself, a critical section to protect access to it and an
// event to signal the server when there are items on it that need to be
// processed.
//
PADD_CACHE AddCache = NULL;
ULONG AddCacheSize = 0;
RTL_CRITICAL_SECTION AddCacheLock;
HANDLE LLSAddCacheEvent;
DWORD LastUsedTime = 0;
BOOL UsersDeleted = FALSE;
static RTL_CRITICAL_SECTION GenTableLock;
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// The License List is a linear list of all the licenses the object is
// using.
//
// The license list is kept as part of each user and mapping record, if
// the user is mapped then the mapping should contain the license list.
// The structure is a sorted array of pointers to License Records, and
// access is controled by the ServiceTableLock.
//
// The license is identified by the Service Family Name (the license list
// is sorted on this).
//
/////////////////////////////////////////////////////////////////////////
int __cdecl
LicenseListCompare(const void *arg1, const void *arg2) {
PUSER_LICENSE_RECORD pLic1, pLic2;
pLic1 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg1);
pLic2 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg2);
return lstrcmpi( pLic1->Family->Name, pLic2->Family->Name );
} // LicenseListCompare
/////////////////////////////////////////////////////////////////////////
PUSER_LICENSE_RECORD
LicenseListFind(
LPTSTR Name,
PUSER_LICENSE_RECORD *pLicenseList,
ULONG NumTableEntries
)
/*++
Routine Description:
Find the license in a license list for the given Family of products.
Arguments:
Name - Name of product family to find license for.
pLicenseList - Size of the license list to search.
NumTableEntries - Pointer to the license List to search.
Return Value:
Pointer to the found License Record, or NULL if not found.
--*/
{
LONG begin = 0;
LONG end = (LONG) NumTableEntries - 1;
LONG cur;
int match;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: LicenseListFind\n"));
#endif
if ((Name == NULL) || (pLicenseList == NULL) || (NumTableEntries == 0))
return NULL;
while (end >= begin) {
// go halfway in-between
cur = (begin + end) / 2;
// compare the two result into match
match = lstrcmpi(Name, pLicenseList[cur]->Family->Name);
if (match < 0)
// move new begin
end = cur - 1;
else
begin = cur + 1;
if (match == 0)
return pLicenseList[cur];
}
return NULL;
} // LicenseListFind
/////////////////////////////////////////////////////////////////////////
NTSTATUS
LicenseListDelete(
PMASTER_SERVICE_ROOT Family,
PUSER_LICENSE_RECORD **pLicenses,
PULONG pLicenseListSize
)
/*++
Routine Description:
Delete the given license from the license list.
Arguments:
Family -
pLicenses -
pLicenseListSize -
Return Value:
STATUS_SUCCESS if successful, else error code.
--*/
{
PUSER_LICENSE_RECORD *LicenseList;
ULONG LicenseListSize;
PUSER_LICENSE_RECORD LicenseRec;
ULONG i;
PUSER_LICENSE_RECORD *pLicenseListTmp;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: LicenseListDelete\n"));
#endif
if ( (pLicenses == NULL) || (pLicenseListSize == NULL) )
return STATUS_OBJECT_NAME_NOT_FOUND;
LicenseListSize = *pLicenseListSize;
LicenseList = *pLicenses;
//
// Get record based on name given
//
LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize);
if (LicenseRec == NULL)
return STATUS_OBJECT_NAME_NOT_FOUND;
//
// Check if this is the last user
//
if (LicenseListSize == 1) {
LocalFree(LicenseList);
*pLicenseListSize = 0;
*pLicenses = NULL;
return STATUS_SUCCESS;
}
//
// Not the last so find it in the list
//
i = 0;
while ( (i < LicenseListSize) && (LicenseList[i]->Family != Family) )
i++;
//
// Now move everything below it up.
//
i++;
while (i < LicenseListSize) {
LicenseList[i-1] = LicenseList[i];
i++;
}
pLicenseListTmp = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize - 1), LHND);
//
// Make sure we could allocate table
//
if (pLicenseListTmp != NULL) {
LicenseList = pLicenseListTmp;
}
LicenseListSize--;
LocalFree(LicenseRec);
*pLicenses = LicenseList;
*pLicenseListSize = LicenseListSize;
return STATUS_SUCCESS;
} // LicenseListDelete
/////////////////////////////////////////////////////////////////////////
PUSER_LICENSE_RECORD
LicenseListAdd(
PMASTER_SERVICE_ROOT Family,
PUSER_LICENSE_RECORD **pLicenses,
PULONG pLicenseListSize
)
/*++
Routine Description:
Adds an empty license record to the license list. Sets the license
family, but not any of the other info.
Arguments:
Return Value:
--*/
{
PUSER_LICENSE_RECORD *LicenseList;
ULONG LicenseListSize;
PUSER_LICENSE_RECORD LicenseRec;
PUSER_LICENSE_RECORD *pLicenseListTmp;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: LicenseListAdd\n"));
#endif
if ((Family == NULL) || (pLicenses == NULL) || (pLicenseListSize == NULL) )
return NULL;
LicenseList = *pLicenses;
LicenseListSize = *pLicenseListSize;
//
// We do a double check here to see if another thread just got done
// adding the Mapping, between when we checked last and actually got
// the write lock.
//
LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize );
if (LicenseRec != NULL) {
return LicenseRec;
}
LicenseRec = (PUSER_LICENSE_RECORD) LocalAlloc(LPTR, sizeof(USER_LICENSE_RECORD));
if (LicenseRec == NULL) {
ASSERT(FALSE);
return NULL;
}
//
// Allocate space for table (zero init it).
//
if (LicenseList == NULL)
pLicenseListTmp = (PUSER_LICENSE_RECORD *) LocalAlloc(LPTR, sizeof(PUSER_LICENSE_RECORD));
else
pLicenseListTmp = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize + 1), LHND);
//
// Make sure we could allocate Mapping table
//
if (pLicenseListTmp == NULL) {
LocalFree(LicenseRec);
return NULL;
} else {
LicenseList = pLicenseListTmp;
}
// now copy it over...
LicenseList[LicenseListSize] = LicenseRec;
LicenseRec->Family = Family;
LicenseRec->Flags = LLS_FLAG_LICENSED;
LicenseRec->RefCount = 0;
LicenseRec->Service = NULL;
LicenseRec->LicensesNeeded = 0;
LicenseListSize++;
// Have added the entry - now need to sort it in order of the names
qsort((void *) LicenseList, (size_t) LicenseListSize, sizeof(PUSER_LICENSE_RECORD), LicenseListCompare);
*pLicenses = LicenseList;
*pLicenseListSize = LicenseListSize;
return LicenseRec;
} // LicenseListAdd
/////////////////////////////////////////////////////////////////////////
// These routines are specific to the license list in the user and
// mapping records.
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
VOID
UserLicenseListFree (
PUSER_RECORD pUser
)
/*++
Routine Description:
Walks the license list deleting all entries and freeing up any claimed
licenses from the service table. This only cleans up the licenses
in a user record (not a mapping) so the # licenses is always 1.
Arguments:
Return Value:
--*/
{
ULONG i;
BOOL ReScan = FALSE;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserLicenseListFree\n"));
#endif
//
// Walk license table and free all licenses
//
for (i = 0; i < pUser->LicenseListSize; i++) {
pUser->LicenseList[i]->Service->LicensesUsed -= 1;
pUser->LicenseList[i]->Service->LicensesClaimed -= (1 - pUser->LicenseList[i]->LicensesNeeded);
pUser->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE;
ReScan = TRUE;
LocalFree(pUser->LicenseList[i]);
}
//
// Free related entries in user list
//
if (pUser->LicenseList != NULL)
LocalFree(pUser->LicenseList);
pUser->LicenseList = NULL;
pUser->LicenseListSize = 0;
pUser->LicensedProducts = 0;
//
// Get rid of pointers in services table
//
for (i = 0; i < pUser->ServiceTableSize; i++)
pUser->Services[i].License = NULL;
//
// Check if we freed up licenses and need to re-scan the user-table
//
if (ReScan) {
//
// Set to licensed so scan doesn't assign to ourself
//
pUser->Flags |= LLS_FLAG_LICENSED;
for (i = 0; i < RootServiceListSize; i++) {
if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
FamilyLicenseUpdate( RootServiceList[i] );
}
}
if (pUser->ServiceTableSize > 0)
pUser->Flags &= ~LLS_FLAG_LICENSED;
}
} // UserLicenseListFree
/////////////////////////////////////////////////////////////////////////
VOID
MappingLicenseListFree (
PMAPPING_RECORD pMap
)
/*++
Routine Description:
Walks the license list in a mapping freeing up any claimed licenses.
Like UserLicenseListFree, but for a mapping.
Arguments:
Return Value:
--*/
{
ULONG i;
BOOL ReScan = FALSE;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: MappingLicenseListFree\n"));
#endif
//
// Walk license table and free all licenses
//
for (i = 0; i < pMap->LicenseListSize; i++) {
pMap->LicenseList[i]->Service->LicensesUsed -= pMap->Licenses;
pMap->LicenseList[i]->Service->LicensesClaimed -= (pMap->Licenses - pMap->LicenseList[i]->LicensesNeeded);
pMap->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE;
ReScan = TRUE;
LocalFree(pMap->LicenseList[i]);
}
//
// Free related entries in mapping list
//
if (pMap->LicenseList != NULL)
LocalFree(pMap->LicenseList);
pMap->LicenseList = NULL;
pMap->LicenseListSize = 0;
//
// Check if we freed up licenses and need to re-scan the user-table
//
if (ReScan)
for (i = 0; i < RootServiceListSize; i++) {
if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
FamilyLicenseUpdate( RootServiceList[i] );
}
}
} // MappingLicenseListFree
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// The service table is a linear array of records pointed to by the
// user record. Each entry contains a pointer into the service table
// identifying the service, some statistical useage information and a
// pointer into the license table identifying the license used by the
// service.
//
/////////////////////////////////////////////////////////////////////////
int __cdecl
SvcListCompare(
const void *arg1,
const void *arg2
)
{
PSVC_RECORD pSvc1, pSvc2;
pSvc1 = (PSVC_RECORD) arg1;
pSvc2 = (PSVC_RECORD) arg2;
return lstrcmpi( pSvc1->Service->Name, pSvc2->Service->Name );
} // SvcListCompare
/////////////////////////////////////////////////////////////////////////
PSVC_RECORD
SvcListFind(
LPTSTR DisplayName,
PSVC_RECORD ServiceList,
ULONG NumTableEntries
)
/*++
Routine Description:
Internal routine to actually do binary search on Service List in user
record. This is a binary search, however since the string pointers are
from the service table and therefore the pointers are fixed, we only
need to compare the pointers, not the strings themselves to find a
match.
Arguments:
ServiceName -
Return Value:
Pointer to found service table entry or NULL if not found.
--*/
{
LONG begin = 0;
LONG end = (LONG) NumTableEntries - 1;
LONG cur;
int match;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: SvcListFind\n"));
#endif
if ((DisplayName == NULL) || (ServiceList == NULL) || (NumTableEntries == 0))
return NULL;
while (end >= begin) {
// go halfway in-between
cur = (begin + end) / 2;
// compare the two result into match
match = lstrcmpi(DisplayName, ServiceList[cur].Service->Name);
if (match < 0)
// move new begin
end = cur - 1;
else
begin = cur + 1;
if (match == 0)
return &ServiceList[cur];
}
return NULL;
} // SvcListFind
/////////////////////////////////////////////////////////////////////////
NTSTATUS
SvcListDelete(
LPTSTR UserName,
LPTSTR ServiceName
)
/*++
Routine Description:
Deletes a service record from the service table.
Arguments:
Return Value:
--*/
{
PUSER_RECORD pUserRec;
PSVC_RECORD pService;
PSVC_RECORD SvcTable = NULL;
PUSER_LICENSE_RECORD License = NULL;
ULONG NumLicenses = 1;
ULONG i;
BOOL ReScan = FALSE;
PMASTER_SERVICE_ROOT Family = NULL;
PSVC_RECORD pSvcTableTmp;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: SvcListDelete\n"));
#endif
pUserRec = UserListFind(UserName);
if (pUserRec == NULL)
return STATUS_OBJECT_NAME_NOT_FOUND;
RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
pService = SvcListFind( ServiceName, pUserRec->Services, pUserRec->ServiceTableSize );
//
// If we couldn't find it then get out.
//
if (pService == NULL) {
RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
Family = pService->Service->Family;
//
// If we are a mapping then we may use more then one license
//
if (pUserRec->Mapping != NULL)
NumLicenses = pUserRec->Mapping->Licenses;
License = pService->License;
if (License != NULL) {
License->RefCount--;
//
// If this is the last service that uses this license then we need
// to get rid of it.
//
if (License->RefCount == 0) {
License->Service->LicensesUsed -= NumLicenses;
NumLicenses -= License->LicensesNeeded;
License->Service->LicensesClaimed -= NumLicenses;
//
// Do we need to delete it from the mapping or user license table?
//
if (pUserRec->Mapping != NULL) {
if ((License->Service == BackOfficeRec) && (pUserRec->Mapping->Flags & LLS_FLAG_SUITE_AUTO))
pUserRec->Mapping->Flags &= ~LLS_FLAG_SUITE_USE;
LicenseListDelete(License->Service->Family, &pUserRec->Mapping->LicenseList, &pUserRec->Mapping->LicenseListSize );
} else {
if ((License->Service == BackOfficeRec) && (pUserRec->Flags & LLS_FLAG_SUITE_AUTO))
pUserRec->Flags &= ~LLS_FLAG_SUITE_USE;
LicenseListDelete(License->Service->Family, &pUserRec->LicenseList, &pUserRec->LicenseListSize );
}
//
// Freed a license so need to scan and adjust counts
//
ReScan = TRUE;
}
}
if (pService->Flags & LLS_FLAG_LICENSED)
pUserRec->LicensedProducts--;
else {
//
// This was an unlicensed product - see if this makes the user
// licensed
//
if (pUserRec->LicensedProducts == (pUserRec->ServiceTableSize - 1))
pUserRec->Flags |= LLS_FLAG_LICENSED;
}
//
// First check if this is the only entry in the table
//
if (pUserRec->ServiceTableSize == 1) {
LocalFree(pUserRec->Services);
pUserRec->Services = NULL;
goto SvcListDeleteExit;
}
//
// Find this record linearly in the table.
//
i = 0;
while ((i < pUserRec->ServiceTableSize) && (lstrcmpi(pUserRec->Services[i].Service->Name, ServiceName)))
i++;
//
// Now move everything below it up.
//
i++;
while (i < pUserRec->ServiceTableSize) {
memcpy(&pUserRec->Services[i-1], &pUserRec->Services[i], sizeof(SVC_RECORD));
i++;
}
pSvcTableTmp = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize - 1), LHND);
if (pSvcTableTmp == NULL) {
pUserRec->ServiceTableSize--;
RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
return STATUS_SUCCESS;
} else {
SvcTable = pSvcTableTmp;
}
pUserRec->Services = SvcTable;
SvcListDeleteExit:
pUserRec->ServiceTableSize--;
if (pUserRec->ServiceTableSize == 0)
pUserRec->Services = NULL;
RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
if (ReScan)
FamilyLicenseUpdate ( Family );
return STATUS_SUCCESS;
} // SvcListDelete
/////////////////////////////////////////////////////////////////////////
VOID
SvcListLicenseFree(
PUSER_RECORD pUser
)
/*++
Routine Description:
Walk the services table and free up any licenses they are using. If the
licenses are then no longer needed (refCount == 0) then the license is
deleted.
Arguments:
Return Value:
--*/
{
ULONG i;
ULONG NumLicenses = 1;
PUSER_LICENSE_RECORD License = NULL;
BOOL ReScan = FALSE;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: SvcListLicenseFree\n"));
#endif
//
// If we are a mapping then we may use more then one license
//
for (i = 0; i < pUser->ServiceTableSize; i++) {
if (pUser->Mapping != NULL)
NumLicenses = pUser->Mapping->Licenses;
else
NumLicenses = 1;
License = pUser->Services[i].License;
if (License != NULL) {
License->RefCount--;
//
// If this is the last service that uses this license then we need
// to get rid of it.
//
if (License->RefCount == 0) {
if ( (pUser->Mapping != NULL) && (License->Service == BackOfficeRec) && (pUser->Mapping->Flags & LLS_FLAG_SUITE_AUTO) )
pUser->Mapping->Flags &= ~LLS_FLAG_SUITE_USE;
License->Service->LicensesUsed -= NumLicenses;
NumLicenses -= License->LicensesNeeded;
if (License->Service->LicensesClaimed > 0) {
//
// Freed a license so need to scan and adjust counts
//
License->Service->Family->Flags |= LLS_FLAG_UPDATE;
ReScan = TRUE;
}
License->Service->LicensesClaimed -= NumLicenses;
if (pUser->Mapping != NULL)
LicenseListDelete(License->Service->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize );
else
LicenseListDelete(License->Service->Family, &pUser->LicenseList, &pUser->LicenseListSize );
}
}
pUser->Services[i].License = NULL;
}
pUser->LicensedProducts = 0;
//
// Check if we freed up licenses and need to re-scan the user-table
//
if (ReScan) {
//
// Flag license so rescan won't worry about this user
//
pUser->Flags |= LLS_FLAG_LICENSED;
for (i = 0; i < RootServiceListSize; i++) {
if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
FamilyLicenseUpdate( RootServiceList[i] );
}
}
}
} // SvcListLicenseFree
/////////////////////////////////////////////////////////////////////////
VOID
SvcListLicenseUpdate(
PUSER_RECORD pUser
)
/*++
Routine Description:
Walk the services table and assign the appropriate license to each
service. This is the opposite of the SvcListLicenseFree Routine.
Arguments:
Return Value:
--*/
{
ULONG i;
ULONG Claimed = 0;
PUSER_LICENSE_RECORD License = NULL;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: SvcListLicenseUpdate\n"));
#endif
//
// Check if user is set to use BackOffice
if ( pUser->Flags & LLS_FLAG_SUITE_USE ) {
//
// Go grab a backoffice license to fulfill the suite useage
//
License = LicenseListAdd(BackOfficeRec->Family, &pUser->LicenseList, &pUser->LicenseListSize);
ASSERT(License != NULL);
if (License != NULL) {
License->Service = BackOfficeRec;
RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
// Can only claim a # of licenses that we have
if ( BackOfficeRec->LicensesClaimed < BackOfficeRec->Licenses) {
Claimed = BackOfficeRec->Licenses - BackOfficeRec->LicensesClaimed;
if (Claimed > 1)
Claimed = 1;
}
//
// Adjust license counts
//
BackOfficeRec->LicensesUsed += 1;
BackOfficeRec->LicensesClaimed += Claimed;
License->LicensesNeeded = 1 - Claimed;
//
// Figure out if we are licensed or not.
//
if (License->LicensesNeeded > 0) {
//
// Not licensed
//
License->Flags &= ~LLS_FLAG_LICENSED;
pUser->Flags &= ~LLS_FLAG_LICENSED;
for (i = 0; i < pUser->ServiceTableSize; i++) {
pUser->Services[i].Flags &= ~LLS_FLAG_LICENSED;
pUser->Services[i].License = License;
License->RefCount++;
}
} else {
//
// Licensed
//
License->Flags |= LLS_FLAG_LICENSED;
pUser->Flags |= LLS_FLAG_LICENSED;
for (i = 0; i < pUser->ServiceTableSize; i++) {
pUser->LicensedProducts++;
pUser->Services[i].Flags |= LLS_FLAG_LICENSED;
pUser->Services[i].License = License;
License->RefCount++;
}
}
RtlReleaseResource(&MasterServiceListLock);
}
} else {
BOOL Licensed = TRUE;
//
// Loop through all the services and make sure they are all
// licensed.
//
for (i = 0; i < pUser->ServiceTableSize; i++) {
SvcLicenseUpdate(pUser, &pUser->Services[i]);
if ( pUser->Services[i].Flags & LLS_FLAG_LICENSED )
pUser->LicensedProducts++;
else
Licensed = FALSE;
}
if (Licensed)
pUser->Flags |= LLS_FLAG_LICENSED;
else
pUser->Flags &= ~LLS_FLAG_LICENSED;
}
} // SvcListLicenseUpdate
/////////////////////////////////////////////////////////////////////////
VOID
SvcLicenseUpdate(
PUSER_RECORD pUser,
PSVC_RECORD Svc
)
/*++
Routine Description:
For a given service record for a user check and update license compliance.
Is a single service record version of SvcListLicenseUpdate.
Arguments:
Return Value:
--*/
{
ULONG NumLicenses = 1;
PUSER_LICENSE_RECORD License = NULL;
BOOL UseMapping = FALSE;
PMASTER_SERVICE_RECORD LicenseService = NULL;
PMASTER_SERVICE_RECORD Service;
BOOL ReScan = FALSE;
DWORD Flags = 0;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: SvcLicenseUpdate\n"));
#endif
if ((pUser == NULL) || (Svc == NULL))
return;
Flags = pUser->Flags;
//
// If we are a mapping then we may use more then one license
//
if (pUser->Mapping != NULL) {
NumLicenses = pUser->Mapping->Licenses;
UseMapping = TRUE;
Flags = pUser->Mapping->Flags;
}
//
// Try to find a license record in the license list of the user or mapping
// to use. If we are using BackOffice then look for BackOffice license
// instead of the service license.
if (Flags & LLS_FLAG_SUITE_USE) {
Service = BackOfficeRec;
if (UseMapping)
License = LicenseListFind(BackOfficeStr, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
else
License = LicenseListFind(BackOfficeStr, pUser->LicenseList, pUser->LicenseListSize);
} else {
//
// Not BackOffice - so look for normal service license
//
Service = Svc->Service;
ASSERT(Service != NULL);
//
// Try to find a license for this family of products
//
if (UseMapping)
License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
else
License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize);
}
//
// Check if we couldn't find a license. If we didn't find it then we need
// to create a new license for this.
//
if (License == NULL) {
ULONG LicenseListSize;
PUSER_LICENSE_RECORD *LicenseList;
//
// The license list to use depends if we are part of a mapping or not.
//
if (UseMapping) {
LicenseListSize = pUser->Mapping->LicenseListSize;
LicenseList = pUser->Mapping->LicenseList;
} else {
LicenseListSize = pUser->LicenseListSize;
LicenseList = pUser->LicenseList;
}
//
// Check if we need to add a license for BackOffice or just the service
// itself.
//
if (Flags & LLS_FLAG_SUITE_USE)
License = LicenseListAdd(BackOfficeRec->Family, &LicenseList, &LicenseListSize);
else
License = LicenseListAdd(Service->Family, &LicenseList, &LicenseListSize);
//
// Now update the couters in the parent record
//
if (UseMapping) {
pUser->Mapping->LicenseListSize = LicenseListSize;
pUser->Mapping->LicenseList = LicenseList;
} else {
pUser->LicenseListSize = LicenseListSize;
pUser->LicenseList = LicenseList;
}
if (License != NULL)
License->LicensesNeeded = NumLicenses;
}
//
// We have either found an old license or added a new one, either way
// License points to it.
//
if (License != NULL) {
RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
//
// if we have a license for this family already and the product
// version >= current then we are okay, else need to get new license
//
if ( (License->Service != NULL) && (License->Service->Version >= Service->Version) ) {
LicenseService = License->Service;
} else {
//
// we have an old license for this family, but the version
// isn't adequate, so we need to try and get a new license.
// Walk the family tree looking for the licenses we
// need.
//
LicenseService = Service;
while ((LicenseService != NULL) && ( (LicenseService->LicensesUsed + NumLicenses) > LicenseService->Licenses) )
if (LicenseService->next > 0)
LicenseService = MasterServiceTable[LicenseService->next - 1];
else
LicenseService = NULL;
//
// if we couldn't find a license just use the old
// service.
if (LicenseService == NULL)
LicenseService = Service;
else {
//
// Need to clean up old stuff
//
if (License->Service != NULL) {
//
// If we actually free up any licenses then mark that we need
// to rescan to allocate these freed licenses.
//
if ((NumLicenses - License->LicensesNeeded) > 0)
ReScan = TRUE;
License->Service->LicensesUsed -= NumLicenses;
License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded);
License->LicensesNeeded = NumLicenses;
License->Service = NULL;
}
}
}
if (LicenseService != NULL) {
ULONG Claimed = 0;
//
// If we switched services then adjust LicensesUsed
//
if (License->Service != LicenseService) {
LicenseService->LicensesUsed += NumLicenses;
if (License->Service != NULL) {
License->Service->LicensesUsed -= NumLicenses;
}
}
// Can only claim a # of licenses that we have
if ( LicenseService->LicensesClaimed < LicenseService->Licenses) {
Claimed = LicenseService->Licenses - LicenseService->LicensesClaimed;
if (Claimed > License->LicensesNeeded)
Claimed = License->LicensesNeeded;
}
LicenseService->LicensesClaimed += Claimed;
License->Service = LicenseService;
License->LicensesNeeded -= Claimed;
if (License->LicensesNeeded != 0)
License->Flags &= ~LLS_FLAG_LICENSED;
else
License->Flags |= LLS_FLAG_LICENSED;
}
RtlReleaseResource(&MasterServiceListLock);
if (License->Flags & LLS_FLAG_LICENSED)
Svc->Flags |= LLS_FLAG_LICENSED;
else
Svc->Flags &= ~LLS_FLAG_LICENSED;
} else
Svc->Flags &= ~LLS_FLAG_LICENSED;
if ((Svc->License != License) && (License != NULL))
License->RefCount++;
Svc->License = License;
if (ReScan)
FamilyLicenseUpdate ( Service->Family );
} // SvcLicenseUpdate
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// Misc licensing Routines.
//
// BackOffice and Mappings have a special affect on LicenseUseage and so
// there are a couple miscelanous routines to handle them.
//
// There are also two special cases that cause us to re-walk our lists to
// fixup the licenses:
//
// 1. Sometimes when we add licenses we free up some we already had claimed.
// Ex: Users of a LicenseGroup used 5 SQL 4.0 licenses but could only
// claim 2 (there weren't enough). Later we add 5 SQL 5.0 licenses,
// since we can use these to get into license compliance we free the
// 2 previously claimed licenses and take the 5 SQL 5.0 licenses. Now
// we need to re-walk the user table to try and apply the 2 freed
// licenses.
//
// If we switch a user to BackOffice then it will also free up licenses
// causing us to re-walk the table.
//
// 2. If we ever apply new licenses to a user in a mapping then we need
// to re-walk all the other users in the mapping and update their
// license compliance.
//
/////////////////////////////////////////////////////////////////////////
VOID
MappingLicenseUpdate (
PMAPPING_RECORD Mapping,
BOOL ReSynch
)
/*++
Routine Description:
Go through all user records for a given mapping and recalc license
compliance.
Arguments:
Mapping - the Mapping to recalc licenses for.
ReSync - If true all previous licenses are destroyed before the licenses
are checked, else only services that currently don't have a
license assignment are touched.
Return Value:
--*/
{
ULONG i, j;
PUSER_LICENSE_RECORD License = NULL;
PUSER_RECORD pUser;
PSVC_RECORD SvcTable = NULL;
BOOL BackOfficeCheck = FALSE;
ULONG Claimed;
BOOL Licensed = TRUE;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: MappingLicenseUpdate\n"));
#endif
//
// Run through all the users in the mapping - adjust their licenses
// based on the licenses the mapping has...
//
RtlAcquireResourceExclusive(&MappingListLock, TRUE);
for (i = 0; i < Mapping->LicenseListSize; i++)
if (!(Mapping->LicenseList[i]->Flags & LLS_FLAG_LICENSED))
Licensed = FALSE;
if (Licensed)
Mapping->Flags |= LLS_FLAG_LICENSED;
else
Mapping->Flags &= ~LLS_FLAG_LICENSED;
//
// If we want to resynch then blow away all old references
//
if (ReSynch)
for (i = 0; i < Mapping->LicenseListSize; i++)
Mapping->LicenseList[i]->RefCount = 0;
//
// Special handling if the Mapping uses BackOffice
//
if (Mapping->Flags & LLS_FLAG_SUITE_USE) {
License = LicenseListFind(BackOfficeStr, Mapping->LicenseList, Mapping->LicenseListSize);
//
// If there isn't one (can happen if all users were deleted from
// the mapping with BackOffice flag set). Then update everything.
//
if (License == NULL) {
License = LicenseListAdd(BackOfficeRec->Family, &Mapping->LicenseList, &Mapping->LicenseListSize);
ASSERT(License != NULL);
if (License != NULL) {
License->Service = BackOfficeRec;
// Can only claim a # of licenses that we have
if ( BackOfficeRec->LicensesClaimed < BackOfficeRec->Licenses) {
Claimed = BackOfficeRec->Licenses - BackOfficeRec->LicensesClaimed;
if (Claimed > Mapping->Licenses)
Claimed = Mapping->Licenses;
}
BackOfficeRec->LicensesUsed += Mapping->Licenses;
BackOfficeRec->LicensesClaimed += Claimed;
License->LicensesNeeded = Mapping->Licenses - Claimed;
Mapping->Flags |= LLS_FLAG_LICENSED;
if (License->LicensesNeeded > 0) {
License->Flags &= ~LLS_FLAG_LICENSED;
Mapping->Flags &= ~LLS_FLAG_LICENSED;
}
}
}
}
//
// Run through all the members in the Mapping and update their license
// Compliance.
//
for (i = 0; i < Mapping->NumMembers; i++) {
pUser = UserListFind(Mapping->Members[i]);
if ( (pUser != NULL) && (pUser->Mapping == Mapping) ) {
RtlEnterCriticalSection(&pUser->ServiceTableLock);
SvcTable = pUser->Services;
pUser->LicensedProducts = 0;
if (Mapping->Flags & LLS_FLAG_SUITE_USE) {
if (Mapping->Flags & LLS_FLAG_LICENSED) {
pUser->Flags |= LLS_FLAG_LICENSED;
pUser->LicensedProducts = pUser->ServiceTableSize;
} else
pUser->Flags &= ~LLS_FLAG_LICENSED;
//
// All Services and users are flagged as per BackOffice
//
for (j = 0; j < pUser->ServiceTableSize; j++) {
if (ReSynch)
SvcTable[j].License = NULL;
if (SvcTable[j].License == NULL) {
SvcTable[j].License = License;
License->RefCount++;
}
if (Mapping->Flags & LLS_FLAG_LICENSED) {
SvcTable[j].Flags |= LLS_FLAG_LICENSED;
} else
SvcTable[j].Flags &= ~LLS_FLAG_LICENSED;
}
} else {
BOOL Licensed = TRUE;
//
// Fixup all the service records
//
for (j = 0; j < pUser->ServiceTableSize; j++) {
if (ReSynch)
SvcTable[j].License = NULL;
if (SvcTable[j].License == NULL) {
SvcLicenseUpdate(pUser, &SvcTable[j]);
BackOfficeCheck = TRUE;
}
}
//
// Now run through the services again and see if this user is
// actually licenses for all the products.
//
pUser->LicensedProducts = 0;
for (j = 0; j < pUser->ServiceTableSize; j++)
if ( (SvcTable[j].License != NULL) && (SvcTable[j].License->Flags & LLS_FLAG_LICENSED) ) {
SvcTable[j].Flags |= LLS_FLAG_LICENSED;
pUser->LicensedProducts++;
} else {
SvcTable[j].Flags &= ~LLS_FLAG_LICENSED;
Licensed = FALSE;
}
if (Licensed)
pUser->Flags |= LLS_FLAG_LICENSED;
else
pUser->Flags &= ~LLS_FLAG_LICENSED;
}
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
}
}
RtlReleaseResource(&MappingListLock);
//
// Check if we need to re-check for BackOffice
//
if (BackOfficeCheck && (pUser != NULL))
UserBackOfficeCheck( pUser );
} // MappingLicenseUpdate
/////////////////////////////////////////////////////////////////////////
VOID
UserMappingAdd (
PMAPPING_RECORD Mapping,
PUSER_RECORD pUser
)
/*++
Routine Description:
Takes care of re-adjusting the licenses when we add a user to a mapping.
We need to free up any old licenses they have and point them to use
the licenses the mapping has.
Arguments:
Return Value:
--*/
{
ULONG i, j;
PUSER_LICENSE_RECORD License = NULL;
PSVC_RECORD SvcTable = NULL;
BOOL ReScan = FALSE;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserMappingAdd\n"));
#endif
if ( (pUser == NULL) || (Mapping == NULL) )
return;
//
// Run though and clean up all old licenses
//
RtlEnterCriticalSection(&pUser->ServiceTableLock);
SvcListLicenseFree(pUser);
UserLicenseListFree(pUser);
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
pUser->Mapping = Mapping;
MappingLicenseUpdate(Mapping, FALSE);
} // UserMappingAdd
/////////////////////////////////////////////////////////////////////////
VOID
FamilyLicenseUpdate (
PMASTER_SERVICE_ROOT Family
)
/*++
Routine Description:
Used when licenses are freed-up or added to a given family of products.
Goes through the user list looking for out-of-license conditions for the
given family of products and distributes the new licenses.
Arguments:
Return Value:
--*/
{
ULONG NumLicenses = 1;
PUSER_LICENSE_RECORD License = NULL;
PMASTER_SERVICE_RECORD LicenseService = NULL;
ULONG i, j;
PUSER_RECORD pUser;
BOOL UseMapping = FALSE;
BOOL ReScan = TRUE;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: FamilyLicenseUpdate\n"));
#endif
RtlAcquireResourceExclusive(&UserListLock, TRUE);
while (ReScan) {
//
// Walk user list in order of entry - adding any licenses
//
ReScan = FALSE;
i = 0;
while (i < UserListNumEntries) {
pUser = LLSGetElementGenericTable(&UserList, i);
if (pUser != NULL) {
//
// only worry about un-licensed users
//
if ( !(pUser->Flags & LLS_FLAG_LICENSED ) ) {
//
// Find the License?
//
RtlEnterCriticalSection(&pUser->ServiceTableLock);
if (pUser->Mapping != NULL) {
License = LicenseListFind(Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
NumLicenses = pUser->Mapping->Licenses;
} else {
License = LicenseListFind(Family->Name, pUser->LicenseList, pUser->LicenseListSize);
NumLicenses = 1;
}
//
// Make sure we need any extra licenses for this product
//
if ( (License != NULL) && (License->LicensesNeeded > 0) ) {
//
// We have found a user using this family of products in need
// of more licenses...
//
LicenseService = License->Service;
if (pUser->Mapping != NULL)
pUser->Mapping->Flags |= LLS_FLAG_UPDATE;
//
// Check if we can satisfy licenses using currently
// assigned service
//
if ((LicenseService->Licenses - LicenseService->LicensesClaimed) >= License->LicensesNeeded) {
LicenseService->LicensesClaimed += License->LicensesNeeded;
License->LicensesNeeded = 0;
} else {
//
// See if any other service will satisfy it...
//
while ((LicenseService != NULL) && ((LicenseService->Licenses - LicenseService->LicensesClaimed) < NumLicenses ) )
if (LicenseService->next > 0)
LicenseService = MasterServiceTable[LicenseService->next - 1];
else
LicenseService = NULL;
//
// check if we found a service to satisfy licensing needs
//
if (LicenseService != NULL) {
//
// Free up any stuff - since we are freeing licenses
// we need to re-scan.
//
ReScan = TRUE;
License->Service->LicensesUsed -= NumLicenses;
License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded);
//
// Now do new stuff
//
License->Service = LicenseService;
License->Service->LicensesUsed += NumLicenses;
License->Service->LicensesClaimed += NumLicenses;
License->LicensesNeeded = 0;
} else {
//
// Eat any unclaimed licenses
//
LicenseService = License->Service;
if (LicenseService->Licenses > LicenseService->LicensesClaimed) {
License->LicensesNeeded -= (LicenseService->Licenses - LicenseService->LicensesClaimed);
LicenseService->LicensesClaimed = LicenseService->Licenses;
}
}
}
//
// Check if we got into license
//
if (License->LicensesNeeded == 0) {
BOOL Licensed = TRUE;
License->Flags |= LLS_FLAG_LICENSED;
//
// this license is fulfilled so scan product list and
// adjust flags on any product using this license.
//
for (j = 0; j < pUser->ServiceTableSize; j++) {
if (pUser->Services[j].License == License) {
pUser->Services[j].Flags |= LLS_FLAG_LICENSED;
} else
if (!(pUser->Services[j].Flags & LLS_FLAG_LICENSED))
Licensed = FALSE;
}
//
// Recalc how many products are licensed
//
pUser->LicensedProducts = 0;
for (j = 0; j < pUser->ServiceTableSize; j++) {
if (pUser->Services[j].Flags & LLS_FLAG_LICENSED)
pUser->LicensedProducts++;
}
if (Licensed)
pUser->Flags |= LLS_FLAG_LICENSED;
}
}
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
}
}
i++;
}
}
//
// If this license is for BackOffice, we have applied any licenses to
// anything set to use BackOffice. If there are still licenses left
// then see if any users should be auto-switched to BackOffice.
//
// if (Family == BackOfficeRec->Family) {
i = 0;
while ( (BackOfficeRec->LicensesClaimed < BackOfficeRec->Licenses) && (i < UserListNumEntries) ) {
pUser = LLSGetElementGenericTable(&UserList, i);
if (pUser != NULL)
UserBackOfficeCheck(pUser);
i++;
}
// }
//
// Run through mapping and re-adjust any that need it.
//
RtlAcquireResourceExclusive(&MappingListLock, TRUE);
for (i = 0; i < MappingListSize; i++) {
if (MappingList[i]->Flags & LLS_FLAG_UPDATE) {
MappingList[i]->Flags &= ~LLS_FLAG_UPDATE;
MappingLicenseUpdate( MappingList[i], FALSE );
}
}
RtlReleaseResource(&MappingListLock);
RtlReleaseResource(&UserListLock);
} // FamilyLicenseUpdate
/////////////////////////////////////////////////////////////////////////
VOID
UserListLicenseDelete(
PMASTER_SERVICE_RECORD Service,
LONG Quantity
)
/*++
Routine Description:
This is used when licenses are deleted. It must walk the user-list in
the reverse order they were entered (since licenses are applied in a
FIFO manner they are removed in a LIFO manner) and delete the required
number of licenses from those consumed.
Arguments:
Return Value:
--*/
{
LONG Licenses;
ULONG i, j;
PUSER_RECORD pUser;
PSVC_RECORD pService;
ULONG NumLicenses = 1;
PUSER_LICENSE_RECORD License = NULL;
BOOL UseMapping = FALSE;
LONG Claimed;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserListLicenseDelete\n"));
#endif
RtlAcquireResourceExclusive(&UserListLock, TRUE);
Licenses = 0 - Quantity;
//
// Walk user list in opposite order of entry - removing licenses
//
i = UserListNumEntries - 1;
while (((LONG)i >= 0) && (Licenses > 0)) {
pUser = LLSGetElementGenericTable(&UserList, i);
if (pUser != NULL) {
NumLicenses = 1;
UseMapping = FALSE;
//
// If we are a mapping then we may use more then one license
//
if (pUser->Mapping != NULL) {
NumLicenses = pUser->Mapping->Licenses;
UseMapping = TRUE;
}
//
// Try to find a license for this family of products
//
if (UseMapping)
License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
else
License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize);
if (License != NULL) {
//
// Check if same as product we adjusted
//
if (License->Service == Service) {
//
// Can only release how many we took
//
Claimed = NumLicenses - License->LicensesNeeded;
if (Claimed > 0) {
if (Claimed > Licenses) {
License->LicensesNeeded += Licenses;
License->Service->LicensesClaimed -= Licenses;
Licenses = 0;
} else {
License->LicensesNeeded = NumLicenses;
License->Service->LicensesClaimed -= Claimed;
Licenses -= Claimed;
}
License->Flags &= ~LLS_FLAG_LICENSED;
//
// Flag any mapping that we need to recalc uses in the
// mapping
//
if (UseMapping)
pUser->Mapping->Flags |= LLS_FLAG_UPDATE;
//
// Scan product list and adjust flags on any
// product using this license.
//
RtlEnterCriticalSection(&pUser->ServiceTableLock);
for (j = 0; j < pUser->ServiceTableSize; j++)
if (pUser->Services[j].License == License)
pUser->Services[j].Flags &= ~LLS_FLAG_LICENSED;
//
// Recalc how many products are licensed
//
pUser->LicensedProducts = 0;
for (j = 0; j < pUser->ServiceTableSize; j++) {
if (pUser->Services[j].Flags & LLS_FLAG_LICENSED)
pUser->LicensedProducts++;
}
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
pUser->Flags &= ~LLS_FLAG_LICENSED;
}
}
}
}
i--;
}
//
// Run through mapping and re-adjust any that need it.
//
RtlAcquireResourceExclusive(&MappingListLock, TRUE);
for (i = 0; i < MappingListSize; i++) {
if (MappingList[i]->Flags & LLS_FLAG_UPDATE) {
MappingList[i]->Flags &= ~LLS_FLAG_UPDATE;
MappingLicenseUpdate( MappingList[i], FALSE );
}
}
RtlReleaseResource(&MappingListLock);
RtlReleaseResource(&UserListLock);
} // UserListLicenseDelete
/////////////////////////////////////////////////////////////////////////
VOID
UserBackOfficeCheck (
PUSER_RECORD pUser
)
/*++
Routine Description:
Checks if the user should switch to BackOffice, and if so - does so. If
we switch to BackOffice then we need to free up any old licenes the
user may be using and claim a BackOffice License.
Note: We will only switch to BackOffice if there are enough BackOffice
licenses available to satisfy our needs.
Arguments:
Return Value:
--*/
{
DWORD Flags;
ULONG i;
ULONG LicenseListSize;
ULONG NumLicenses = 1;
PSVC_RECORD SvcTable = NULL;
PUSER_LICENSE_RECORD *LicenseList = NULL;
PUSER_LICENSE_RECORD License = NULL;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserBackOfficeCheck\n"));
#endif
RtlEnterCriticalSection(&pUser->ServiceTableLock);
if (pUser->Mapping != NULL) {
Flags = pUser->Mapping->Flags;
LicenseListSize = pUser->Mapping->LicenseListSize;
LicenseList = pUser->Mapping->LicenseList;
NumLicenses = pUser->Mapping->Licenses;
} else {
Flags = pUser->Flags;
LicenseListSize = pUser->LicenseListSize;
LicenseList = pUser->LicenseList;
}
//
// If we are already using BackOffice - get out
//
if (Flags & LLS_FLAG_SUITE_USE) {
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
return;
}
if ( Flags & LLS_FLAG_SUITE_AUTO )
//
// if we aren't licensed, or the # services == auto switch threshold
// then switch to using BackOffice
//
if ((!(Flags & LLS_FLAG_LICENSED)) || ((LicenseListSize + 1) >= BACKOFFICE_SWITCH) ) {
//
// Make sure we have licenses for this
//
RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
if ( BackOfficeRec->Licenses >= (NumLicenses + BackOfficeRec->LicensesClaimed) ) {
//
// Free up the old licenses - temporarily claim the BackOffice
// licenses so somebody else won't.
//
BackOfficeRec->LicensesClaimed += NumLicenses;
UserLicenseListFree(pUser);
BackOfficeRec->LicensesClaimed -= NumLicenses;
//
// UserLicenseListFree might have assigned us a license in
// the rescan if we are part of a mapping so check this.
//
if (pUser->Mapping != NULL)
Flags = pUser->Mapping->Flags;
else
Flags = pUser->Flags;
//
// If we are already using BackOffice - get out
//
if (Flags & LLS_FLAG_SUITE_USE) {
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
RtlReleaseResource(&MasterServiceListLock);
return;
}
//
// And if part of a mapping free those up
//
if (pUser->Mapping != NULL)
MappingLicenseListFree(pUser->Mapping);
//
// Now add the BackOffice License
//
if (pUser->Mapping != NULL) {
pUser->Mapping->LicenseList = NULL;
pUser->Mapping->LicenseListSize = 0;
License = LicenseListAdd(BackOfficeRec->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize);
LicenseList = pUser->Mapping->LicenseList;
LicenseListSize = pUser->Mapping->LicenseListSize;
} else {
pUser->LicenseList = NULL;
pUser->LicenseListSize = 0;
License = LicenseListAdd(BackOfficeRec->Family, &pUser->LicenseList, &pUser->LicenseListSize);
LicenseList = pUser->LicenseList;
LicenseListSize = pUser->LicenseListSize;
}
ASSERT(License != NULL);
if (License != NULL)
License->Service = BackOfficeRec;
//
// if mapping adjust mapping records then go through all users and
// adjust them
//
if (pUser->Mapping != NULL) {
pUser->Mapping->Flags |= LLS_FLAG_SUITE_USE;
pUser->Mapping->Flags |= LLS_FLAG_LICENSED;
BackOfficeRec->LicensesUsed += NumLicenses;
BackOfficeRec->LicensesClaimed += NumLicenses;
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
RtlReleaseResource(&MasterServiceListLock);
MappingLicenseUpdate(pUser->Mapping, TRUE);
return;
} else {
pUser->Flags |= LLS_FLAG_SUITE_USE;
pUser->Flags |= LLS_FLAG_LICENSED;
pUser->LicensedProducts = pUser->ServiceTableSize;
BackOfficeRec->LicensesUsed += NumLicenses;
BackOfficeRec->LicensesClaimed += NumLicenses;
//
// Run through products & licenses adjusting licenses
//
SvcTable = pUser->Services;
for (i = 0; i < pUser->ServiceTableSize; i++) {
SvcTable[i].Flags |= LLS_FLAG_LICENSED;
SvcTable[i].License = License;
SvcTable[i].License->RefCount++;
}
}
}
RtlReleaseResource(&MasterServiceListLock);
}
RtlLeaveCriticalSection(&pUser->ServiceTableLock);
} // UserBackOfficeCheck
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// Utility routines for managing the user and SID lists - used mostly
// by the splay table routines.
/////////////////////////////////////////////////////////////////////////
LLS_GENERIC_COMPARE_RESULTS
SidListCompare (
struct _LLS_GENERIC_TABLE *Table,
PVOID FirstStruct,
PVOID SecondStruct
)
/*++
Routine Description:
Arguments:
Table -
FirstStruct -
SecondStruct -
Return Value:
--*/
{
PUSER_RECORD UseRec1, UseRec2;
int ret;
if ((FirstStruct == NULL) || (SecondStruct == NULL))
return LLSGenericEqual;
UseRec1 = (PUSER_RECORD) FirstStruct;
UseRec2 = (PUSER_RECORD) SecondStruct;
if (UseRec1->IDSize == UseRec2->IDSize) {
ret = memcmp((PVOID) UseRec1->UserID, (PVOID) UseRec2->UserID, UseRec1->IDSize);
if (ret < 0)
return LLSGenericLessThan;
else if (ret > 0)
return LLSGenericGreaterThan;
else
return LLSGenericEqual;
} else
//
// Not same size, so just compare length
//
if (UseRec1->IDSize > UseRec2->IDSize)
return LLSGenericGreaterThan;
else
return LLSGenericLessThan;
} // SidListCompare
/////////////////////////////////////////////////////////////////////////
LLS_GENERIC_COMPARE_RESULTS
UserListCompare (
struct _LLS_GENERIC_TABLE *Table,
PVOID FirstStruct,
PVOID SecondStruct
)
/*++
Routine Description:
Arguments:
Table -
FirstStruct -
SecondStruct -
Return Value:
--*/
{
PUSER_RECORD UseRec1, UseRec2;
int ret;
if ((FirstStruct == NULL) || (SecondStruct == NULL))
return LLSGenericEqual;
UseRec1 = (PUSER_RECORD) FirstStruct;
UseRec2 = (PUSER_RECORD) SecondStruct;
ret = lstrcmpi((LPTSTR) UseRec1->UserID, (LPTSTR) UseRec2->UserID);
if (ret < 0)
return LLSGenericLessThan;
else if (ret > 0)
return LLSGenericGreaterThan;
else
return LLSGenericEqual;
} // UserListCompare
/////////////////////////////////////////////////////////////////////////
PVOID
UserListAlloc (
struct _LLS_GENERIC_TABLE *Table,
CLONG ByteSize
)
/*++
Routine Description:
Arguments:
Table -
ByteSize -
Return Value:
--*/
{
return (PVOID) LocalAlloc(LPTR, ByteSize);
} // UserListAlloc
/////////////////////////////////////////////////////////////////////////
VOID
UserListFree (
struct _LLS_GENERIC_TABLE *Table,
PVOID Buffer
)
/*++
Routine Description:
Arguments:
Table -
Buffer -
Return Value:
--*/
{
PUSER_RECORD UserRec;
if (Buffer == NULL)
return;
UserRec = (PUSER_RECORD) Buffer;
LocalFree(UserRec->UserID);
LocalFree(UserRec);
} // UserListFree
/////////////////////////////////////////////////////////////////////////
PUSER_RECORD
UserListFind(
LPTSTR UserName
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
PUSER_RECORD pUserRec;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserListFind\n"));
#endif
UserRec.UserID = (PVOID) UserName;
RtlEnterCriticalSection(&GenTableLock);
pUserRec = (PUSER_RECORD) LLSLookupElementGenericTable(&UserList, &UserRec);
RtlLeaveCriticalSection(&GenTableLock);
return pUserRec;
} // UserListFind
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
VOID
UserListAdd(
PMASTER_SERVICE_RECORD Service,
ULONG DataType,
ULONG DataLength,
PVOID Data,
ULONG AccessCount,
DWORD LastAccess,
DWORD FlagsParam
)
/*++
Routine Description:
Routine called by the Add cache routine to update the user and/or SID
lists with the new service information.
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
PUSER_RECORD pUserRec;
BOOLEAN Added;
PSVC_RECORD pService;
PSVC_RECORD SvcTable = NULL;
PLLS_GENERIC_TABLE pTable = NULL;
PRTL_RESOURCE pLock = NULL;
BOOL SIDSwitch = FALSE;
BOOL UserLock = FALSE;
PMAPPING_RECORD pMap = NULL;
ULONG ProductLicenses, ProductLicensesUsed, i;
BOOL SwitchToBackOffice = FALSE;
NTSTATUS status;
PSVC_RECORD pSvcTableTmp;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserListAdd\n"));
#endif
// only 2 bits are used
ASSERT( FlagsParam == ( FlagsParam & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO ) ) );
//
// Set up lock and table pointers based on if data is SID or username...
//
UserRec.UserID = Data;
if (DataType == DATA_TYPE_USERNAME) {
pTable = &UserList;
pLock = &UserListLock;
} else if (DataType == DATA_TYPE_SID) {
pTable = &SidList;
pLock = &SidListLock;
}
if (pTable == NULL)
return;
//
// The generic table package copies the record so the record is
// temporary, but since we store the string as a pointer the pointer is
// copied but the actual memory that is pointed to is kept around
// permenantly.
//
// We have already allocated memory for the Data
//
UserRec.UserID = Data;
UserRec.IDSize = DataLength;
UserRec.Flags = FlagsParam;
UserRec.LicensedProducts = 0;
UserRec.LastReplicated = 0;
UserRec.ServiceTableSize = 0;
UserRec.Services = NULL;
UserRec.Mapping = NULL;
UserRec.LicenseListSize = 0;
UserRec.LicenseList = NULL;
//
// Assume that the user is licensed - we will blast it if they aren't
// down below.
//
UserRec.Flags |= LLS_FLAG_LICENSED;
//
// Need to update list so get exclusive access. First get Add/Enum lock
// so we don't block reads if doing an enum.
//
RtlAcquireResourceExclusive(pLock, TRUE);
pUserRec = (PUSER_RECORD) LLSInsertElementGenericTable(pTable, (PVOID) &UserRec, sizeof(USER_RECORD), &Added);
if (pUserRec == NULL) {
ASSERT(FALSE);
LocalFree(UserRec.UserID);
RtlReleaseResource(pLock);
return;
}
pUserRec->Flags &= ~LLS_FLAG_DELETED;
// if auto suite is ever turned off, it's gone for good
if ( ! ( FlagsParam & LLS_FLAG_SUITE_AUTO ) )
{
// set suite use to be that specified in the function parameters
pUserRec->Flags &= ~LLS_FLAG_SUITE_AUTO;
pUserRec->Flags |= ( FlagsParam & LLS_FLAG_SUITE_USE );
}
//
// If for some reason the record is already there then we need to
// clean-up the name we allocated.
//
if (Added == FALSE) {
LocalFree(UserRec.UserID);
//
// If this is a SID then check the SID record to find the corresponding
// USER_RECORD (it better be there) and update that instead.. Note: We
// kludge this by storing the pointer to the user table in the
// LastReplicated field.
//
if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated != 0)) {
//
// Switch data as approp.
//
SIDSwitch = TRUE;
}
} else {
//
// Do this here so when we release to READ access another thread
// won't AV when trying to get access to it.
//
status = RtlInitializeCriticalSection(&pUserRec->ServiceTableLock);
if (!NT_SUCCESS(status))
{
// We're out of memory. Fail to add the user
return;
}
if (DataType == DATA_TYPE_USERNAME) {
pMap = MappingListUserFind(UserRec.UserID);
pUserRec->Mapping = pMap;
UserListNumEntries++;
} else
SidListNumEntries++;
}
//
// If this is a SID, and we haven't gotten an appropriate user-rec
// then try to de-reference this and get the appropriate user-rec.
//
if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated == 0)) {
TCHAR UserName[MAX_USERNAME_LENGTH + 1];
TCHAR DomainName[MAX_DOMAINNAME_LENGTH + 1];
TCHAR FullName[MAX_USERNAME_LENGTH + MAX_DOMAINNAME_LENGTH + 2];
SID_NAME_USE snu;
PUSER_RECORD pUserRec2;
DWORD unSize, dnSize;
unSize = sizeof(UserName);
dnSize = sizeof(DomainName);
if (LookupAccountSid(NULL, (PSID) Data, UserName, &unSize, DomainName, &dnSize, &snu)) {
//
// Okay, de-referenced the SID, so go get the user-rec, but pre-pend
// domain first...
//
lstrcpy(FullName, DomainName);
lstrcat(FullName, TEXT("\\"));
lstrcat(FullName, UserName);
UserRec.UserID = FullName;
UserRec.IDSize = (lstrlen(FullName) + 1) * sizeof(TCHAR);
//
// Get locks, we will try shared first.
//
RtlAcquireResourceExclusive(&UserListLock, TRUE);
UserLock = TRUE;
SIDSwitch = TRUE;
RtlEnterCriticalSection(&GenTableLock);
pUserRec2 = (PUSER_RECORD) LLSLookupElementGenericTable(&UserList, &UserRec);
RtlLeaveCriticalSection(&GenTableLock);
if (pUserRec2 != NULL) {
//
// Tarnation! we found it - so use it.
//
pUserRec->LastReplicated = (ULONG_PTR) pUserRec2;
} else {
//
// Dang it all... It ain't in the dern table, so we're gonna
// put it there. First need to alloc perm storage for UserID
//
UserRec.UserID = LocalAlloc(LPTR, UserRec.IDSize);
if (UserRec.UserID != NULL) {
lstrcpy((LPTSTR) UserRec.UserID, FullName);
//
// Need to update list so get exclusive access. First get
// Add/Enum lock so we don't block reads if doing an enum.
//
pUserRec2 = (PUSER_RECORD) LLSInsertElementGenericTable(&UserList, (PVOID) &UserRec, sizeof(USER_RECORD), &Added);
}
//
// If we couldn't insert it then seomthing is wrong, clean up
// and exit.
//
if (pUserRec2 == NULL) {
ASSERT(FALSE);
if (UserRec.UserID != NULL)
LocalFree(UserRec.UserID);
RtlReleaseResource(pLock);
RtlReleaseResource(&UserListLock);
return;
}
//
// Update SID USER_REC pointer (LastReplicated) and then finally
// free up the SID lock
//
pUserRec->LastReplicated = (ULONG_PTR) pUserRec2;
if (Added == TRUE) {
//
// Do this here so when we release to READ access another
// thread won't AV when trying to get access to it.
//
status = RtlInitializeCriticalSection(&pUserRec2->ServiceTableLock);
if (!NT_SUCCESS(status))
{
// We're out of memory. Fail to add the user
return;
}
pMap = MappingListUserFind(UserRec.UserID);
pUserRec2->Mapping = pMap;
UserListNumEntries++;
}
}
//
// We have found or added a USER_REC for the SID which pUserRec2
// points to. Now we need to switch locks and tables.
//
}
}
//
// If we need to munge from SID to User tables, then do so...
//
if (SIDSwitch) {
//
// Switch data as approp.
//
pUserRec = (PUSER_RECORD) pUserRec->LastReplicated;
DataType = DATA_TYPE_USERNAME;
//
// Release locks on SID Table
//
RtlReleaseResource(pLock);
//
// Now switch locks to User Table
//
pTable = &UserList;
pLock = &UserListLock;
if (!UserLock)
RtlAcquireResourceExclusive(pLock, TRUE);
}
//
// At this point we have either found the old record, or added a new
// one. In either case pUserRec points to the correct record.
//
if (pUserRec != NULL) {
//
// Check Service table to make sure our service is already there
//
RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
pService = SvcListFind( Service->Name, pUserRec->Services, pUserRec->ServiceTableSize );
if (pService != NULL) {
//
// Found entry in service table so just increment count
//
if (pService->AccessCount + AccessCount < MAX_ACCESS_COUNT)
pService->AccessCount += AccessCount;
else
pService->AccessCount = MAX_ACCESS_COUNT;
pService->LastAccess = LastAccess;
} else {
//
// Need to add more entries to service table (or create it...)
//
if (pUserRec->Services == NULL)
pSvcTableTmp = (PSVC_RECORD) LocalAlloc( LPTR, sizeof(SVC_RECORD));
else
pSvcTableTmp = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize + 1), LHND);
if (pSvcTableTmp != NULL) {
SvcTable = pSvcTableTmp;
} else {
// why is this a void function?
ASSERT(FALSE);
return;
}
pUserRec->Services = SvcTable;
if (SvcTable != NULL) {
DWORD Flags;
if (pUserRec->Mapping != NULL)
Flags = pUserRec->Mapping->Flags;
else
Flags = pUserRec->Flags;
SvcTable[pUserRec->ServiceTableSize].Service = Service;
SvcTable[pUserRec->ServiceTableSize].LastAccess = LastAccess;
//
// Update AccessCount field, but make sure we don't roll over
//
if (AccessCount < MAX_ACCESS_COUNT)
SvcTable[pUserRec->ServiceTableSize].AccessCount = AccessCount;
else
SvcTable[pUserRec->ServiceTableSize].AccessCount = MAX_ACCESS_COUNT;
SvcTable[pUserRec->ServiceTableSize].Flags = LLS_FLAG_LICENSED;
//
// Now update the actual license info
//
SvcLicenseUpdate(pUserRec, &SvcTable[pUserRec->ServiceTableSize]);
pUserRec->ServiceTableSize += 1;
if (SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED)
pUserRec->LicensedProducts++;
//
// If the added product isn't licensed then update user flag
//
if (IsMaster && !(SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED) )
pUserRec->Flags &= ~LLS_FLAG_LICENSED;
// Now that it is all setup - sort the table (so search will work)
qsort((void *) pUserRec->Services, (size_t) pUserRec->ServiceTableSize, sizeof(SVC_RECORD), SvcListCompare);
UserBackOfficeCheck ( pUserRec );
}
}
RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
}
RtlReleaseResource(pLock);
} // UserListAdd
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// The AddCache routines are a queue of User Identifiers (Username or
// SID's) and the service being accessed. Records are dequeued by a
// background thread and handed off to the UserListAdd function.
/////////////////////////////////////////////////////////////////////////
VOID
AddCacheManager (
IN PVOID ThreadParameter
)
/*++
Routine Description:
Arguments:
ThreadParameter - Not used.
Return Value:
This thread never exits.
--*/
{
NTSTATUS Status;
PADD_CACHE pAdd;
//
// Loop forever waiting to be given the opportunity to serve the
// greater good.
//
for ( ; ; ) {
//
// Wait to be notified that there is work to be done
//
Status = NtWaitForSingleObject( LLSAddCacheEvent, TRUE, NULL );
//
// Take an item from the add cache.
//
RtlEnterCriticalSection(&AddCacheLock);
while (AddCache != NULL) {
pAdd = AddCache;
AddCache = AddCache->prev;
AddCacheSize--;
RtlLeaveCriticalSection(&AddCacheLock);
if (pAdd != NULL) {
UserListAdd(pAdd->Service, pAdd->DataType, pAdd->DataLength, pAdd->Data, pAdd->AccessCount, pAdd->LastAccess, pAdd->Flags);
LocalFree(pAdd);
}
Sleep(0);
//
// Need to re-enter critical section to check in the while loop
RtlEnterCriticalSection(&AddCacheLock);
}
RtlLeaveCriticalSection(&AddCacheLock);
}
} // AddCacheManager
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
NTSTATUS
UserListInit()
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
HANDLE Thread;
DOMAIN_CONTROLLER_INFO * pDCInfo = NULL;
//
// Initialize the generic table.
//
LLSInitializeGenericTable ( &UserList,
UserListCompare,
UserListAlloc,
UserListFree,
(PVOID) TEXT("LLS Table") );
Status = RtlInitializeCriticalSection(&GenTableLock);
if (!NT_SUCCESS(Status))
return Status;
try
{
RtlInitializeResource(&UserListLock);
} except(EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status))
return Status;
//
// Initialize the SID table.
//
LLSInitializeGenericTable ( &SidList,
SidListCompare,
UserListAlloc,
UserListFree,
(PVOID) TEXT("LLS SID Table") );
try
{
RtlInitializeResource(&SidListLock);
} except(EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status))
return Status;
//
// Now our add cache
//
Status = RtlInitializeCriticalSection(&AddCacheLock);
if (!NT_SUCCESS(Status))
return Status;
//
// Get MyDomain
//
GetDCInfo(MAX_COMPUTERNAME_LENGTH + 2,
MyDomain,
&pDCInfo);
lstrcat(MyDomain, TEXT("\\"));
MyDomainSize = (lstrlen(MyDomain) + 1) * sizeof(TCHAR);
if (pDCInfo != NULL) {
NetApiBufferFree(pDCInfo);
}
//
// Create the Add Cache Management event
//
Status = NtCreateEvent(
&LLSAddCacheEvent,
EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
NULL,
SynchronizationEvent,
FALSE
);
if (!NT_SUCCESS(Status))
return Status;
//
// Create the Add Cache management thread
//
Thread = CreateThread(
NULL,
0L,
(LPTHREAD_START_ROUTINE) AddCacheManager,
0L,
0L,
NULL
);
if (NULL != Thread)
CloseHandle(Thread);
LastUsedTime = DateSystemGet();
return STATUS_SUCCESS;
} // UserListInit
/////////////////////////////////////////////////////////////////////////
VOID
UserListUpdate(
ULONG DataType,
PVOID Data,
PSERVICE_RECORD Service
)
/*++
Routine Description:
Actual start of the perseat license code. Given a SID or UserName find
the record in the appropriate table and check the given service. If the
service is already there then the info is updated, if it isn't there then
the record is put onto the add cache queue for background processing.
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
PUSER_RECORD pUserRec;
BOOLEAN Added;
ULONG DataLength;
PSVC_RECORD pService;
PSVC_RECORD SvcTable = NULL;
PLLS_GENERIC_TABLE pTable = NULL;
PRTL_RESOURCE pLock = NULL;
PADD_CACHE pAdd = NULL;
NTSTATUS NtStatus;
BOOL ToAddCache = FALSE;
BOOL FullName = TRUE;
LPTSTR pName;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: UserListUpdate\n"));
#endif
//
// Setup table and lock pointers based if Data is UserName or SID
//
UserRec.UserID = Data;
if (DataType == DATA_TYPE_USERNAME) {
pTable = &UserList;
pLock = &UserListLock;
DataLength = (lstrlen((LPWSTR) Data) + 1) * sizeof(TCHAR);
} else if (DataType == DATA_TYPE_SID) {
pTable = &SidList;
pLock = &SidListLock;
DataLength = RtlLengthSid((PSID) Data);
}
if (pTable == NULL)
return;
//
// Searching so don't need exclusive access
//
RtlAcquireResourceExclusive(pLock, TRUE);
RtlEnterCriticalSection(&GenTableLock);
pUserRec = (PUSER_RECORD) LLSLookupElementGenericTable(pTable, &UserRec);
RtlLeaveCriticalSection(&GenTableLock);
if (pUserRec == NULL)
ToAddCache = TRUE;
else {
//
// pUserRec now points to the record we must update.
//
// Check Service table to make sure our service is already there
//
pUserRec->Flags &= ~LLS_FLAG_DELETED;
RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
pService = SvcListFind( Service->DisplayName, pUserRec->Services, pUserRec->ServiceTableSize );
if (pService == NULL)
ToAddCache = TRUE;
else {
//
// Found entry in service table so just increment count
//
pService->AccessCount += 1;
pService->LastAccess = LastUsedTime;
}
RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
}
RtlReleaseResource(pLock);
if (ToAddCache) {
//
// Couldn't find the specific user/service, so put it on the Add Cache.
// First alloc memory for the name and Add Cache record.
//
pAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE));
if (pAdd == NULL) {
ASSERT(FALSE);
return;
}
if (DataType == DATA_TYPE_USERNAME) {
FullName = FALSE;
pName = (LPTSTR) Data;
//
// Make sure first char isn't backslash, if not then look for
// backslash as domain-name. If first char is backslash then get
// rid of it.
//
if (*pName != TEXT('\\'))
while ((*pName != TEXT('\0')) && !FullName) {
if (*pName == TEXT('\\'))
FullName = TRUE;
pName++;
}
else
((LPTSTR) Data)++;
}
//
// If we don't have a fully qualified Domain\Username, then tack the
// Domain name onto the name.
//
if (!FullName) {
UserRec.UserID = LocalAlloc( LPTR, DataLength + MyDomainSize);
if (UserRec.UserID == NULL) {
ASSERT(FALSE);
LocalFree(pAdd);
return;
}
pAdd->Data = UserRec.UserID;
lstrcpy((LPTSTR) pAdd->Data, MyDomain);
lstrcat((LPTSTR) pAdd->Data, (LPTSTR) Data);
pAdd->DataLength = DataLength + MyDomainSize;
} else {
UserRec.UserID = LocalAlloc( LPTR, DataLength);
if (UserRec.UserID == NULL) {
ASSERT(FALSE);
LocalFree(pAdd);
return;
}
pAdd->Data = UserRec.UserID;
memcpy(pAdd->Data, Data, DataLength);
pAdd->DataLength = DataLength;
}
//
// copy over all the data fields into the newly created Add Cache
// record.
//
pAdd->DataType = DataType;
pAdd->Service = Service->MasterService;
pAdd->AccessCount = 1;
pAdd->LastAccess = LastUsedTime;
pAdd->Flags = LLS_FLAG_SUITE_AUTO;
//
// Now update the actual Add Cache
//
RtlEnterCriticalSection(&AddCacheLock);
pAdd->prev = AddCache;
AddCache = pAdd;
AddCacheSize++;
RtlLeaveCriticalSection(&AddCacheLock);
//
// Now must signal the event so we can pull off the new record.
//
NtStatus = NtSetEvent( LLSAddCacheEvent, NULL );
ASSERT(NT_SUCCESS(NtStatus));
}
} // UserListUpdate
#if DBG
/////////////////////////////////////////////////////////////////////////
VOID
AddCacheDebugDump ( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
PADD_CACHE pAdd;
UNICODE_STRING UString;
ULONG i = 0;
RtlEnterCriticalSection(&AddCacheLock);
dprintf(TEXT("Add Cache Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(ADD_CACHE), AddCacheSize);
pAdd = AddCache;
while (pAdd != NULL) {
if (pAdd->DataType == DATA_TYPE_USERNAME)
dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"),
++i,
pAdd->Service,
pAdd->DataLength,
pAdd->Data);
else if (pAdd->DataType == DATA_TYPE_SID) {
Status = RtlConvertSidToUnicodeString(&UString, (PSID) pAdd->Data, TRUE);
dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"),
++i,
pAdd->Service,
pAdd->DataLength,
UString.Buffer);
RtlFreeUnicodeString(&UString);
}
pAdd = pAdd->prev;
}
RtlLeaveCriticalSection(&AddCacheLock);
} // AddCacheDebugDump
/////////////////////////////////////////////////////////////////////////
VOID
UserListDebugDump( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG i = 0;
PUSER_RECORD UserRec = NULL;
PVOID RestartKey = NULL;
RtlAcquireResourceShared(&UserListLock, TRUE);
dprintf(TEXT("User List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries);
UserRec = (PUSER_RECORD) LLSEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
while (UserRec != NULL) {
//
// Dump info for found user-rec
//
if (UserRec->Mapping != NULL)
dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"),
++i,
TimeToString((ULONG)(UserRec->LastReplicated)),
UserRec->LicenseListSize,
UserRec->ServiceTableSize,
UserRec->Flags,
UserRec->Mapping->Name,
UserRec->IDSize,
(LPTSTR) UserRec->UserID );
else
dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"),
++i,
TimeToString((ULONG)(UserRec->LastReplicated)),
UserRec->LicenseListSize,
UserRec->ServiceTableSize,
UserRec->Flags,
UserRec->IDSize,
(LPTSTR) UserRec->UserID );
// Get next record
UserRec = (PUSER_RECORD) LLSEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
}
RtlReleaseResource(&UserListLock);
} // UserListDebugDump
/////////////////////////////////////////////////////////////////////////
VOID
UserListDebugInfoDump(
PVOID Data
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
PUSER_RECORD pUserRec;
PSVC_RECORD SvcTable = NULL;
ULONG i;
RtlAcquireResourceExclusive(&UserListLock, TRUE);
dprintf(TEXT("User List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries);
//
// Only dump user if one was specified.
//
if (lstrlen((LPWSTR) Data) > 0) {
UserRec.UserID = Data;
RtlEnterCriticalSection(&GenTableLock);
pUserRec = (PUSER_RECORD) LLSLookupElementGenericTable(&UserList, &UserRec);
RtlLeaveCriticalSection(&GenTableLock);
if (pUserRec != NULL) {
//
// Dump info for found user-rec
//
if (pUserRec->Mapping != NULL)
dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"),
TimeToString((ULONG)(pUserRec->LastReplicated)),
pUserRec->LicenseListSize,
pUserRec->ServiceTableSize,
pUserRec->Flags,
pUserRec->Mapping->Name,
pUserRec->IDSize,
(LPTSTR) pUserRec->UserID );
else
dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"),
TimeToString((ULONG)(pUserRec->LastReplicated)),
pUserRec->LicenseListSize,
pUserRec->ServiceTableSize,
pUserRec->Flags,
pUserRec->IDSize,
(LPTSTR) pUserRec->UserID );
//
// Now do the service table - but get critical section first.
//
RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
SvcTable = pUserRec->Services;
if (pUserRec->ServiceTableSize != 0)
dprintf(TEXT("\nServiceTable\n"));
for (i = 0; i < pUserRec->ServiceTableSize; i++)
dprintf( TEXT(" AC: %4lu LA: %s Flags: 0x%4lX Svc: %s\n"),
SvcTable[i].AccessCount,
TimeToString(SvcTable[i].LastAccess),
SvcTable[i].Flags,
SvcTable[i].Service->Name );
if (pUserRec->LicenseListSize != 0)
dprintf(TEXT("\nLicenseTable\n"));
for (i = 0; i < pUserRec->LicenseListSize; i++)
dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"),
pUserRec->LicenseList[i]->Flags,
pUserRec->LicenseList[i]->RefCount,
pUserRec->LicenseList[i]->LicensesNeeded,
pUserRec->LicenseList[i]->Service->Name );
RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
} else
dprintf(TEXT("User Not Found: %s\n"), (LPWSTR) Data);
}
RtlReleaseResource(&UserListLock);
} // UserListDebugInfoDump
/////////////////////////////////////////////////////////////////////////
VOID
UserListDebugFlush( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
//
// Searching so don't need exclusive access
//
RtlAcquireResourceExclusive(&UserListLock, TRUE);
RtlEnterCriticalSection(&GenTableLock);
LLSLookupElementGenericTable(&UserList, &UserRec);
RtlLeaveCriticalSection(&GenTableLock);
RtlReleaseResource(&UserListLock);
} // UserListDebugFlush
/////////////////////////////////////////////////////////////////////////
VOID
SidListDebugDump( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG i = 0;
PUSER_RECORD UserRec = NULL;
UNICODE_STRING UString;
NTSTATUS NtStatus;
PVOID RestartKey = NULL;
RtlAcquireResourceShared(&SidListLock, TRUE);
dprintf(TEXT("SID List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries);
UserRec = (PUSER_RECORD) LLSEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey);
while (UserRec != NULL) {
//
// Dump info for found user-rec
//
NtStatus = RtlConvertSidToUnicodeString(&UString, (PSID) UserRec->UserID, TRUE);
dprintf(TEXT("%4lu) User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"),
++i,
UserRec->LastReplicated,
UserRec->ServiceTableSize,
UserRec->IDSize,
UString.Buffer );
RtlFreeUnicodeString(&UString);
// Get next record
UserRec = (PUSER_RECORD) LLSEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey);
}
RtlReleaseResource(&SidListLock);
} // SidListDebugDump
/////////////////////////////////////////////////////////////////////////
VOID
SidListDebugInfoDump(
PVOID Data
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
PUSER_RECORD pUserRec;
PSVC_RECORD SvcTable = NULL;
ULONG i;
RtlAcquireResourceExclusive(&SidListLock, TRUE);
dprintf(TEXT("SID List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries);
//
// Only dump user if one was specified.
//
if (lstrlen((LPWSTR) Data) > 0) {
UserRec.UserID = Data;
RtlEnterCriticalSection(&GenTableLock);
pUserRec = (PUSER_RECORD) LLSLookupElementGenericTable(&SidList, &UserRec);
RtlLeaveCriticalSection(&GenTableLock);
if (pUserRec != NULL) {
//
// Dump info for found user-rec
//
dprintf(TEXT(" User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"),
pUserRec->LastReplicated,
pUserRec->ServiceTableSize,
pUserRec->IDSize,
(LPTSTR) pUserRec->UserID );
// No Service Table for SID's
} else
dprintf(TEXT("SID Not Found: %s\n"), (LPWSTR) Data);
}
RtlReleaseResource(&SidListLock);
} // SidListDebugInfoDump
/////////////////////////////////////////////////////////////////////////
VOID
SidListDebugFlush( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
USER_RECORD UserRec;
//
// Searching so don't need exclusive access
//
RtlAcquireResourceExclusive(&SidListLock, TRUE);
RtlReleaseResource(&SidListLock);
} // SidListDebugFlush
#endif