windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/lls/server/svctbl.c

1130 lines
28 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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"
//
// 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
extern ULONG NumFilePrintEntries;
extern LPTSTR *FilePrintTable;
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
//
memset(tmpStr,0,12);
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;
#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;
}
//
// 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;
NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR));
if (NewServiceName == NULL) {
ASSERT(FALSE);
LocalFree(NewService);
return NULL;
}
// now copy it over...
NewService->Name = NewServiceName;
lstrcpy(NewService->Name, ServiceName);
//
// Allocate space for Root Name
//
NewService->Name[VersionIndex] = TEXT('\0');
NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(NewService->Name) + 1) * sizeof(TCHAR));
if (NewServiceName == NULL) {
ASSERT(FALSE);
LocalFree(NewService->Name);
LocalFree(NewService);
return NULL;
}
lstrcpy(NewServiceName, NewService->Name);
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
*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;
DWORD dwInsertsCount;
DWORD dwMessageID;
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,
FULL_USERNAME_LENGTH,
szFullUserName);
apszSubString[ 0 ] = szFullUserName;
}
dwInsertsCount = 2;
apszSubString[ 1 ] = ServiceID;
dwMessageID = LLS_EVENT_USER_NO_LICENSE;
NoLicense = TRUE;
}
if ( dwCapacityState != LICENSE_CAPACITY_NORMAL ) {
//
// 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 {
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
//
// 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;
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) ) {
lstrcpy( szFullUserName, szDomainName );
lstrcat( szFullUserName, TEXT( "\\" ) );
lstrcat( szFullUserName, szUserName );
}
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