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.

1172 lines
29 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. LlsUtil.c
  5. Abstract:
  6. Author:
  7. Arthur Hanson (arth) Dec 07, 1994
  8. Environment:
  9. Revision History:
  10. Jeff Parham (jeffparh) 12-Jan-1996
  11. o Added WinNtBuildNumberGet() to ascertain the Windows NT build number
  12. running on a given machine.
  13. o Enhanced output of TimeToString().
  14. --*/
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <ntlsa.h>
  19. #include <windows.h>
  20. #include <stdlib.h>
  21. #include <crypt.h>
  22. #include <wchar.h>
  23. #include <dsgetdc.h>
  24. #include "debug.h"
  25. #include "llssrv.h"
  26. #include "llsevent.h"
  27. //
  28. // NB : Keep this define in sync with client\llsrpc.rc.
  29. //
  30. #define IDS_LICENSEWARNING 1501
  31. const char HeaderString[] = "License Logging System Data File\x01A";
  32. #define HEADER_SIZE 34
  33. typedef struct _LLS_FILE_HEADER {
  34. char Header[HEADER_SIZE];
  35. DWORD Version;
  36. DWORD DataSize;
  37. } LLS_FILE_HEADER, *PLLS_FILE_HEADER;
  38. extern HANDLE gLlsDllHandle;
  39. static HANDLE ghWarningDlgThreadHandle = NULL;
  40. VOID WarningDlgThread( PVOID ThreadParameter );
  41. /////////////////////////////////////////////////////////////////////////
  42. NTSTATUS
  43. EBlock(
  44. PVOID Data,
  45. ULONG DataSize
  46. )
  47. /*++
  48. Routine Description:
  49. Arguments:
  50. Return Value:
  51. --*/
  52. {
  53. NTSTATUS Status = STATUS_SUCCESS;
  54. DATA_KEY PublicKey;
  55. CRYPT_BUFFER CryptBuffer;
  56. //
  57. // Init our public key
  58. //
  59. PublicKey.Length = 4;
  60. PublicKey.MaximumLength = 4;
  61. PublicKey.Buffer = LocalAlloc(LPTR, 4);
  62. if (PublicKey.Buffer != NULL) {
  63. ((char *) (PublicKey.Buffer))[0] = '7';
  64. ((char *) (PublicKey.Buffer))[1] = '7';
  65. ((char *) (PublicKey.Buffer))[2] = '7';
  66. ((char *) (PublicKey.Buffer))[3] = '7';
  67. CryptBuffer.Length = DataSize;
  68. CryptBuffer.MaximumLength = DataSize;
  69. CryptBuffer.Buffer = (PVOID) Data;
  70. Status = RtlEncryptData2(&CryptBuffer, &PublicKey);
  71. LocalFree(PublicKey.Buffer);
  72. } else
  73. Status = STATUS_NO_MEMORY;
  74. return Status;
  75. } // EBlock
  76. /////////////////////////////////////////////////////////////////////////
  77. NTSTATUS
  78. DeBlock(
  79. PVOID Data,
  80. ULONG DataSize
  81. )
  82. /*++
  83. Routine Description:
  84. Arguments:
  85. Return Value:
  86. --*/
  87. {
  88. NTSTATUS Status = STATUS_SUCCESS;
  89. DATA_KEY PublicKey;
  90. CRYPT_BUFFER CryptBuffer;
  91. //
  92. // Init our public key
  93. //
  94. PublicKey.Length = 4;
  95. PublicKey.MaximumLength = 4;
  96. PublicKey.Buffer = LocalAlloc(LPTR, 4);
  97. if (PublicKey.Buffer != NULL) {
  98. ((char *) (PublicKey.Buffer))[0] = '7';
  99. ((char *) (PublicKey.Buffer))[1] = '7';
  100. ((char *) (PublicKey.Buffer))[2] = '7';
  101. ((char *) (PublicKey.Buffer))[3] = '7';
  102. CryptBuffer.Length = DataSize;
  103. CryptBuffer.MaximumLength = DataSize;
  104. CryptBuffer.Buffer = (PVOID) Data;
  105. Status = RtlDecryptData2(&CryptBuffer, &PublicKey);
  106. LocalFree(PublicKey.Buffer);
  107. } else
  108. Status = STATUS_NO_MEMORY;
  109. return Status;
  110. } // DeBlock
  111. /////////////////////////////////////////////////////////////////////////
  112. BOOL
  113. FileExists(
  114. LPTSTR FileName
  115. )
  116. /*++
  117. Routine Description:
  118. Arguments:
  119. Return Value:
  120. --*/
  121. {
  122. return (BOOL) RtlDoesFileExists_U(FileName);
  123. } // FileExists
  124. /////////////////////////////////////////////////////////////////////////
  125. VOID
  126. lsplitpath(
  127. const TCHAR *path,
  128. TCHAR *drive,
  129. TCHAR *dir,
  130. TCHAR *fname,
  131. TCHAR *ext
  132. )
  133. /*++
  134. Routine Description:
  135. Splits a path name into its individual components
  136. Took the _splitpath and _makepath routines and converted them to
  137. be NT (long file name) and Unicode friendly.
  138. Arguments:
  139. Entry:
  140. path - pointer to path name to be parsed
  141. drive - pointer to buffer for drive component, if any
  142. dir - pointer to buffer for subdirectory component, if any
  143. fname - pointer to buffer for file base name component, if any
  144. ext - pointer to buffer for file name extension component, if any
  145. Exit:
  146. drive - pointer to drive string. Includes ':' if a drive was given.
  147. dir - pointer to subdirectory string. Includes leading and
  148. trailing '/' or '\', if any.
  149. fname - pointer to file base name
  150. ext - pointer to file extension, if any. Includes leading '.'.
  151. Return Value:
  152. --*/
  153. {
  154. TCHAR *p;
  155. TCHAR *last_slash = NULL, *dot = NULL;
  156. SIZE_T len;
  157. // init these so we don't exit with bogus values
  158. drive[0] = TEXT('\0');
  159. dir[0] = TEXT('\0');
  160. fname[0] = TEXT('\0');
  161. ext[0] = TEXT('\0');
  162. if (path[0] == TEXT('\0'))
  163. return;
  164. /*+---------------------------------------------------------------------+
  165. | Assume that the path argument has the following form, where any or |
  166. | all of the components may be missing. |
  167. | |
  168. | <drive><dir><fname><ext> |
  169. | |
  170. | drive: |
  171. | 0 to MAX_DRIVE-1 characters, the last of which, if any, is a |
  172. | ':' or a '\' in the case of a UNC path. |
  173. | dir: |
  174. | 0 to _MAX_DIR-1 characters in the form of an absolute path |
  175. | (leading '/' or '\') or relative path, the last of which, if |
  176. | any, must be a '/' or '\'. E.g - |
  177. | |
  178. | absolute path: |
  179. | \top\next\last\ ; or |
  180. | /top/next/last/ |
  181. | relative path: |
  182. | top\next\last\ ; or |
  183. | top/next/last/ |
  184. | Mixed use of '/' and '\' within a path is also tolerated |
  185. | fname: |
  186. | 0 to _MAX_FNAME-1 characters not including the '.' character |
  187. | ext: |
  188. | 0 to _MAX_EXT-1 characters where, if any, the first must be a |
  189. | '.' |
  190. +---------------------------------------------------------------------+*/
  191. // extract drive letter and :, if any
  192. if ( path[0] && (path[1] == TEXT(':')) ) {
  193. if (drive) {
  194. drive[0] = path[0];
  195. drive[1] = path[1];
  196. drive[2] = TEXT('\0');
  197. }
  198. path += 2;
  199. }
  200. // if no drive then check for UNC pathname
  201. if (drive[0] == TEXT('\0'))
  202. if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) {
  203. // got a UNC path so put server-sharename into drive
  204. drive[0] = path[0];
  205. drive[1] = path[1];
  206. path += 2;
  207. p = &drive[2];
  208. while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
  209. *p++ = *path++;
  210. if (*path == TEXT('\0'))
  211. return;
  212. // now sitting at the share - copy this as well (copy slash first)
  213. *p++ = *path++;
  214. while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
  215. *p++ = *path++;
  216. // tack on terminating NULL
  217. *p = TEXT('\0');
  218. }
  219. /*+---------------------------------------------------------------------+
  220. | extract path string, if any. Path now points to the first character|
  221. | of the path, if any, or the filename or extension, if no path was |
  222. | specified. Scan ahead for the last occurence, if any, of a '/' or |
  223. | '\' path separator character. If none is found, there is no path. |
  224. | We will also note the last '.' character found, if any, to aid in |
  225. | handling the extension. |
  226. +---------------------------------------------------------------------+*/
  227. for (last_slash = NULL, p = (TCHAR *)path; *p; p++) {
  228. if (*p == TEXT('/') || *p == TEXT('\\'))
  229. // point to one beyond for later copy
  230. last_slash = p + 1;
  231. else if (*p == TEXT('.'))
  232. dot = p;
  233. }
  234. if (last_slash) {
  235. // found a path - copy up through last_slash or max. characters allowed,
  236. // whichever is smaller
  237. if (dir) {
  238. len = __min((last_slash - path), (_MAX_DIR - 1));
  239. lstrcpyn(dir, path, (int)len + 1);
  240. dir[len] = TEXT('\0');
  241. }
  242. path = last_slash;
  243. }
  244. /*+---------------------------------------------------------------------+
  245. | extract file name and extension, if any. Path now points to the |
  246. | first character of the file name, if any, or the extension if no |
  247. | file name was given. Dot points to the '.' beginning the extension,|
  248. | if any. |
  249. +---------------------------------------------------------------------+*/
  250. if (dot && (dot >= path)) {
  251. // found the marker for an extension - copy the file name up to the
  252. // '.'.
  253. if (fname) {
  254. len = __min((dot - path), (_MAX_FNAME - 1));
  255. lstrcpyn(fname, path, (int)len + 1);
  256. *(fname + len) = TEXT('\0');
  257. }
  258. // now we can get the extension - remember that p still points to the
  259. // terminating nul character of path.
  260. if (ext) {
  261. len = __min((p - dot), (_MAX_EXT - 1));
  262. lstrcpyn(ext, dot, (int)len + 1);
  263. ext[len] = TEXT('\0');
  264. }
  265. }
  266. else {
  267. // found no extension, give empty extension and copy rest of string
  268. // into fname.
  269. if (fname) {
  270. len = __min((p - path), (_MAX_FNAME - 1));
  271. lstrcpyn(fname, path, (int)len + 1);
  272. fname[len] = TEXT('\0');
  273. }
  274. if (ext) {
  275. *ext = TEXT('\0');
  276. }
  277. }
  278. } // lsplitpath
  279. /////////////////////////////////////////////////////////////////////////
  280. VOID
  281. lmakepath(
  282. TCHAR *path,
  283. const TCHAR *drive,
  284. const TCHAR *dir,
  285. const TCHAR *fname,
  286. const TCHAR *ext
  287. )
  288. /*++
  289. Routine Description:
  290. Create a path name from its individual components.
  291. Arguments:
  292. Entry:
  293. char *path - pointer to buffer for constructed path
  294. char *drive - pointer to drive component, may or may not contain
  295. trailing ':'
  296. char *dir - pointer to subdirectory component, may or may not include
  297. leading and/or trailing '/' or '\' characters
  298. char *fname - pointer to file base name component
  299. char *ext - pointer to extension component, may or may not contain
  300. a leading '.'.
  301. Exit:
  302. path - pointer to constructed path name
  303. Return Value:
  304. --*/
  305. {
  306. const TCHAR *p;
  307. /*+---------------------------------------------------------------------+
  308. | we assume that the arguments are in the following form (although we |
  309. | do not diagnose invalid arguments or illegal filenames (such as |
  310. | names longer than 8.3 or with illegal characters in them) |
  311. | |
  312. | drive: |
  313. | A or A: |
  314. | dir: |
  315. | \top\next\last\ ; or |
  316. | /top/next/last/ ; or |
  317. | |
  318. | either of the above forms with either/both the leading and |
  319. | trailing / or \ removed. Mixed use of '/' and '\' is also |
  320. | tolerated |
  321. | fname: |
  322. | any valid file name |
  323. | ext: |
  324. | any valid extension (none if empty or null ) |
  325. +---------------------------------------------------------------------+*/
  326. // copy drive
  327. if (drive && *drive)
  328. while (*drive)
  329. *path++ = *drive++;
  330. // copy dir
  331. if ((p = dir) && *p) {
  332. do {
  333. *path++ = *p++;
  334. }
  335. while (*p);
  336. if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) {
  337. *path++ = TEXT('\\');
  338. }
  339. }
  340. // copy fname
  341. if (p = fname) {
  342. while (*p) {
  343. *path++ = *p++;
  344. }
  345. }
  346. // copy ext, including 0-terminator - check to see if a '.' needs to be
  347. // inserted.
  348. if (p = ext) {
  349. if (*p && *p != TEXT('.')) {
  350. *path++ = TEXT('.');
  351. }
  352. while (*path++ = *p++)
  353. ;
  354. }
  355. else {
  356. // better add the 0-terminator
  357. *path = TEXT('\0');
  358. }
  359. } // lmakepath
  360. /////////////////////////////////////////////////////////////////////////
  361. VOID
  362. FileBackupCreate(
  363. LPTSTR Path
  364. )
  365. /*++
  366. Routine Description:
  367. Arguments:
  368. Return Value:
  369. --*/
  370. {
  371. HANDLE hFile = NULL;
  372. DWORD dwFileNumber = 0;
  373. TCHAR Drive[_MAX_DRIVE + 1];
  374. TCHAR Dir[_MAX_DIR + 1];
  375. TCHAR FileName[_MAX_FNAME + 1];
  376. TCHAR Ext[_MAX_EXT + 1];
  377. TCHAR NewExt[_MAX_EXT + 1];
  378. TCHAR NewPath[MAX_PATH + 1];
  379. //
  380. // Make sure file exists
  381. //
  382. if (!FileExists(FileName))
  383. return;
  384. //
  385. // Split name into constituent parts...
  386. //
  387. lsplitpath(Path, Drive, Dir, FileName, Ext);
  388. // Find next backup number...
  389. // Files are backed up as .xxx where xxx is a number in the form .001,
  390. // the first backup is stored as .001, second as .002, etc...
  391. do {
  392. //
  393. // Create new file name with backup extension
  394. //
  395. dwFileNumber++;
  396. wsprintf(NewExt, TEXT("%03u"), dwFileNumber);
  397. lmakepath(NewPath, Drive, Dir, FileName, NewExt);
  398. } while ( FileExists(NewPath) );
  399. MoveFile( Path, NewPath );
  400. } // FileBackupCreate
  401. /////////////////////////////////////////////////////////////////////////
  402. HANDLE
  403. LlsFileInit(
  404. LPTSTR FileName,
  405. DWORD Version,
  406. DWORD DataSize
  407. )
  408. /*++
  409. Routine Description:
  410. Arguments:
  411. Return Value:
  412. --*/
  413. {
  414. HANDLE hFile = NULL;
  415. LLS_FILE_HEADER Header;
  416. DWORD BytesWritten;
  417. #ifdef DEBUG
  418. if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
  419. dprintf(TEXT("LLS TRACE: LlsFileInit\n"));
  420. #endif
  421. if (FileName == NULL)
  422. return NULL;
  423. strcpy(Header.Header, HeaderString);
  424. Header.Version = Version;
  425. Header.DataSize = DataSize;
  426. SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
  427. hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  428. if (hFile != INVALID_HANDLE_VALUE) {
  429. if (!WriteFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesWritten, NULL)) {
  430. CloseHandle(hFile);
  431. hFile = NULL;
  432. }
  433. } else {
  434. return NULL;
  435. }
  436. return hFile;
  437. } // LlsFileInit
  438. /////////////////////////////////////////////////////////////////////////
  439. HANDLE
  440. LlsFileCheck(
  441. LPTSTR FileName,
  442. LPDWORD Version,
  443. LPDWORD DataSize
  444. )
  445. /*++
  446. Routine Description:
  447. Arguments:
  448. Return Value:
  449. --*/
  450. {
  451. BOOL FileOK = FALSE;
  452. HANDLE hFile = NULL;
  453. LLS_FILE_HEADER Header;
  454. DWORD FileSize;
  455. DWORD BytesRead;
  456. #ifdef DEBUG
  457. if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
  458. dprintf(TEXT("LLS TRACE: LlsFileCheck\n"));
  459. #endif
  460. if (FileName == NULL)
  461. return NULL;
  462. //
  463. // We are assuming the file exists
  464. //
  465. SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
  466. hFile = CreateFile(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  467. if (hFile != INVALID_HANDLE_VALUE) {
  468. FileSize = GetFileSize(hFile, NULL);
  469. //
  470. // Make sure there is enough data there to read
  471. //
  472. if (FileSize > (sizeof(LLS_FILE_HEADER) + 1)) {
  473. if (ReadFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesRead, NULL)) {
  474. if ( !_strcmpi(Header.Header, HeaderString) ) {
  475. //
  476. // Data checks out - so return datalength
  477. //
  478. *Version = Header.Version;
  479. *DataSize = Header.DataSize;
  480. FileOK = TRUE;
  481. }
  482. }
  483. }
  484. //
  485. // If we opened the file and something was wrong - close it.
  486. //
  487. if (!FileOK) {
  488. CloseHandle(hFile);
  489. hFile = NULL;
  490. }
  491. } else {
  492. return NULL;
  493. }
  494. return hFile;
  495. } // LlsFileCheck
  496. /////////////////////////////////////////////////////////////////////////
  497. DWORD
  498. DateSystemGet(
  499. )
  500. /*++
  501. Routine Description:
  502. Arguments:
  503. Return Value:
  504. Seconds since midnight.
  505. --*/
  506. {
  507. DWORD Seconds = 0;
  508. LARGE_INTEGER SysTime;
  509. NtQuerySystemTime(&SysTime);
  510. RtlTimeToSecondsSince1980(&SysTime, &Seconds);
  511. return Seconds;
  512. } // DateSystemGet
  513. /////////////////////////////////////////////////////////////////////////
  514. DWORD
  515. DateLocalGet(
  516. )
  517. /*++
  518. Routine Description:
  519. Arguments:
  520. Return Value:
  521. Seconds since midnight.
  522. --*/
  523. {
  524. DWORD Seconds = 0;
  525. LARGE_INTEGER SysTime, LocalTime;
  526. NtQuerySystemTime(&SysTime);
  527. RtlSystemTimeToLocalTime(&SysTime, &LocalTime);
  528. RtlTimeToSecondsSince1980(&LocalTime, &Seconds);
  529. return Seconds;
  530. } // DateLocalGet
  531. /////////////////////////////////////////////////////////////////////////
  532. DWORD
  533. InAWorkgroup(
  534. VOID
  535. )
  536. /*++
  537. Routine Description:
  538. This function determines whether we are a member of a domain, or of
  539. a workgroup. First it checks to make sure we're running on a Windows NT
  540. system (otherwise we're obviously in a domain) and if so, queries LSA
  541. to get the Primary domain SID, if this is NULL, we're in a workgroup.
  542. If we fail for some random unexpected reason, we'll pretend we're in a
  543. workgroup (it's more restrictive).
  544. Arguments:
  545. None
  546. Return Value:
  547. TRUE - We're in a workgroup
  548. FALSE - We're in a domain
  549. --*/
  550. {
  551. NT_PRODUCT_TYPE ProductType;
  552. OBJECT_ATTRIBUTES ObjectAttributes;
  553. LSA_HANDLE Handle;
  554. NTSTATUS Status;
  555. PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
  556. DWORD Result = FALSE;
  557. Status = RtlGetNtProductType(&ProductType);
  558. if (!NT_SUCCESS(Status)) {
  559. #if DBG
  560. dprintf(TEXT("ERROR LLS Could not get Product type\n"));
  561. #endif
  562. return TRUE;
  563. }
  564. if (ProductType == NtProductLanManNt) {
  565. return(FALSE);
  566. }
  567. InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
  568. Status = LsaOpenPolicy(NULL,
  569. &ObjectAttributes,
  570. POLICY_VIEW_LOCAL_INFORMATION,
  571. &Handle);
  572. if (!NT_SUCCESS(Status)) {
  573. #if DBG
  574. dprintf(TEXT("ERROR LLS: Could not open LSA Policy Database\n"));
  575. #endif
  576. return TRUE;
  577. }
  578. Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
  579. (PVOID *) &PolicyPrimaryDomainInfo);
  580. if (NT_SUCCESS(Status)) {
  581. if (PolicyPrimaryDomainInfo->Sid == NULL) {
  582. Result = TRUE;
  583. }
  584. else {
  585. Result = FALSE;
  586. }
  587. }
  588. if (PolicyPrimaryDomainInfo) {
  589. LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
  590. }
  591. LsaClose(Handle);
  592. return(Result);
  593. } // InAWorkgroup
  594. /////////////////////////////////////////////////////////////////////////
  595. VOID
  596. LogEvent(
  597. DWORD MessageId,
  598. DWORD NumberOfSubStrings,
  599. LPWSTR *SubStrings,
  600. DWORD ErrorCode
  601. )
  602. {
  603. HANDLE LogHandle;
  604. WORD wEventType;
  605. LogHandle = RegisterEventSourceW (
  606. NULL,
  607. TEXT("LicenseService")
  608. );
  609. if (LogHandle == NULL) {
  610. #if DBG
  611. dprintf(TEXT("LLS RegisterEventSourceW failed %lu\n"), GetLastError());
  612. #endif
  613. return;
  614. }
  615. switch ( MessageId >> 30 )
  616. {
  617. case STATUS_SEVERITY_INFORMATIONAL:
  618. case STATUS_SEVERITY_SUCCESS:
  619. wEventType = EVENTLOG_INFORMATION_TYPE;
  620. break;
  621. case STATUS_SEVERITY_WARNING:
  622. wEventType = EVENTLOG_WARNING_TYPE;
  623. break;
  624. case STATUS_SEVERITY_ERROR:
  625. default:
  626. wEventType = EVENTLOG_ERROR_TYPE;
  627. break;
  628. }
  629. if (ErrorCode == ERROR_SUCCESS) {
  630. //
  631. // No error codes were specified
  632. //
  633. (void) ReportEventW(
  634. LogHandle,
  635. wEventType,
  636. 0, // event category
  637. MessageId,
  638. NULL,
  639. (WORD)NumberOfSubStrings,
  640. 0,
  641. SubStrings,
  642. (PVOID) NULL
  643. );
  644. }
  645. else {
  646. //
  647. // Log the error code specified
  648. //
  649. (void) ReportEventW(
  650. LogHandle,
  651. wEventType,
  652. 0, // event category
  653. MessageId,
  654. NULL,
  655. (WORD)NumberOfSubStrings,
  656. sizeof(DWORD),
  657. SubStrings,
  658. (PVOID) &ErrorCode
  659. );
  660. }
  661. DeregisterEventSource(LogHandle);
  662. } // LogEvent
  663. #define THROTTLE_WRAPAROUND 24
  664. //
  665. // Reduce the frequency of logging
  666. // No need for the limit to be exact
  667. //
  668. /////////////////////////////////////////////////////////////////////////
  669. VOID
  670. ThrottleLogEvent(
  671. DWORD MessageId,
  672. DWORD NumberOfSubStrings,
  673. LPWSTR *SubStrings,
  674. DWORD ErrorCode
  675. )
  676. {
  677. static LONG lLogged = THROTTLE_WRAPAROUND;
  678. LONG lResult;
  679. lResult = InterlockedIncrement(&lLogged);
  680. if (THROTTLE_WRAPAROUND <= lResult)
  681. {
  682. LogEvent(
  683. MessageId,
  684. NumberOfSubStrings,
  685. SubStrings,
  686. ErrorCode );
  687. lLogged = 0;
  688. }
  689. }
  690. /////////////////////////////////////////////////////////////////////////
  691. VOID
  692. LicenseCapacityWarningDlg(DWORD dwCapacityState)
  693. {
  694. //
  695. // NB : The ServiceLock critical section is entered for the duration
  696. // of this routine. No serialization issues.
  697. //
  698. if (ghWarningDlgThreadHandle == NULL) {
  699. //
  700. // No action necessary if this fails.
  701. //
  702. DWORD Ignore;
  703. DWORD * pWarningMessageID;
  704. pWarningMessageID = LocalAlloc(LPTR, sizeof(DWORD));
  705. if ( pWarningMessageID != NULL ) {
  706. switch( dwCapacityState ) {
  707. case LICENSE_CAPACITY_NEAR_MAXIMUM:
  708. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_NEAR_MAX;
  709. break;
  710. case LICENSE_CAPACITY_AT_MAXIMUM:
  711. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_AT_MAX;
  712. break;
  713. case LICENSE_CAPACITY_EXCEEDED:
  714. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_EXCEEDED;
  715. break;
  716. default:
  717. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_EXCEEDED;
  718. };
  719. ghWarningDlgThreadHandle = CreateThread(NULL,
  720. 0L,
  721. (LPTHREAD_START_ROUTINE)
  722. WarningDlgThread,
  723. pWarningMessageID,
  724. 0L,
  725. &Ignore);
  726. if (ghWarningDlgThreadHandle == NULL)
  727. {
  728. //
  729. // CreateThread failed
  730. //
  731. LocalFree(pWarningMessageID);
  732. }
  733. }
  734. }
  735. }
  736. /////////////////////////////////////////////////////////////////////////
  737. VOID
  738. WarningDlgThread(
  739. PVOID ThreadParameter)
  740. {
  741. LPTSTR pszWarningMessage = NULL;
  742. DWORD * pWarningMessageID;
  743. HANDLE hThread;
  744. TCHAR szWarningTitle[256] = TEXT("");
  745. ASSERT(ThreadParameter != NULL);
  746. pWarningMessageID = (DWORD *)ThreadParameter;
  747. //
  748. // NB : The .dll should already have been loaded in MasterServiceListInit
  749. // on service startup. This logic exists here for the case where
  750. // the code invoked on initialization should fail.
  751. //
  752. // It is OK if another thread should simulataneously initialize
  753. // gLlsDllHandle. Worst case, there will be an orphaned handle
  754. // to the .dll. But the .dll is loaded for the lifetime of this
  755. // .exe, so no big deal.
  756. //
  757. if ( gLlsDllHandle == NULL ) {
  758. gLlsDllHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
  759. }
  760. if ( gLlsDllHandle != NULL) {
  761. DWORD ccWarningMessage;
  762. //
  763. // Fetch the dialog title.
  764. //
  765. LoadString(gLlsDllHandle,
  766. IDS_LICENSEWARNING,
  767. szWarningTitle,
  768. sizeof(szWarningTitle)/sizeof(TCHAR));
  769. //
  770. // Fetch the dialog message.
  771. //
  772. ccWarningMessage = FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
  773. FORMAT_MESSAGE_FROM_HMODULE |
  774. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  775. gLlsDllHandle,
  776. *pWarningMessageID,
  777. 0,
  778. (LPTSTR)&pszWarningMessage,
  779. 0,
  780. NULL);
  781. if ( ccWarningMessage > 2 ) {
  782. //
  783. // Strip the trailing <CR><LF> format message always adds.
  784. //
  785. pszWarningMessage[ccWarningMessage - 2] = TEXT('\0');
  786. MessageBox(NULL,
  787. pszWarningMessage,
  788. szWarningTitle,
  789. MB_ICONWARNING | MB_OK | MB_SYSTEMMODAL |
  790. MB_SERVICE_NOTIFICATION);
  791. }
  792. if ( pszWarningMessage != NULL) {
  793. LocalFree(pszWarningMessage);
  794. }
  795. }
  796. LocalFree(pWarningMessageID);
  797. //
  798. // By closing the handle, we allow the system to remove all remaining
  799. // traces of this thread.
  800. //
  801. hThread = ghWarningDlgThreadHandle;
  802. ghWarningDlgThreadHandle = NULL;
  803. CloseHandle(hThread);
  804. }
  805. /////////////////////////////////////////////////////////////////////////
  806. DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber )
  807. /*++
  808. Routine Description:
  809. Retrieve the build number of Windows NT running on a given machine.
  810. Arguments:
  811. pszServerName (LPTSTR)
  812. Name of the server to check.
  813. pdwBuildNumber (LPDWORD)
  814. On return, holds the build number of the server (e.g., 1057 for the
  815. release version of Windows NT 3.51).
  816. Return Value:
  817. ERROR_SUCCESS or Win error code.
  818. --*/
  819. {
  820. LONG lError;
  821. HKEY hKeyLocalMachine;
  822. lError = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
  823. if ( ERROR_SUCCESS != lError )
  824. {
  825. #if DBG
  826. dprintf( TEXT("WinNtBuildNumberGet(): Could not connect to remote registry, error %ld.\n"), lError );
  827. #endif
  828. }
  829. else
  830. {
  831. HKEY hKeyCurrentVersion;
  832. lError = RegOpenKeyEx( hKeyLocalMachine,
  833. TEXT( "Software\\Microsoft\\Windows NT\\CurrentVersion" ),
  834. 0,
  835. KEY_READ,
  836. &hKeyCurrentVersion );
  837. if ( ERROR_SUCCESS != lError )
  838. {
  839. #if DBG
  840. dprintf( TEXT("WinNtBuildNumberGet(): Could not open key, error %ld.\n"), lError );
  841. #endif
  842. }
  843. else
  844. {
  845. DWORD dwType;
  846. TCHAR szWinNtBuildNumber[ 16 ];
  847. DWORD cbWinNtBuildNumber = sizeof( szWinNtBuildNumber );
  848. lError = RegQueryValueEx( hKeyCurrentVersion,
  849. TEXT( "CurrentBuildNumber" ),
  850. NULL,
  851. &dwType,
  852. (LPBYTE) &szWinNtBuildNumber,
  853. &cbWinNtBuildNumber );
  854. if ( ERROR_SUCCESS != lError )
  855. {
  856. #if DBG
  857. dprintf( TEXT("WinNtBuildNumberGet(): Could not query value, error %ld.\n"), lError );
  858. #endif
  859. }
  860. else
  861. {
  862. *pdwBuildNumber = (DWORD) _wtol( szWinNtBuildNumber );
  863. }
  864. RegCloseKey( hKeyCurrentVersion );
  865. }
  866. RegCloseKey( hKeyLocalMachine );
  867. }
  868. return (DWORD) lError;
  869. }
  870. #if DBG
  871. /////////////////////////////////////////////////////////////////////////
  872. LPTSTR
  873. TimeToString(
  874. ULONG Seconds
  875. )
  876. {
  877. TIME_FIELDS tf;
  878. LARGE_INTEGER Time, LTime;
  879. static TCHAR TimeString[100];
  880. if ( 0 == Seconds )
  881. {
  882. lstrcpy(TimeString, TEXT("None"));
  883. }
  884. else
  885. {
  886. RtlSecondsSince1980ToTime(Seconds, &Time);
  887. RtlSystemTimeToLocalTime(&Time, &LTime);
  888. RtlTimeToTimeFields(&LTime, &tf);
  889. wsprintf(TimeString, TEXT("%02hd/%02hd/%04hd @ %02hd:%02hd:%02hd"), tf.Month, tf.Day, tf.Year, tf.Hour, tf.Minute, tf.Second);
  890. }
  891. return TimeString;
  892. } // TimeToString
  893. #endif