Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1129 lines
28 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. svctbl.c
  5. Abstract:
  6. Service Table routines. Handles all access to service table
  7. for keeping track of running services and session counts to those
  8. services.
  9. Author:
  10. Arthur Hanson (arth) 07-Dec-1994
  11. Revision History:
  12. Jeff Parham (jeffparh) 05-Dec-1995
  13. o Integrated per seat and per server purchase models for secure
  14. certificates.
  15. o Added logging of per server license rejections.
  16. --*/
  17. #include <stdlib.h>
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <windows.h>
  22. #include <lm.h>
  23. #include <dsgetdc.h>
  24. #include "llsapi.h"
  25. #include "debug.h"
  26. #include "llssrv.h"
  27. #include "registry.h"
  28. #include "ntlsapi.h"
  29. #include "mapping.h"
  30. #include "msvctbl.h"
  31. #include "svctbl.h"
  32. #include "perseat.h"
  33. #include "llsevent.h"
  34. #include "llsutil.h"
  35. #include "purchase.h"
  36. //
  37. // Must have ending space for version number placeholder!
  38. //
  39. #define FILE_PRINT "FilePrint "
  40. #define FILE_PRINT_BASE "FilePrint"
  41. #define FILE_PRINT_VERSION_NDX ( 9 )
  42. #define REMOTE_ACCESS "REMOTE_ACCESS "
  43. #define REMOTE_ACCESS_BASE "REMOTE_ACCESS"
  44. #define THIRTY_MINUTES (30 * 60) // 30 minutes in seconds
  45. #define TWELVE_HOURS (12 * 60 * 60) // 12 hours in seconds
  46. extern ULONG NumFilePrintEntries;
  47. extern LPTSTR *FilePrintTable;
  48. ULONG ServiceListSize = 0;
  49. PSERVICE_RECORD *ServiceList = NULL;
  50. static PSERVICE_RECORD *ServiceFreeList = NULL;
  51. static DWORD gdwLastWarningTime = 0;
  52. RTL_RESOURCE ServiceListLock;
  53. DWORD AssessPerServerLicenseCapacity(
  54. ULONG cLicensesPurchased,
  55. ULONG cLicensesConsumed);
  56. int __cdecl MServiceRecordCompare(
  57. const void *arg1,
  58. const void *arg2);
  59. DWORD GetUserNameFromSID(
  60. PSID UserSID,
  61. DWORD ccFullUserName,
  62. TCHAR szFullUserName[]);
  63. /////////////////////////////////////////////////////////////////////////
  64. NTSTATUS
  65. ServiceListInit()
  66. /*++
  67. Routine Description:
  68. Creates the service table, used for tracking the services and session
  69. count. This will pull the initial services from the registry.
  70. The table is linear so a binary search can be used on the table, so
  71. some extra records are initialized so that each time we add a new
  72. service we don't have to do a realloc. We also assume that adding
  73. new services is a relatively rare occurance, since we need to sort
  74. it each time.
  75. The service table is guarded by a read and write semaphore. Multiple
  76. reads can occur, but a write blocks everything.
  77. The service table has two default entries for FilePrint and REMOTE_ACCESS.
  78. Arguments:
  79. None.
  80. Return Value:
  81. None.
  82. --*/
  83. {
  84. BOOL PerSeatLicensing;
  85. ULONG SessionLimit;
  86. PSERVICE_RECORD Service;
  87. NTSTATUS status = STATUS_SUCCESS;
  88. try
  89. {
  90. RtlInitializeResource(&ServiceListLock);
  91. } except(EXCEPTION_EXECUTE_HANDLER ) {
  92. status = GetExceptionCode();
  93. }
  94. if (!NT_SUCCESS(status))
  95. return status;
  96. //
  97. // Just need to init FilePrint values...
  98. //
  99. Service = ServiceListAdd(TEXT(FILE_PRINT), FILE_PRINT_VERSION_NDX );
  100. RegistryInitValues(TEXT(FILE_PRINT_BASE), &PerSeatLicensing, &SessionLimit);
  101. //
  102. // Need to init RAS separatly as it uses File/Print Licenses.
  103. //
  104. Service = ServiceListAdd(TEXT(REMOTE_ACCESS), lstrlen(TEXT(REMOTE_ACCESS)) - 1);
  105. if (Service != NULL) {
  106. Service->MaxSessionCount = SessionLimit;
  107. Service->PerSeatLicensing = PerSeatLicensing;
  108. }
  109. return STATUS_SUCCESS;
  110. } // ServiceListInit
  111. /////////////////////////////////////////////////////////////////////////
  112. int __cdecl ServiceListCompare(const void *arg1, const void *arg2) {
  113. PSERVICE_RECORD Svc1, Svc2;
  114. Svc1 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg1);
  115. Svc2 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg2);
  116. return lstrcmpi( Svc1->Name, Svc2->Name);
  117. } // ServiceListCompare
  118. PSERVICE_RECORD
  119. ServiceListFind(
  120. LPTSTR ServiceName
  121. )
  122. /*++
  123. Routine Description:
  124. Internal routine to actually do binary search on ServiceList, this
  125. does not do any locking as we expect the wrapper routine to do this.
  126. The search is a simple binary search.
  127. Arguments:
  128. ServiceName -
  129. Return Value:
  130. Pointer to found service table entry or NULL if not found.
  131. --*/
  132. {
  133. LONG begin = 0;
  134. LONG end = (LONG) ServiceListSize - 1;
  135. LONG cur;
  136. int match;
  137. PSERVICE_RECORD Service;
  138. #if DBG
  139. if (TraceFlags & TRACE_FUNCTION_TRACE)
  140. dprintf(TEXT("LLS TRACE: ServiceListFind\n"));
  141. #endif
  142. if ((ServiceName == NULL) || (ServiceListSize == 0))
  143. return NULL;
  144. while (end >= begin) {
  145. // go halfway in-between
  146. cur = (begin + end) / 2;
  147. Service = ServiceList[cur];
  148. // compare the two result into match
  149. match = lstrcmpi(ServiceName, Service->Name);
  150. if (match < 0)
  151. // move new begin
  152. end = cur - 1;
  153. else
  154. begin = cur + 1;
  155. if (match == 0)
  156. return Service;
  157. }
  158. return NULL;
  159. } // ServiceListFind
  160. /////////////////////////////////////////////////////////////////////////
  161. DWORD
  162. VersionToDWORD(LPTSTR Version)
  163. /*++
  164. Routine Description:
  165. Arguments:
  166. Return Value:
  167. --*/
  168. {
  169. LPSTR pVer;
  170. DWORD Ver = 0;
  171. char tmpStr[12]; // two extra chars for null-termination just in case
  172. #if DBG
  173. if (TraceFlags & TRACE_FUNCTION_TRACE)
  174. dprintf(TEXT("LLS TRACE: VersionToDWORD\n"));
  175. #endif
  176. if ((Version == NULL) || (*Version == TEXT('\0')))
  177. return Ver;
  178. //
  179. // Do major version number
  180. //
  181. memset(tmpStr,0,12);
  182. if (0 ==WideCharToMultiByte(CP_ACP, 0, Version, -1, tmpStr, 10, NULL, NULL))
  183. {
  184. // Error
  185. return 0;
  186. }
  187. Ver = (ULONG) atoi(tmpStr);
  188. Ver *= 0x10000;
  189. //
  190. // Now minor - look for period
  191. //
  192. pVer = tmpStr;
  193. while ((*pVer != '\0') && (*pVer != '.'))
  194. pVer++;
  195. if (*pVer == '.') {
  196. pVer++;
  197. Ver += atoi(pVer);
  198. }
  199. return Ver;
  200. } // VersionToDWORD
  201. /////////////////////////////////////////////////////////////////////////
  202. PSERVICE_RECORD
  203. ServiceListAdd(
  204. LPTSTR ServiceName,
  205. ULONG VersionIndex
  206. )
  207. /*++
  208. Routine Description:
  209. Adds a service to the service table. This will also cause a poll of
  210. the registry to get the initial values for session limits and the
  211. type of licensing being used.
  212. Arguments:
  213. ServiceName -
  214. Return Value:
  215. Pointer to added service table entry, or NULL if failed.
  216. --*/
  217. {
  218. ULONG i;
  219. ULONG SessionLimit = 0;
  220. BOOL PerSeatLicensing = FALSE;
  221. PSERVICE_RECORD NewService;
  222. LPTSTR NewServiceName, pDisplayName, pFamilyDisplayName;
  223. PSERVICE_RECORD CurrentRecord = NULL;
  224. PMASTER_SERVICE_RECORD mService;
  225. NTSTATUS status;
  226. PSERVICE_RECORD *pServiceListTmp, *pServiceFreeListTmp;
  227. #if DBG
  228. if (TraceFlags & TRACE_FUNCTION_TRACE)
  229. dprintf(TEXT("LLS TRACE: ServiceListAdd\n"));
  230. #endif
  231. //
  232. // We do a double check here to see if another thread just got done
  233. // adding the service, between when we checked last and actually got
  234. // the write lock.
  235. //
  236. CurrentRecord = ServiceListFind(ServiceName);
  237. if (CurrentRecord != NULL) {
  238. return CurrentRecord;
  239. }
  240. //
  241. // Allocate space for table (zero init it).
  242. //
  243. if (ServiceList == NULL) {
  244. pServiceListTmp = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
  245. pServiceFreeListTmp = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
  246. } else {
  247. pServiceListTmp = (PSERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
  248. pServiceFreeListTmp = (PSERVICE_RECORD *) LocalReAlloc(ServiceFreeList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
  249. }
  250. //
  251. // Make sure we could allocate service table
  252. //
  253. if ((pServiceListTmp == NULL) || (pServiceFreeListTmp == NULL)) {
  254. if (pServiceListTmp != NULL)
  255. LocalFree(pServiceListTmp);
  256. if (pServiceFreeListTmp != NULL)
  257. LocalFree(pServiceFreeListTmp);
  258. return NULL;
  259. } else {
  260. ServiceList = pServiceListTmp;
  261. ServiceFreeList = pServiceFreeListTmp;
  262. }
  263. //
  264. // Allocate space for saving off Service Name - we will take a space, then
  265. // the version string onto the end of the Product Name. Therefore the
  266. // product name will be something like "Microsoft SQL 4.2a". We maintain
  267. // a pointer to the version, so that we can convert the space to a NULL
  268. // and then get the product and version string separatly. Keeping them
  269. // together simplifies the qsort and binary search routines.
  270. //
  271. NewService = (PSERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVICE_RECORD));
  272. if (NewService == NULL) {
  273. ASSERT(FALSE);
  274. return NULL;
  275. }
  276. ServiceList[ServiceListSize] = NewService;
  277. ServiceFreeList[ServiceListSize] = NewService;
  278. NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR));
  279. if (NewServiceName == NULL) {
  280. ASSERT(FALSE);
  281. LocalFree(NewService);
  282. return NULL;
  283. }
  284. // now copy it over...
  285. NewService->Name = NewServiceName;
  286. lstrcpy(NewService->Name, ServiceName);
  287. //
  288. // Allocate space for Root Name
  289. //
  290. NewService->Name[VersionIndex] = TEXT('\0');
  291. NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(NewService->Name) + 1) * sizeof(TCHAR));
  292. if (NewServiceName == NULL) {
  293. ASSERT(FALSE);
  294. LocalFree(NewService->Name);
  295. LocalFree(NewService);
  296. return NULL;
  297. }
  298. lstrcpy(NewServiceName, NewService->Name);
  299. NewService->Name[VersionIndex] = TEXT(' ');
  300. // point service structure to it...
  301. NewService->FamilyName = NewServiceName;
  302. //
  303. // Allocate space for Display Name
  304. //
  305. RegistryDisplayNameGet(NewService->FamilyName, NewService->Name, &pDisplayName);
  306. if (pDisplayName == NULL) {
  307. ASSERT(FALSE);
  308. LocalFree(NewService->Name);
  309. LocalFree(NewService->FamilyName);
  310. LocalFree(NewService);
  311. return NULL;
  312. }
  313. // point service structure to it...
  314. NewService->DisplayName = pDisplayName;
  315. RegistryFamilyDisplayNameGet(NewService->FamilyName, NewService->DisplayName, &pFamilyDisplayName);
  316. if (pFamilyDisplayName == NULL) {
  317. ASSERT(FALSE);
  318. LocalFree(NewService->Name);
  319. LocalFree(NewService->FamilyName);
  320. LocalFree(NewService->DisplayName);
  321. LocalFree(NewService);
  322. return NULL;
  323. }
  324. // point service structure to it...
  325. NewService->FamilyDisplayName = pFamilyDisplayName;
  326. //
  327. // Update table size and init entry, including reading init values
  328. // from registry.
  329. //
  330. NewService->Version = VersionToDWORD(&ServiceName[VersionIndex + 1]);
  331. // Init values from registry...
  332. RegistryInitService(NewService->FamilyName, &PerSeatLicensing, &SessionLimit);
  333. if ( PerSeatLicensing )
  334. {
  335. // per seat mode
  336. NewService->MaxSessionCount = 0;
  337. }
  338. else if ( ServiceIsSecure( NewService->DisplayName ) )
  339. {
  340. // per server mode with a secure product; requires certificate
  341. NewService->MaxSessionCount = ProductLicensesGet( NewService->DisplayName, TRUE );
  342. }
  343. else
  344. {
  345. // per server mode with an unsecure product; use limit from registry
  346. NewService->MaxSessionCount = SessionLimit;
  347. }
  348. NewService->PerSeatLicensing = PerSeatLicensing;
  349. NewService->SessionCount = 0;
  350. NewService->Index = ServiceListSize;
  351. status = RtlInitializeCriticalSection(&NewService->ServiceLock);
  352. if (!NT_SUCCESS(status))
  353. {
  354. LocalFree(NewService->Name);
  355. LocalFree(NewService->FamilyName);
  356. LocalFree(NewService->DisplayName);
  357. LocalFree(NewService);
  358. return NULL;
  359. }
  360. if (lstrcmpi(ServiceName, TEXT(REMOTE_ACCESS))) {
  361. RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
  362. mService = MasterServiceListAdd( NewService->FamilyDisplayName, NewService->DisplayName, NewService->Version);
  363. if (mService == NULL) {
  364. ASSERT(FALSE);
  365. } else {
  366. NewService->MasterService = mService;
  367. //
  368. // In case this got added from the local service list table and we
  369. // didn't have a version # yet.
  370. //
  371. if (mService->Version == 0) {
  372. PMASTER_SERVICE_ROOT ServiceRoot = NULL;
  373. //
  374. // Fixup next pointer chain
  375. //
  376. ServiceRoot = mService->Family;
  377. i = 0;
  378. while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < NewService->Version))
  379. i++;
  380. mService->next = 0;
  381. mService->Version = NewService->Version;
  382. if (i > 0) {
  383. if (MasterServiceTable[ServiceRoot->Services[i - 1]]->next == mService->Index + 1)
  384. mService->next = 0;
  385. else
  386. mService->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
  387. if (MasterServiceTable[ServiceRoot->Services[i - 1]] != mService)
  388. MasterServiceTable[ServiceRoot->Services[i - 1]]->next = mService->Index + 1;
  389. }
  390. // Resort it in order of the versions
  391. qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(ULONG), MServiceRecordCompare);
  392. }
  393. }
  394. RtlReleaseResource(&MasterServiceListLock);
  395. }
  396. ServiceListSize++;
  397. // Have added the entry - now need to sort it in order of the service names
  398. qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVICE_RECORD), ServiceListCompare);
  399. return NewService;
  400. } // ServiceListAdd
  401. /////////////////////////////////////////////////////////////////////////
  402. VOID
  403. ServiceListResynch( )
  404. /*++
  405. Routine Description:
  406. Arguments:
  407. Return Value:
  408. --*/
  409. {
  410. PSERVICE_RECORD Service;
  411. BOOL PerSeatLicensing;
  412. ULONG SessionLimit;
  413. ULONG i = 0;
  414. PSERVICE_RECORD FilePrintService;
  415. #if DBG
  416. if (TraceFlags & TRACE_FUNCTION_TRACE)
  417. dprintf(TEXT("LLS TRACE: ServiceListReSynch\n"));
  418. #endif
  419. if (ServiceList == NULL)
  420. return;
  421. //
  422. // Need to update list so get exclusive access.
  423. //
  424. RtlAcquireResourceExclusive(&ServiceListLock, TRUE);
  425. for (i = 0; i < ServiceListSize; i++) {
  426. //
  427. // Note: We will init REMOTE_ACCESS with bogus values here, but we
  428. // reset it to the correct values below. Since we have exclusive access
  429. // to the table, this is fine (and faster than always checking for
  430. // REMOTE_ACCESS).
  431. //
  432. RegistryInitService((ServiceList[i])->FamilyName, &PerSeatLicensing, &SessionLimit);
  433. if ( PerSeatLicensing )
  434. {
  435. // per seat mode
  436. (ServiceList[i])->MaxSessionCount = 0;
  437. }
  438. else if ( ServiceIsSecure( (ServiceList[i])->DisplayName ) )
  439. {
  440. // per server mode with a secure product; requires certificate
  441. (ServiceList[i])->MaxSessionCount = ProductLicensesGet( (ServiceList[i])->DisplayName, TRUE );
  442. }
  443. else
  444. {
  445. // per server mode with an unsecure product; use limit from registry
  446. (ServiceList[i])->MaxSessionCount = SessionLimit;
  447. }
  448. (ServiceList[i])->PerSeatLicensing = PerSeatLicensing;
  449. }
  450. //
  451. // Need to init RAS separatly as it uses File/Print Licenses.
  452. //
  453. Service = ServiceListFind(TEXT(REMOTE_ACCESS));
  454. FilePrintService = ServiceListFind(TEXT(FILE_PRINT));
  455. ASSERT( NULL != Service );
  456. ASSERT( NULL != FilePrintService );
  457. if ( ( NULL != Service ) && ( NULL != FilePrintService ) )
  458. {
  459. Service->MaxSessionCount = FilePrintService->MaxSessionCount;
  460. Service->PerSeatLicensing = FilePrintService->PerSeatLicensing;
  461. }
  462. RtlReleaseResource(&ServiceListLock);
  463. return;
  464. } // ServiceListResynch
  465. /////////////////////////////////////////////////////////////////////////
  466. NTSTATUS
  467. DispatchRequestLicense(
  468. ULONG DataType,
  469. PVOID Data,
  470. LPTSTR ServiceID,
  471. ULONG VersionIndex,
  472. BOOL IsAdmin,
  473. ULONG *Handle
  474. )
  475. /*++
  476. Routine Description:
  477. Arguments:
  478. ServiceID -
  479. IsAdmin -
  480. Handle -
  481. Return Value:
  482. --*/
  483. {
  484. #define FULL_USERNAME_LENGTH (MAX_DOMAINNAME_LENGTH + \
  485. MAX_USERNAME_LENGTH + 3)
  486. LPWSTR apszSubString[ 2 ];
  487. NTSTATUS Status = STATUS_SUCCESS;
  488. PSERVICE_RECORD Service;
  489. ULONG SessionCount;
  490. ULONG TableEntry;
  491. LPTSTR pServiceID;
  492. BOOL NoLicense = FALSE;
  493. BOOL PerSeat;
  494. #if DBG
  495. if (TraceFlags & TRACE_FUNCTION_TRACE)
  496. dprintf(TEXT("LLS TRACE: DispatchRequestLicense\n"));
  497. #endif
  498. *Handle = 0xFFFFFFFF;
  499. pServiceID = ServiceID;
  500. // we only need read access since we aren't adding at this point
  501. RtlAcquireResourceShared( &ServiceListLock, TRUE );
  502. // check if in FilePrint table, if so then we use FilePrint as the name
  503. ServiceID[ VersionIndex ] = TEXT('\0');
  504. if ( ServiceFindInTable( ServiceID, FilePrintTable, NumFilePrintEntries, &TableEntry ) )
  505. {
  506. pServiceID = TEXT(FILE_PRINT);
  507. }
  508. ServiceID[ VersionIndex ] = TEXT(' ');
  509. Service = ServiceListFind( pServiceID );
  510. if (Service == NULL)
  511. {
  512. // couldn't find service in list, so add it
  513. RtlConvertSharedToExclusive(&ServiceListLock);
  514. Service = ServiceListAdd( pServiceID, VersionIndex );
  515. RtlConvertExclusiveToShared(&ServiceListLock);
  516. }
  517. if (Service != NULL)
  518. {
  519. // service found or added successfully
  520. *Handle = (ULONG) Service->Index;
  521. RtlEnterCriticalSection(&Service->ServiceLock);
  522. SessionCount = Service->SessionCount + 1;
  523. #if DBG
  524. if (TraceFlags & TRACE_LICENSE_REQUEST)
  525. dprintf(TEXT("LLS: [0x%lX] %s License: %ld of %ld\n"), Service, Service->Name, SessionCount, Service->MaxSessionCount);
  526. #endif
  527. if (SessionCount > Service->HighMark)
  528. {
  529. Service->HighMark = SessionCount;
  530. }
  531. PerSeat = Service->PerSeatLicensing;
  532. if ( !PerSeat ) {
  533. if ( !IsAdmin ) {
  534. TCHAR szFullUserName[ FULL_USERNAME_LENGTH ] = TEXT("");
  535. DWORD dwCapacityState;
  536. DWORD dwError;
  537. DWORD dwInsertsCount;
  538. DWORD dwMessageID;
  539. dwCapacityState = AssessPerServerLicenseCapacity(
  540. Service->MaxSessionCount,
  541. SessionCount);
  542. if ( dwCapacityState == LICENSE_CAPACITY_NORMAL ) {
  543. //
  544. // Within normal capacity.
  545. //
  546. Service->SessionCount++;
  547. }
  548. else if ( dwCapacityState == LICENSE_CAPACITY_NEAR_MAXIMUM ) {
  549. //
  550. // Within the threshold of near 100% capacity.
  551. //
  552. dwInsertsCount = 1;
  553. apszSubString[ 0 ] = Service->DisplayName;
  554. dwMessageID = LLS_EVENT_LOG_PER_SERVER_NEAR_MAX;
  555. dwError = ERROR_SUCCESS;
  556. Service->SessionCount++;
  557. }
  558. else if ( dwCapacityState == LICENSE_CAPACITY_AT_MAXIMUM ) {
  559. //
  560. // Exceeding 100% capacity, but still within the grace range.
  561. //
  562. dwInsertsCount = 1;
  563. apszSubString[ 0 ] = Service->DisplayName;
  564. dwMessageID = LLS_EVENT_LOG_PER_SERVER_AT_MAX;
  565. dwError = ERROR_SUCCESS;
  566. Service->SessionCount++;
  567. }
  568. else {
  569. //
  570. // License maximum exceeded. Zero tolerance for exceeding
  571. // limits on concurrent licenses
  572. //
  573. if ( NT_LS_USER_NAME == DataType )
  574. {
  575. apszSubString[ 0 ] = (LPWSTR) Data;
  576. dwError = ERROR_SUCCESS;
  577. }
  578. else
  579. {
  580. dwError = GetUserNameFromSID((PSID)Data,
  581. FULL_USERNAME_LENGTH,
  582. szFullUserName);
  583. apszSubString[ 0 ] = szFullUserName;
  584. }
  585. dwInsertsCount = 2;
  586. apszSubString[ 1 ] = ServiceID;
  587. dwMessageID = LLS_EVENT_USER_NO_LICENSE;
  588. NoLicense = TRUE;
  589. }
  590. if ( dwCapacityState != LICENSE_CAPACITY_NORMAL ) {
  591. //
  592. // Log warning and put up warning dialog locally.
  593. // Limit the log/ui warning to a low frequency. Specifically:
  594. // Once per every 12 hours for warnings.
  595. // Every 30 minutes when the license maximum is exceeded
  596. // causing licenses to no longer be provided.
  597. //
  598. LARGE_INTEGER liTime;
  599. DWORD dwCurrentTime;
  600. NtQuerySystemTime(&liTime);
  601. RtlTimeToSecondsSince1970(&liTime, &dwCurrentTime);
  602. if ( dwCurrentTime - gdwLastWarningTime >
  603. (DWORD)(NoLicense ? THIRTY_MINUTES : TWELVE_HOURS) ) {
  604. LogEvent(dwMessageID, dwInsertsCount, apszSubString,
  605. dwError);
  606. LicenseCapacityWarningDlg(dwCapacityState);
  607. gdwLastWarningTime = dwCurrentTime;
  608. }
  609. }
  610. }
  611. else {
  612. Service->SessionCount++;
  613. }
  614. }
  615. else {
  616. Service->SessionCount++;
  617. }
  618. RtlLeaveCriticalSection(&Service->ServiceLock);
  619. RtlReleaseResource(&ServiceListLock);
  620. if ( PerSeat )
  621. {
  622. // per node ("per seat") license
  623. // translate REMOTE_ACCESS into FILE_PRINT before adding to
  624. // per seat license records
  625. if ( !lstrcmpi( ServiceID, TEXT( REMOTE_ACCESS ) ) )
  626. {
  627. RtlAcquireResourceShared(&ServiceListLock, TRUE);
  628. Service = ServiceListFind(TEXT(FILE_PRINT));
  629. RtlReleaseResource(&ServiceListLock);
  630. ASSERT(Service != NULL);
  631. }
  632. if (Service == NULL)
  633. {
  634. // shouldn't ever happen
  635. *Handle = 0xFFFFFFFF;
  636. return LS_UNKNOWN_STATUS;
  637. }
  638. UserListUpdate( DataType, Data, Service );
  639. }
  640. else
  641. {
  642. // concurrent use ("per server") license
  643. if (NoLicense)
  644. {
  645. Status = LS_INSUFFICIENT_UNITS;
  646. *Handle = 0xFFFFFFFF;
  647. }
  648. }
  649. }
  650. else
  651. {
  652. // could neither find nor create service entry
  653. RtlReleaseResource(&ServiceListLock);
  654. #if DBG
  655. dprintf( TEXT( "DispatchRequestLicense(): Could neither find nor create service entry.\n" ) );
  656. #endif
  657. }
  658. return Status;
  659. } // DispatchRequestLicense
  660. /////////////////////////////////////////////////////////////////////////
  661. VOID
  662. DispatchFreeLicense(
  663. ULONG Handle
  664. )
  665. /*++
  666. Routine Description:
  667. Arguments:
  668. Handle -
  669. Return Value:
  670. None.
  671. --*/
  672. {
  673. PSERVICE_RECORD Service;
  674. #if DBG
  675. if (TraceFlags & TRACE_FUNCTION_TRACE)
  676. dprintf(TEXT("LLS TRACE: DispatchFreeLicense\n"));
  677. #endif
  678. //
  679. // We only need read access since we aren't adding at this point.
  680. //
  681. RtlAcquireResourceShared(&ServiceListLock, TRUE);
  682. #if DBG
  683. if (TraceFlags & TRACE_LICENSE_FREE)
  684. dprintf(TEXT("Free Handle: 0x%lX\n"), Handle);
  685. #endif
  686. if (Handle < ServiceListSize) {
  687. Service = ServiceFreeList[Handle];
  688. RtlEnterCriticalSection(&Service->ServiceLock);
  689. if (Service->SessionCount > 0)
  690. Service->SessionCount--;
  691. RtlLeaveCriticalSection(&Service->ServiceLock);
  692. } else {
  693. #if DBG
  694. dprintf(TEXT("Passed invalid Free Handle: 0x%lX\n"), Handle);
  695. #endif
  696. }
  697. RtlReleaseResource(&ServiceListLock);
  698. } // DispatchFreeLicense
  699. /////////////////////////////////////////////////////////////////////////
  700. DWORD
  701. GetUserNameFromSID(
  702. PSID UserSID,
  703. DWORD ccFullUserName,
  704. TCHAR szFullUserName[]
  705. )
  706. /*++
  707. Routine Description:
  708. Arguments:
  709. UserSID -
  710. ccFullUserName -
  711. szFullUserName -
  712. Return Value:
  713. None.
  714. --*/
  715. {
  716. TCHAR szUserName[ MAX_USERNAME_LENGTH + 1 ];
  717. TCHAR szDomainName[ MAX_DOMAINNAME_LENGTH + 1 ];
  718. DWORD Status = ERROR_SUCCESS;
  719. DWORD cbUserName;
  720. DWORD cbDomainName;
  721. SID_NAME_USE snu;
  722. cbUserName = sizeof( szUserName );
  723. cbDomainName = sizeof( szDomainName );
  724. if ((UserSID == NULL) || (!IsValidSid(UserSID))) {
  725. return ERROR_INVALID_PARAMETER;
  726. }
  727. if ( LookupAccountSid( NULL,
  728. UserSID,
  729. szUserName,
  730. &cbUserName,
  731. szDomainName,
  732. &cbDomainName,
  733. &snu ) )
  734. {
  735. if ( ccFullUserName >=
  736. ( cbUserName + cbDomainName + sizeof(TEXT("\\")) ) /
  737. sizeof(TCHAR) ) {
  738. lstrcpy( szFullUserName, szDomainName );
  739. lstrcat( szFullUserName, TEXT( "\\" ) );
  740. lstrcat( szFullUserName, szUserName );
  741. }
  742. else {
  743. Status = ERROR_INSUFFICIENT_BUFFER;
  744. }
  745. }
  746. else {
  747. Status = GetLastError();
  748. }
  749. return(Status);
  750. }
  751. /////////////////////////////////////////////////////////////////////////
  752. DWORD
  753. AssessPerServerLicenseCapacity(
  754. ULONG cLicensesPurchased,
  755. ULONG cLicensesConsumed
  756. )
  757. /*++
  758. Routine Description:
  759. Determine the license capacity state given the number of licenses
  760. purchased and used according to the following table:
  761. # of Purchased Warning Message Violation Message License not provided
  762. Licenses Sent at % Sent at % at %
  763. -------------- --------------- ----------------- --------------------
  764. 0-9 # of licenses - 1 # of licenses + 1 # of licenses + 2
  765. 10-500 90% <= x <= 100% 100% < x <= 110% x > 110%
  766. 501+ 95% <= x <= 100% 100% < x <= 105% x > 105%
  767. Arguments:
  768. cLicensesPurchased -- License purchase total
  769. cLicensesConsumed -- Number of licenses used
  770. Return Value:
  771. Returned state.
  772. --*/
  773. {
  774. ULONG GracePercentage;
  775. ULONG Divisor;
  776. if ( cLicensesPurchased == 0 ) {
  777. Divisor = 0;
  778. }
  779. else if ( cLicensesPurchased < 10 ) {
  780. Divisor = 1;
  781. }
  782. else if ( cLicensesPurchased <= 500 ) {
  783. Divisor = 10;
  784. }
  785. else {
  786. Divisor = 20;
  787. }
  788. GracePercentage = Divisor > 1 ? cLicensesPurchased / Divisor : Divisor;
  789. if ( cLicensesConsumed <= cLicensesPurchased ) {
  790. if ( cLicensesConsumed >= cLicensesPurchased - GracePercentage ) {
  791. return LICENSE_CAPACITY_NEAR_MAXIMUM;
  792. }
  793. else {
  794. return LICENSE_CAPACITY_NORMAL;
  795. }
  796. }
  797. else {
  798. if ( cLicensesConsumed > cLicensesPurchased + GracePercentage ) {
  799. return LICENSE_CAPACITY_EXCEEDED;
  800. }
  801. else {
  802. return LICENSE_CAPACITY_AT_MAXIMUM;
  803. }
  804. }
  805. } // AssessPerServerLicenseCapacity
  806. #if DBG
  807. /////////////////////////////////////////////////////////////////////////
  808. VOID
  809. ServiceListDebugDump( )
  810. /*++
  811. Routine Description:
  812. Arguments:
  813. Return Value:
  814. --*/
  815. {
  816. ULONG i = 0;
  817. //
  818. // Need to scan list so get read access.
  819. //
  820. RtlAcquireResourceShared(&ServiceListLock, TRUE);
  821. dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
  822. if (ServiceList == NULL)
  823. goto ServiceListDebugDumpExit;
  824. for (i = 0; i < ServiceListSize; i++) {
  825. if ((ServiceList[i])->PerSeatLicensing)
  826. dprintf(TEXT("%3lu) PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  827. i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
  828. else
  829. dprintf(TEXT("%3lu) PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  830. i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
  831. }
  832. ServiceListDebugDumpExit:
  833. RtlReleaseResource(&ServiceListLock);
  834. return;
  835. } // ServiceListDebugDump
  836. /////////////////////////////////////////////////////////////////////////
  837. VOID
  838. ServiceListDebugInfoDump( PVOID Data )
  839. /*++
  840. Routine Description:
  841. Arguments:
  842. Return Value:
  843. --*/
  844. {
  845. PSERVICE_RECORD CurrentRecord = NULL;
  846. dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
  847. if (lstrlen((LPWSTR) Data) > 0) {
  848. CurrentRecord = ServiceListFind((LPWSTR) Data);
  849. if (CurrentRecord != NULL) {
  850. if (CurrentRecord->PerSeatLicensing)
  851. dprintf(TEXT(" PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  852. CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
  853. else
  854. dprintf(TEXT(" PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
  855. CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
  856. }
  857. }
  858. } // ServiceListDebugInfoDump
  859. #endif