|
|
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
svctbl.c
Abstract:
Service Table routines. Handles all access to service table for keeping track of running services and session counts to those services.
Author:
Arthur Hanson (arth) 07-Dec-1994
Revision History:
Jeff Parham (jeffparh) 05-Dec-1995 o Integrated per seat and per server purchase models for secure certificates. o Added logging of per server license rejections.
--*/
#include <stdlib.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <lm.h>
#include <dsgetdc.h>
#include "llsapi.h"
#include "debug.h"
#include "llssrv.h"
#include "registry.h"
#include "ntlsapi.h"
#include "mapping.h"
#include "msvctbl.h"
#include "svctbl.h"
#include "perseat.h"
#include "llsevent.h"
#include "llsutil.h"
#include "purchase.h"
#include <strsafe.h> //include last
//
// Must have ending space for version number placeholder!
//
#define FILE_PRINT "FilePrint "
#define FILE_PRINT_BASE "FilePrint"
#define FILE_PRINT_VERSION_NDX ( 9 )
#define REMOTE_ACCESS "REMOTE_ACCESS "
#define REMOTE_ACCESS_BASE "REMOTE_ACCESS"
#define THIRTY_MINUTES (30 * 60) // 30 minutes in seconds
#define TWELVE_HOURS (12 * 60 * 60) // 12 hours in seconds
#define MACHINE_ACCOUNT_NO_CAL_NEEDED 0xFFFFFFFE
extern ULONG NumFilePrintEntries; extern LPTSTR *FilePrintTable; extern DWORD PotentialAttackCounter;
ULONG ServiceListSize = 0; PSERVICE_RECORD *ServiceList = NULL; static PSERVICE_RECORD *ServiceFreeList = NULL; static DWORD gdwLastWarningTime = 0;
RTL_RESOURCE ServiceListLock;
DWORD AssessPerServerLicenseCapacity( ULONG cLicensesPurchased, ULONG cLicensesConsumed); int __cdecl MServiceRecordCompare( const void *arg1, const void *arg2); DWORD GetUserNameFromSID( PSID UserSID, DWORD ccFullUserName, TCHAR szFullUserName[]);
/////////////////////////////////////////////////////////////////////////
NTSTATUS ServiceListInit()
/*++
Routine Description:
Creates the service table, used for tracking the services and session count. This will pull the initial services from the registry.
The table is linear so a binary search can be used on the table, so some extra records are initialized so that each time we add a new service we don't have to do a realloc. We also assume that adding new services is a relatively rare occurance, since we need to sort it each time.
The service table is guarded by a read and write semaphore. Multiple reads can occur, but a write blocks everything.
The service table has two default entries for FilePrint and REMOTE_ACCESS.
Arguments:
None.
Return Value:
None.
--*/
{ BOOL PerSeatLicensing; ULONG SessionLimit; PSERVICE_RECORD Service; NTSTATUS status = STATUS_SUCCESS;
try { RtlInitializeResource(&ServiceListLock); } except(EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode(); }
if (!NT_SUCCESS(status)) return status;
//
// Just need to init FilePrint values...
//
Service = ServiceListAdd(TEXT(FILE_PRINT), FILE_PRINT_VERSION_NDX ); RegistryInitValues(TEXT(FILE_PRINT_BASE), &PerSeatLicensing, &SessionLimit);
//
// Need to init RAS separatly as it uses File/Print Licenses.
//
Service = ServiceListAdd(TEXT(REMOTE_ACCESS), lstrlen(TEXT(REMOTE_ACCESS)) - 1); if (Service != NULL) { Service->MaxSessionCount = SessionLimit; Service->PerSeatLicensing = PerSeatLicensing; }
return STATUS_SUCCESS;
} // ServiceListInit
/////////////////////////////////////////////////////////////////////////
int __cdecl ServiceListCompare(const void *arg1, const void *arg2) { PSERVICE_RECORD Svc1, Svc2;
Svc1 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg1); Svc2 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg2);
return lstrcmpi( Svc1->Name, Svc2->Name);
} // ServiceListCompare
PSERVICE_RECORD ServiceListFind( LPTSTR ServiceName )
/*++
Routine Description:
Internal routine to actually do binary search on ServiceList, this does not do any locking as we expect the wrapper routine to do this. The search is a simple binary search.
Arguments:
ServiceName -
Return Value:
Pointer to found service table entry or NULL if not found.
--*/
{ LONG begin = 0; LONG end = (LONG) ServiceListSize - 1; LONG cur; int match; PSERVICE_RECORD Service;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: ServiceListFind\n")); #endif
if ((ServiceName == NULL) || (ServiceListSize == 0)) return NULL;
while (end >= begin) { // go halfway in-between
cur = (begin + end) / 2; Service = ServiceList[cur];
// compare the two result into match
match = lstrcmpi(ServiceName, Service->Name);
if (match < 0) // move new begin
end = cur - 1; else begin = cur + 1;
if (match == 0) return Service; }
return NULL;
} // ServiceListFind
/////////////////////////////////////////////////////////////////////////
DWORD VersionToDWORD(LPTSTR Version)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ LPSTR pVer; DWORD Ver = 0; char tmpStr[12]; // two extra chars for null-termination just in case
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: VersionToDWORD\n")); #endif
if ((Version == NULL) || (*Version == TEXT('\0'))) return Ver;
//
// Do major version number
//
ZeroMemory(tmpStr, sizeof(tmpStr)); if (0 ==WideCharToMultiByte(CP_ACP, 0, Version, -1, tmpStr, 10, NULL, NULL)) { // Error
return 0; }
Ver = (ULONG) atoi(tmpStr); Ver *= 0x10000;
//
// Now minor - look for period
//
pVer = tmpStr; while ((*pVer != '\0') && (*pVer != '.')) pVer++;
if (*pVer == '.') { pVer++; Ver += atoi(pVer); }
return Ver;
} // VersionToDWORD
/////////////////////////////////////////////////////////////////////////
PSERVICE_RECORD ServiceListAdd( LPTSTR ServiceName, ULONG VersionIndex )
/*++
Routine Description:
Adds a service to the service table. This will also cause a poll of the registry to get the initial values for session limits and the type of licensing being used.
Arguments:
ServiceName -
Return Value:
Pointer to added service table entry, or NULL if failed.
--*/
{ ULONG i; ULONG SessionLimit = 0; BOOL PerSeatLicensing = FALSE; PSERVICE_RECORD NewService; LPTSTR NewServiceName, pDisplayName, pFamilyDisplayName; PSERVICE_RECORD CurrentRecord = NULL; PMASTER_SERVICE_RECORD mService; NTSTATUS status; PSERVICE_RECORD *pServiceListTmp, *pServiceFreeListTmp; HRESULT hr; size_t cch;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: ServiceListAdd\n")); #endif
//
// We do a double check here to see if another thread just got done
// adding the service, between when we checked last and actually got
// the write lock.
//
CurrentRecord = ServiceListFind(ServiceName); if (CurrentRecord != NULL) { return CurrentRecord; }
if (VersionIndex >= (ULONG)lstrlen(ServiceName)) { // Invalid VersionIndex
return NULL; }
//
// Allocate space for table (zero init it).
//
if (ServiceList == NULL) { pServiceListTmp = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) ); pServiceFreeListTmp = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) ); } else { pServiceListTmp = (PSERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND); pServiceFreeListTmp = (PSERVICE_RECORD *) LocalReAlloc(ServiceFreeList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND); }
//
// Make sure we could allocate service table
//
if ((pServiceListTmp == NULL) || (pServiceFreeListTmp == NULL)) { if (pServiceListTmp != NULL) LocalFree(pServiceListTmp);
if (pServiceFreeListTmp != NULL) LocalFree(pServiceFreeListTmp);
return NULL; } else { ServiceList = pServiceListTmp; ServiceFreeList = pServiceFreeListTmp; }
//
// Allocate space for saving off Service Name - we will take a space, then
// the version string onto the end of the Product Name. Therefore the
// product name will be something like "Microsoft SQL 4.2a". We maintain
// a pointer to the version, so that we can convert the space to a NULL
// and then get the product and version string separatly. Keeping them
// together simplifies the qsort and binary search routines.
//
NewService = (PSERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVICE_RECORD)); if (NewService == NULL) { ASSERT(FALSE); return NULL; }
ServiceList[ServiceListSize] = NewService; ServiceFreeList[ServiceListSize] = NewService;
cch = lstrlen(ServiceName) + 1; NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (NewServiceName == NULL) { ASSERT(FALSE); LocalFree(NewService); return NULL; }
// now copy it over...
NewService->Name = NewServiceName; hr = StringCchCopy(NewService->Name, cch, ServiceName); ASSERT(SUCCEEDED(hr));
//
// Allocate space for Root Name
//
NewService->Name[VersionIndex] = TEXT('\0'); cch = lstrlen(NewService->Name) + 1; NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (NewServiceName == NULL) { ASSERT(FALSE); LocalFree(NewService->Name); LocalFree(NewService); return NULL; }
hr = StringCchCopy(NewServiceName, cch, NewService->Name); ASSERT(SUCCEEDED(hr)); NewService->Name[VersionIndex] = TEXT(' ');
// point service structure to it...
NewService->FamilyName = NewServiceName;
//
// Allocate space for Display Name
//
RegistryDisplayNameGet(NewService->FamilyName, NewService->Name, &pDisplayName);
if (pDisplayName == NULL) { ASSERT(FALSE); LocalFree(NewService->Name); LocalFree(NewService->FamilyName); LocalFree(NewService); return NULL; }
// point service structure to it...
NewService->DisplayName = pDisplayName;
RegistryFamilyDisplayNameGet(NewService->FamilyName, NewService->DisplayName, &pFamilyDisplayName);
if (pFamilyDisplayName == NULL) { ASSERT(FALSE); LocalFree(NewService->Name); LocalFree(NewService->FamilyName); LocalFree(NewService->DisplayName); LocalFree(NewService); return NULL; }
// point service structure to it...
NewService->FamilyDisplayName = pFamilyDisplayName;
//
// Update table size and init entry, including reading init values
// from registry.
//
NewService->Version = VersionToDWORD(&ServiceName[VersionIndex + 1]);
// Init values from registry...
RegistryInitService(NewService->FamilyName, &PerSeatLicensing, &SessionLimit);
if ( PerSeatLicensing ) { // per seat mode
NewService->MaxSessionCount = 0; } else if ( ServiceIsSecure( NewService->DisplayName ) ) { // per server mode with a secure product; requires certificate
NewService->MaxSessionCount = ProductLicensesGet( NewService->DisplayName, TRUE ); } else { // per server mode with an unsecure product; use limit from registry
NewService->MaxSessionCount = SessionLimit; }
NewService->PerSeatLicensing = PerSeatLicensing; NewService->SessionCount = 0; NewService->Index = ServiceListSize; status = RtlInitializeCriticalSection(&NewService->ServiceLock); if (!NT_SUCCESS(status)) { LocalFree(NewService->Name); LocalFree(NewService->FamilyName); LocalFree(NewService->DisplayName); LocalFree(NewService); return NULL; }
if (lstrcmpi(ServiceName, TEXT(REMOTE_ACCESS))) { RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE); mService = MasterServiceListAdd( NewService->FamilyDisplayName, NewService->DisplayName, NewService->Version);
if (mService == NULL) { ASSERT(FALSE); } else { NewService->MasterService = mService;
//
// In case this got added from the local service list table and we
// didn't have a version # yet.
//
if (mService->Version == 0) { PMASTER_SERVICE_ROOT ServiceRoot = NULL;
//
// Fixup next pointer chain
//
ServiceRoot = mService->Family; i = 0; while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < NewService->Version)) i++;
mService->next = 0; mService->Version = NewService->Version; if (i > 0) { if (MasterServiceTable[ServiceRoot->Services[i - 1]]->next == mService->Index + 1) mService->next = 0; else mService->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
if (MasterServiceTable[ServiceRoot->Services[i - 1]] != mService) MasterServiceTable[ServiceRoot->Services[i - 1]]->next = mService->Index + 1; }
// Resort it in order of the versions
qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(ULONG), MServiceRecordCompare); } } RtlReleaseResource(&MasterServiceListLock); }
ServiceListSize++;
// Have added the entry - now need to sort it in order of the service names
qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVICE_RECORD), ServiceListCompare);
return NewService; } // ServiceListAdd
/////////////////////////////////////////////////////////////////////////
VOID ServiceListResynch( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ PSERVICE_RECORD Service; BOOL PerSeatLicensing; ULONG SessionLimit; ULONG i = 0; PSERVICE_RECORD FilePrintService;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: ServiceListReSynch\n")); #endif
if (ServiceList == NULL) return;
//
// Need to update list so get exclusive access.
//
RtlAcquireResourceExclusive(&ServiceListLock, TRUE);
for (i = 0; i < ServiceListSize; i++) { //
// Note: We will init REMOTE_ACCESS with bogus values here, but we
// reset it to the correct values below. Since we have exclusive access
// to the table, this is fine (and faster than always checking for
// REMOTE_ACCESS).
//
RegistryInitService((ServiceList[i])->FamilyName, &PerSeatLicensing, &SessionLimit);
if ( PerSeatLicensing ) { // per seat mode
(ServiceList[i])->MaxSessionCount = 0; } else if ( ServiceIsSecure( (ServiceList[i])->DisplayName ) ) { // per server mode with a secure product; requires certificate
(ServiceList[i])->MaxSessionCount = ProductLicensesGet( (ServiceList[i])->DisplayName, TRUE ); } else { // per server mode with an unsecure product; use limit from registry
(ServiceList[i])->MaxSessionCount = SessionLimit; }
(ServiceList[i])->PerSeatLicensing = PerSeatLicensing; }
//
// Need to init RAS separatly as it uses File/Print Licenses.
//
Service = ServiceListFind(TEXT(REMOTE_ACCESS)); FilePrintService = ServiceListFind(TEXT(FILE_PRINT));
ASSERT( NULL != Service ); ASSERT( NULL != FilePrintService );
if ( ( NULL != Service ) && ( NULL != FilePrintService ) ) { Service->MaxSessionCount = FilePrintService->MaxSessionCount; Service->PerSeatLicensing = FilePrintService->PerSeatLicensing; }
RtlReleaseResource(&ServiceListLock);
return; } // ServiceListResynch
/////////////////////////////////////////////////////////////////////////
NTSTATUS DispatchRequestLicense( ULONG DataType, PVOID Data, LPTSTR ServiceID, ULONG VersionIndex, BOOL IsAdmin, ULONG *Handle )
/*++
Routine Description:
Arguments:
ServiceID -
IsAdmin -
Handle -
Return Value:
--*/
{ #define FULL_USERNAME_LENGTH (MAX_DOMAINNAME_LENGTH + \
MAX_USERNAME_LENGTH + 3)
LPWSTR apszSubString[ 2 ]; NTSTATUS Status = STATUS_SUCCESS; PSERVICE_RECORD Service; ULONG SessionCount; ULONG TableEntry; LPTSTR pServiceID; BOOL NoLicense = FALSE; BOOL PerSeat;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: DispatchRequestLicense\n")); #endif
if (DataType == NT_LS_USER_NAME) { WCHAR *wszAccount = (WCHAR *) Data; int cchAccount = lstrlen(wszAccount);
// machine accounts must end in a dollar sign
if (L'$' == wszAccount[(cchAccount-1)]) { //
// Not all accounts ending in $ are a machine account
// but for the sake of efficiency we'll make that
// assumption
//
// Calling NetUserGetInfo is too heavyweight - causes
// overload on the DCs
//
//
// don't allocate a CAL
//
ASSERT(NULL != Handle); *Handle = MACHINE_ACCOUNT_NO_CAL_NEEDED; return STATUS_SUCCESS; } }
*Handle = 0xFFFFFFFF; pServiceID = ServiceID;
// we only need read access since we aren't adding at this point
RtlAcquireResourceShared( &ServiceListLock, TRUE );
// check if in FilePrint table, if so then we use FilePrint as the name
ServiceID[ VersionIndex ] = TEXT('\0'); if ( ServiceFindInTable( ServiceID, FilePrintTable, NumFilePrintEntries, &TableEntry ) ) { pServiceID = TEXT(FILE_PRINT); } ServiceID[ VersionIndex ] = TEXT(' ');
Service = ServiceListFind( pServiceID );
if (Service == NULL) { // couldn't find service in list, so add it
RtlConvertSharedToExclusive(&ServiceListLock); Service = ServiceListAdd( pServiceID, VersionIndex ); RtlConvertExclusiveToShared(&ServiceListLock); }
if (Service != NULL) { // service found or added successfully
*Handle = (ULONG) Service->Index;
RtlEnterCriticalSection(&Service->ServiceLock); SessionCount = Service->SessionCount + 1;
#if DBG
if (TraceFlags & TRACE_LICENSE_REQUEST) dprintf(TEXT("LLS: [0x%lX] %s License: %ld of %ld\n"), Service, Service->Name, SessionCount, Service->MaxSessionCount); #endif
if (SessionCount > Service->HighMark) { Service->HighMark = SessionCount; }
PerSeat = Service->PerSeatLicensing;
if ( !PerSeat ) { if ( !IsAdmin ) { TCHAR szFullUserName[ FULL_USERNAME_LENGTH ] = TEXT(""); DWORD dwCapacityState; DWORD dwError=0; //init to avoid W4 complaint, use uninit in LogEvent
DWORD dwInsertsCount=0; //init to avoid W4 complaint, use uninit in LogEvent
DWORD dwMessageID=0; //init to avoid W4 complaint, use uninit in LogEvent
dwCapacityState = AssessPerServerLicenseCapacity( Service->MaxSessionCount, SessionCount);
if ( dwCapacityState == LICENSE_CAPACITY_NORMAL ) { //
// Within normal capacity.
//
Service->SessionCount++; } else if ( dwCapacityState == LICENSE_CAPACITY_NEAR_MAXIMUM ) { //
// Within the threshold of near 100% capacity.
//
dwInsertsCount = 1; apszSubString[ 0 ] = Service->DisplayName; dwMessageID = LLS_EVENT_LOG_PER_SERVER_NEAR_MAX; dwError = ERROR_SUCCESS; Service->SessionCount++; } else if ( dwCapacityState == LICENSE_CAPACITY_AT_MAXIMUM ) { //
// Exceeding 100% capacity, but still within the grace range.
//
dwInsertsCount = 1; apszSubString[ 0 ] = Service->DisplayName; dwMessageID = LLS_EVENT_LOG_PER_SERVER_AT_MAX; dwError = ERROR_SUCCESS; Service->SessionCount++; } else { //
// License maximum exceeded. Zero tolerance for exceeding
// limits on concurrent licenses
//
if ( NT_LS_USER_NAME == DataType ) { apszSubString[ 0 ] = (LPWSTR) Data; dwError = ERROR_SUCCESS; } else { dwError = GetUserNameFromSID((PSID)Data, sizeof(szFullUserName)/sizeof(szFullUserName[0]), szFullUserName); apszSubString[ 0 ] = szFullUserName; }
dwInsertsCount = 2; apszSubString[ 1 ] = ServiceID; dwMessageID = LLS_EVENT_USER_NO_LICENSE; NoLicense = TRUE; }
if ( dwCapacityState != LICENSE_CAPACITY_NORMAL ) { if (!SBSPerServerHotfix || 0 != PotentialAttackCounter) {
//
// Log warning and put up warning dialog locally.
// Limit the log/ui warning to a low frequency. Specifically:
// Once per every 12 hours for warnings.
// Every 30 minutes when the license maximum is exceeded
// causing licenses to no longer be provided.
//
LARGE_INTEGER liTime; DWORD dwCurrentTime;
NtQuerySystemTime(&liTime); RtlTimeToSecondsSince1970(&liTime, &dwCurrentTime);
if ( dwCurrentTime - gdwLastWarningTime > (DWORD)(NoLicense ? THIRTY_MINUTES : TWELVE_HOURS) ) { LogEvent(dwMessageID, dwInsertsCount, apszSubString, dwError); LicenseCapacityWarningDlg(dwCapacityState); gdwLastWarningTime = dwCurrentTime; }
} else {
//
// SBS mods (bug# 505640). This code modified to log an event in the case of something going
// wrong under ALL conditions.
//
LogEvent(dwMessageID, dwInsertsCount, apszSubString, dwError); LicenseCapacityWarningDlg(dwCapacityState);
//
// end SBS mods
//
} } } else { Service->SessionCount++; } } else { Service->SessionCount++; }
RtlLeaveCriticalSection(&Service->ServiceLock); RtlReleaseResource(&ServiceListLock);
if ( PerSeat ) { // per node ("per seat") license
// translate REMOTE_ACCESS into FILE_PRINT before adding to
// per seat license records
if ( !lstrcmpi( ServiceID, TEXT( REMOTE_ACCESS ) ) ) { RtlAcquireResourceShared(&ServiceListLock, TRUE); Service = ServiceListFind(TEXT(FILE_PRINT)); RtlReleaseResource(&ServiceListLock);
ASSERT(Service != NULL); }
if (Service == NULL) { // shouldn't ever happen
*Handle = 0xFFFFFFFF; return LS_UNKNOWN_STATUS; }
UserListUpdate( DataType, Data, Service ); } else { // concurrent use ("per server") license
if (NoLicense) { Status = LS_INSUFFICIENT_UNITS; *Handle = 0xFFFFFFFF; } } } else { // could neither find nor create service entry
RtlReleaseResource(&ServiceListLock); #if DBG
dprintf( TEXT( "DispatchRequestLicense(): Could neither find nor create service entry.\n" ) ); #endif
}
return Status; } // DispatchRequestLicense
/////////////////////////////////////////////////////////////////////////
VOID DispatchFreeLicense( ULONG Handle )
/*++
Routine Description:
Arguments:
Handle -
Return Value:
None.
--*/
{ PSERVICE_RECORD Service;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE) dprintf(TEXT("LLS TRACE: DispatchFreeLicense\n")); #endif
if (MACHINE_ACCOUNT_NO_CAL_NEEDED == Handle) { return; }
//
// We only need read access since we aren't adding at this point.
//
RtlAcquireResourceShared(&ServiceListLock, TRUE);
#if DBG
if (TraceFlags & TRACE_LICENSE_FREE) dprintf(TEXT("Free Handle: 0x%lX\n"), Handle); #endif
if (Handle < ServiceListSize) { Service = ServiceFreeList[Handle]; RtlEnterCriticalSection(&Service->ServiceLock); if (Service->SessionCount > 0) Service->SessionCount--; RtlLeaveCriticalSection(&Service->ServiceLock); } else { #if DBG
dprintf(TEXT("Passed invalid Free Handle: 0x%lX\n"), Handle); #endif
}
RtlReleaseResource(&ServiceListLock);
} // DispatchFreeLicense
/////////////////////////////////////////////////////////////////////////
DWORD GetUserNameFromSID( PSID UserSID, DWORD ccFullUserName, TCHAR szFullUserName[] )
/*++
Routine Description:
Arguments:
UserSID - ccFullUserName - szFullUserName -
Return Value:
None.
--*/
{ TCHAR szUserName[ MAX_USERNAME_LENGTH + 1 ]; TCHAR szDomainName[ MAX_DOMAINNAME_LENGTH + 1 ]; DWORD Status = ERROR_SUCCESS; DWORD cbUserName; DWORD cbDomainName; SID_NAME_USE snu; HRESULT hr;
cbUserName = sizeof( szUserName ); cbDomainName = sizeof( szDomainName );
if ((UserSID == NULL) || (!IsValidSid(UserSID))) { return ERROR_INVALID_PARAMETER; }
if ( LookupAccountSid( NULL, UserSID, szUserName, &cbUserName, szDomainName, &cbDomainName, &snu ) ) { if ( ccFullUserName >= ( cbUserName + cbDomainName + sizeof(TEXT("\\")) ) / sizeof(TCHAR) ) {
hr = StringCchCopy( szFullUserName, ccFullUserName, szDomainName ); ASSERT(SUCCEEDED(hr)); hr = StringCchCat( szFullUserName, ccFullUserName, TEXT( "\\" ) ); ASSERT(SUCCEEDED(hr)); hr = StringCchCat( szFullUserName, ccFullUserName, szUserName ); ASSERT(SUCCEEDED(hr)); } else { Status = ERROR_INSUFFICIENT_BUFFER; } } else { Status = GetLastError(); }
return(Status); }
/////////////////////////////////////////////////////////////////////////
DWORD AssessPerServerLicenseCapacity( ULONG cLicensesPurchased, ULONG cLicensesConsumed )
/*++
Routine Description:
Determine the license capacity state given the number of licenses purchased and used according to the following table:
# of Purchased Warning Message Violation Message License not provided
Licenses Sent at % Sent at % at % -------------- --------------- ----------------- -------------------- 0-9 # of licenses - 1 # of licenses + 1 # of licenses + 2 10-500 90% <= x <= 100% 100% < x <= 110% x > 110% 501+ 95% <= x <= 100% 100% < x <= 105% x > 105%
Arguments:
cLicensesPurchased -- License purchase total cLicensesConsumed -- Number of licenses used
Return Value:
Returned state.
--*/
{ ULONG GracePercentage; ULONG Divisor;
if ( cLicensesPurchased == 0 ) { Divisor = 0; } else if ( cLicensesPurchased < 10 ) { Divisor = 1; } else if ( cLicensesPurchased <= 500 ) { Divisor = 10; } else { Divisor = 20; }
GracePercentage = Divisor > 1 ? cLicensesPurchased / Divisor : Divisor;
if ( cLicensesConsumed <= cLicensesPurchased ) { if ( cLicensesConsumed >= cLicensesPurchased - GracePercentage ) { return LICENSE_CAPACITY_NEAR_MAXIMUM; } else { return LICENSE_CAPACITY_NORMAL; } } else { if ( cLicensesConsumed > cLicensesPurchased + GracePercentage ) { return LICENSE_CAPACITY_EXCEEDED; } else { return LICENSE_CAPACITY_AT_MAXIMUM; } } } // AssessPerServerLicenseCapacity
#if DBG
/////////////////////////////////////////////////////////////////////////
VOID ServiceListDebugDump( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ ULONG i = 0;
//
// Need to scan list so get read access.
//
RtlAcquireResourceShared(&ServiceListLock, TRUE);
dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize); if (ServiceList == NULL) goto ServiceListDebugDumpExit;
for (i = 0; i < ServiceListSize; i++) { if ((ServiceList[i])->PerSeatLicensing) dprintf(TEXT("%3lu) PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name); else dprintf(TEXT("%3lu) PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name); }
ServiceListDebugDumpExit: RtlReleaseResource(&ServiceListLock);
return; } // ServiceListDebugDump
/////////////////////////////////////////////////////////////////////////
VOID ServiceListDebugInfoDump( PVOID Data )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ PSERVICE_RECORD CurrentRecord = NULL;
dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
if (lstrlen((LPWSTR) Data) > 0) { CurrentRecord = ServiceListFind((LPWSTR) Data); if (CurrentRecord != NULL) { if (CurrentRecord->PerSeatLicensing) dprintf(TEXT(" PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name); else dprintf(TEXT(" PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name); } }
} // ServiceListDebugInfoDump
#endif //DBG
|