Leaked source code of windows server 2003
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.

698 lines
22 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Abstract:
  4. This module provides functionality for ADs within spooler
  5. Author:
  6. Steve Wilson (NT) July 1997
  7. Revision History:
  8. --*/
  9. #include <precomp.h>
  10. #pragma hdrstop
  11. #include "dsprune.hxx"
  12. #include "clusspl.h"
  13. DWORD WINAPI DsUpdate(PDWORD pdwDelay);
  14. VOID ValidateDsProperties(PINIPRINTER pIniPrinter);
  15. HANDLE ghUpdateNow = NULL;
  16. extern DWORD dwUpdateFlag;
  17. extern "C" HANDLE ghDsUpdateThread;
  18. extern "C" DWORD gdwDsUpdateThreadId;
  19. HANDLE ghDsUpdateThread = NULL;
  20. DWORD gdwDsUpdateThreadId;
  21. BOOL gbInDomain;
  22. BOOL gdwLogDsEvents = LOG_ALL_EVENTS;
  23. DWORD
  24. SpawnDsUpdate(
  25. DWORD dwDelay
  26. )
  27. {
  28. DWORD dwError;
  29. PDWORD pdwDelay;
  30. SplInSem();
  31. if (!ghDsUpdateThread && !dwUpgradeFlag) {
  32. if (pdwDelay = (PDWORD) AllocSplMem(sizeof(DWORD))) {
  33. *pdwDelay = dwDelay;
  34. if(!(ghDsUpdateThread = CreateThread(NULL,
  35. 0,
  36. (LPTHREAD_START_ROUTINE) DsUpdate,
  37. (PVOID) pdwDelay,
  38. 0,
  39. &gdwDsUpdateThreadId))) {
  40. dwError = GetLastError();
  41. FreeSplMem(pdwDelay);
  42. } else {
  43. CloseHandle(ghDsUpdateThread);
  44. dwError = ERROR_SUCCESS;
  45. }
  46. } else {
  47. dwError = GetLastError();
  48. }
  49. } else {
  50. if (ghUpdateNow)
  51. SetEvent(ghUpdateNow);
  52. dwError = ERROR_BUSY;
  53. }
  54. return dwError;
  55. }
  56. BOOL
  57. DsUpdatePrinter(
  58. HANDLE h,
  59. PINIPRINTER pIniPrinter
  60. )
  61. {
  62. HANDLE hPrinter;
  63. PWSTR pszPrinterName = NULL;
  64. PDSUPDATEDATA pData = (PDSUPDATEDATA)h;
  65. DWORD dwAction;
  66. PRINTER_DEFAULTS Defaults;
  67. SplInSem();
  68. Defaults.pDatatype = NULL;
  69. Defaults.pDevMode = NULL;
  70. Defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER;
  71. //
  72. // dwAction and DsKeyUpdateForeground are the foreground (client) thread's requested
  73. // action and state. DsKeyUpdate is the background (DsUpdate) thread's state
  74. // Foreground state always has priority over background, so sync up if needed.
  75. // When both the foreground and background actions are 0, then the publish state
  76. // is up to date.
  77. //
  78. DBGMSG( DBG_EXEC, ("\nBACKGROUND UPDATE: Printer \"%ws\", dwAction = %x, DsKeyUpdate = %x, DsKeyUpdateForeground = %x, Attributes = %x\n",
  79. pIniPrinter->pName, pIniPrinter->dwAction, pIniPrinter->DsKeyUpdate, pIniPrinter->DsKeyUpdateForeground, pIniPrinter->Attributes ) );
  80. if (dwAction = pIniPrinter->dwAction) {
  81. pIniPrinter->dwAction = 0; // set to 0 so we know when client thread sets it
  82. pIniPrinter->DsKeyUpdate |= pIniPrinter->DsKeyUpdateForeground;
  83. pIniPrinter->DsKeyUpdateForeground = 0;
  84. //
  85. // Mask off possible conflicts in DS_KEY_PUBLISH, REPUBLISH, and UNPUBLISH actions
  86. //
  87. pIniPrinter->DsKeyUpdate &= ~(DS_KEY_PUBLISH | DS_KEY_REPUBLISH | DS_KEY_UNPUBLISH);
  88. if (dwAction == DSPRINT_PUBLISH) {
  89. pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
  90. } else if (dwAction == DSPRINT_REPUBLISH) {
  91. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  92. } else if (dwAction == DSPRINT_UNPUBLISH) {
  93. pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH;
  94. }
  95. } else {
  96. //
  97. // If DS_KEY_UPDATE_DRIVER is set by AddForm or DeleteForm foreground threads
  98. // in DsUpdateDriverKeys(). We have to copy that to pIniPrinter->DsKeyUpdate
  99. // even if dwAction is not set.
  100. //
  101. pIniPrinter->DsKeyUpdate |= (pIniPrinter->DsKeyUpdateForeground & DS_KEY_UPDATE_DRIVER);
  102. pIniPrinter->DsKeyUpdateForeground &= ~DS_KEY_UPDATE_DRIVER;
  103. }
  104. UpdatePrinterIni(pIniPrinter, UPDATE_DS_ONLY);
  105. if (pIniPrinter->DsKeyUpdate) {
  106. pData->bAllUpdated = FALSE;
  107. LeaveSplSem();
  108. //
  109. // If this printer is pending deletion, delete by GUID because OpenPrinter
  110. // will fail.
  111. //
  112. // We check pIniPrinter->bDsPendingDeletion instead of
  113. // pIniPrinter->Status ~ PRINTER_PENDING_DELETION because PRINTER_PENDING_DELETION
  114. // is set in InternalDeletePrinter after it leaves Spooler CS. This gives the DS thread
  115. // a chance to check PRINTER_PENDING_DELETION flag before it is set.
  116. // The reason we cannot set PRINTER_PENDING_DELETION before we leave Spooler CS is because
  117. // we want OpenPrinter calls that come from PrinterDriverEvent to succeed.
  118. // See how InternalDeletePrinter sets the printer on PRINTER_NO_MORE_JOBS to reject incoming jobs
  119. // but accept OpenPrinter calls.
  120. //
  121. if (pIniPrinter->bDsPendingDeletion) {
  122. //
  123. // This will DECPRINTERREF to match DECPRINTERREF in SplDeletePrinter.
  124. // UnpublishByGUID won't call DeletePrinterCheck when it DECPRINTERREF.
  125. // RunForEachPrinter will do that.
  126. //
  127. UnpublishByGUID(pIniPrinter);
  128. } else {
  129. EnterSplSem();
  130. pszPrinterName = pszGetPrinterName( pIniPrinter, TRUE, NULL );
  131. LeaveSplSem();
  132. if (pszPrinterName) {
  133. if(LocalOpenPrinter(pszPrinterName, &hPrinter, &Defaults) == ROUTER_SUCCESS) {
  134. EnterSplSem();
  135. if( (pIniPrinter->DsKeyUpdate & DS_KEY_UPDATE_DRIVER) &&
  136. !(pIniPrinter->DsKeyUpdate & DS_KEY_REPUBLISH)) {
  137. //
  138. // We update the Registry with the Form data and then
  139. // set DS_KEY_PUBLISH. Eventually SetPrinterDS() would
  140. // update the DS.
  141. UpdateDsDriverKey(hPrinter);
  142. pIniPrinter->DsKeyUpdate &= ~DS_KEY_UPDATE_DRIVER;
  143. if(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
  144. pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
  145. }
  146. }
  147. if (pIniPrinter->DsKeyUpdate & DS_KEY_REPUBLISH) {
  148. SetPrinterDs(hPrinter, DSPRINT_UNPUBLISH, TRUE);
  149. // Unpublishing & republishing printer doesn't rewrite DS keys,
  150. // so on Republish, we should also rewrite DS keys so we know
  151. // everything is synched up
  152. SplDeletePrinterKey(hPrinter, SPLDS_DRIVER_KEY);
  153. SplDeletePrinterKey(hPrinter, SPLDS_SPOOLER_KEY);
  154. UpdateDsDriverKey(hPrinter);
  155. UpdateDsSpoolerKey(hPrinter, 0xffffffff);
  156. SetPrinterDs(hPrinter, DSPRINT_PUBLISH, TRUE);
  157. } else if (pIniPrinter->DsKeyUpdate & DS_KEY_UNPUBLISH) {
  158. SetPrinterDs(hPrinter, DSPRINT_UNPUBLISH, TRUE);
  159. } else if (pIniPrinter->DsKeyUpdate & DS_KEY_PUBLISH) {
  160. SetPrinterDs(hPrinter, DSPRINT_PUBLISH, TRUE);
  161. } else {
  162. //
  163. // If the printer is not published and DS_KEY_UPDATE_DRIVER
  164. // is set, then we will reach here and
  165. // DsKeyUpdate will have the DS_KEY_DRIVER set by
  166. // UpdateDsDriverKey(). So we just clear it here.
  167. //
  168. pIniPrinter->DsKeyUpdate = 0;
  169. UpdatePrinterIni(pIniPrinter, UPDATE_DS_ONLY);
  170. }
  171. LeaveSplSem();
  172. SplClosePrinter(hPrinter);
  173. }
  174. FreeSplStr(pszPrinterName);
  175. }
  176. }
  177. EnterSplSem();
  178. if (pIniPrinter->DsKeyUpdate) {
  179. pData->bSleep = TRUE; // Only sleep if the DS is down
  180. gdwLogDsEvents = LOG_INFO | LOG_SUCCESS; // Only report Warnings & Errors for first printer failures
  181. }
  182. }
  183. return TRUE;
  184. }
  185. BOOL
  186. DsUpdateSpooler(
  187. HANDLE h,
  188. PINISPOOLER pIniSpooler
  189. )
  190. {
  191. //
  192. // Only do this for local spoolers.
  193. //
  194. if (pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL) {
  195. RunForEachPrinter(pIniSpooler, h, DsUpdatePrinter);
  196. }
  197. return TRUE;
  198. }
  199. DWORD
  200. WINAPI
  201. DsUpdate(
  202. PDWORD pdwDelay
  203. )
  204. {
  205. DWORD dwSleep;
  206. DWORD dwError = ERROR_SUCCESS;
  207. HRESULT hr;
  208. DSUPDATEDATA Data;
  209. DWORD dwWaitTime = 0;
  210. SplOutSem();
  211. ghUpdateNow = CreateEvent((LPSECURITY_ATTRIBUTES) NULL, FALSE, FALSE, NULL);
  212. if (ghUpdateNow) {
  213. if (RegisterGPNotification(ghUpdateNow, TRUE)) {
  214. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  215. if (SUCCEEDED(hr)) {
  216. DBGMSG( DBG_EXEC, ("************** ENTER DSUPDATE\n" ) );
  217. //
  218. // Force initial sleep to be within 1 sec & 5 minutes
  219. //
  220. dwSleep = (*pdwDelay >= 1 && *pdwDelay < 300) ? *pdwDelay : 1;
  221. FreeSplMem(pdwDelay);
  222. if (dwSleep > 1) {
  223. Sleep(dwSleep*1000);
  224. }
  225. Data.dwSleepTime = dwSleep * 1000;
  226. gdwLogDsEvents = LOG_ALL_EVENTS;
  227. EnterSplSem();
  228. //
  229. // The logic of this loop changed from between Win2K to Whistler.
  230. // On Win2k, the DS background thread used to die if there were no DS
  231. // actions to be made.If the "Check published state" was changed,
  232. // Spooler couldn't reflect this change unless another DS action
  233. // created the DS thread.
  234. // On Whistler we keep it alive but sleeping.
  235. //
  236. do {
  237. Data.bAllUpdated = TRUE;
  238. Data.bSleep = FALSE;
  239. //
  240. // Run through and update each printer.
  241. //
  242. RunForEachSpooler(&Data, DsUpdateSpooler);
  243. //
  244. // If all printers are updated or the DS is not responding,
  245. // then put the DS thread to sleep.
  246. //
  247. if (Data.bAllUpdated || Data.bSleep) {
  248. dwWaitTime = GetDSSleepInterval(&Data);
  249. //
  250. // If the VerifyPublishedState Policy is set, then we need to verify
  251. // we're published based on the schedule specified by the policy.
  252. // However, if updating is failing, we should revert to the background
  253. // updating schedule rather than the "check published state" schedule.
  254. //
  255. LeaveSplSem();
  256. DBGMSG( DBG_EXEC, ("BACKGROUND UPDATE SLEEP: %d\n", dwWaitTime));
  257. dwError = WaitForSingleObject(ghUpdateNow, dwWaitTime);
  258. if (dwError == WAIT_FAILED) {
  259. //
  260. // There is one case when the DS thread can still die.
  261. // If this wait fails, we don't want the thread indefinitely spinning.
  262. //
  263. DBGMSG(DBG_WARNING, ("VerifyPublishedState Wait Failed: %d\n", GetLastError()));
  264. dwError = GetLastError();
  265. break;
  266. }
  267. EnterSplSem();
  268. //
  269. // If the "Check published state" policy is enabled,CheckPublishedPrinters will force the DS update
  270. // for published printers.If the object doesn't exist in DS, the printer is republished.(see GetPublishPoint)
  271. // The call returns 0 if there is the "check published state" policy
  272. // is disabled or it is enabled and there are no published printers.
  273. // We could actually break the loop and kill the thread in the case when we don't have published printers,
  274. // because we don't care for policy changes.
  275. //
  276. CheckPublishedPrinters();
  277. }
  278. } while (TRUE);
  279. LeaveSplSem();
  280. SplOutSem();
  281. CoUninitialize();
  282. } else {
  283. dwError = HRESULT_CODE(hr);
  284. }
  285. UnregisterGPNotification(ghUpdateNow);
  286. } else {
  287. dwError = GetLastError();
  288. }
  289. CloseHandle(ghUpdateNow);
  290. ghUpdateNow = NULL;
  291. } else {
  292. dwError = GetLastError();
  293. }
  294. DBGMSG(DBG_EXEC, ("************ LEAVE DSUPDATE\n"));
  295. ghDsUpdateThread = NULL;
  296. return dwError;
  297. }
  298. VOID
  299. ValidateDsProperties(
  300. PINIPRINTER pIniPrinter
  301. )
  302. {
  303. // Properties not generated by driver, spooler, or user should be checked here.
  304. // Currently, that means only the Server name. Note that we republish the object
  305. // if the server name changes, so the UNCName property gets fixed as well.
  306. DWORD dwError = ERROR_SUCCESS;
  307. BOOL dwAction = 0;
  308. HRESULT hr;
  309. SplInSem();
  310. PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
  311. WCHAR pData[INTERNET_MAX_HOST_NAME_LENGTH + 3];
  312. DWORD cbNeeded;
  313. DWORD dwType;
  314. struct hostent *pHostEnt;
  315. HKEY hKey = NULL;
  316. dwError = OpenPrinterKey(pIniPrinter, KEY_READ | KEY_WRITE, &hKey, SPLDS_SPOOLER_KEY, TRUE);
  317. if (dwError != ERROR_SUCCESS) {
  318. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  319. return;
  320. }
  321. INCPRINTERREF(pIniPrinter);
  322. LeaveSplSem();
  323. //
  324. // Set to publish by default. This will verify that the printer is published, but won't
  325. // write anything if there's nothing to update.
  326. //
  327. pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
  328. // Check Server Name
  329. //
  330. // If we were unable to find a DNS name for this machine, then gethostbyname failed.
  331. // In this case, let's just treat this attribute as being correct. If other attributes
  332. // cause us to update, we'll probably fail because the network is down or something. But
  333. // then again, we also might succeed.
  334. //
  335. if (pIniSpooler->pszFullMachineName) {
  336. cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData;
  337. dwType = REG_SZ;
  338. dwError = SplRegQueryValue( hKey,
  339. SPLDS_SERVER_NAME,
  340. &dwType,
  341. (PBYTE) pData,
  342. &cbNeeded,
  343. pIniSpooler);
  344. if (dwError != ERROR_SUCCESS || _wcsicmp((PWSTR) pData, pIniSpooler->pszFullMachineName)) {
  345. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  346. goto error;
  347. }
  348. }
  349. //
  350. // Check Short Server Name
  351. //
  352. cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData;
  353. dwType = REG_SZ;
  354. dwError = SplRegQueryValue( hKey,
  355. SPLDS_SHORT_SERVER_NAME,
  356. &dwType,
  357. (PBYTE) pData,
  358. &cbNeeded,
  359. pIniSpooler);
  360. if (dwError != ERROR_SUCCESS || _wcsicmp((PWSTR) pData, pIniSpooler->pMachineName + 2)) {
  361. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  362. goto error;
  363. }
  364. //
  365. // Check Version Number
  366. //
  367. cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData;
  368. dwType = REG_DWORD;
  369. dwError = SplRegQueryValue( hKey,
  370. SPLDS_VERSION_NUMBER,
  371. &dwType,
  372. (PBYTE) pData,
  373. &cbNeeded,
  374. pIniSpooler);
  375. if (dwError != ERROR_SUCCESS || *((PDWORD) pData) != DS_PRINTQUEUE_VERSION_WIN2000) {
  376. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  377. goto error;
  378. }
  379. //
  380. // Check Immortal flag
  381. //
  382. cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData;
  383. dwType = REG_DWORD;
  384. dwError = SplRegQueryValue( hKey,
  385. SPLDS_FLAGS,
  386. &dwType,
  387. (PBYTE) pData,
  388. &cbNeeded,
  389. pIniSpooler);
  390. if (dwError != ERROR_SUCCESS) {
  391. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  392. goto error;
  393. } else if (*((PDWORD) pData) != (DWORD) pIniSpooler->bImmortal) {
  394. dwError = SplRegSetValue( hKey,
  395. SPLDS_FLAGS,
  396. dwType,
  397. (PBYTE) &pIniSpooler->bImmortal,
  398. sizeof pIniSpooler->bImmortal,
  399. pIniSpooler);
  400. if (dwError == ERROR_SUCCESS) {
  401. pIniPrinter->DsKeyUpdate |= DS_KEY_SPOOLER;
  402. }
  403. else {
  404. pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH;
  405. goto error;
  406. }
  407. }
  408. error:
  409. if (hKey)
  410. SplRegCloseKey(hKey, pIniSpooler);
  411. EnterSplSem();
  412. DECPRINTERREF(pIniPrinter);
  413. SplInSem();
  414. return;
  415. }
  416. extern "C" VOID
  417. InitializeDS(
  418. PINISPOOLER pIniSpooler
  419. )
  420. {
  421. PINIPRINTER pIniPrinter = NULL;
  422. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
  423. DWORD dwError = ERROR_SUCCESS;
  424. SYSTEMTIME SystemTime;
  425. DWORD dwDelay = 0;
  426. //
  427. // Verify that we're in a domain
  428. //
  429. dwError = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
  430. if (pDsRole) {
  431. gbInDomain = (dwError == ERROR_SUCCESS &&
  432. pDsRole->MachineRole != DsRole_RoleStandaloneServer &&
  433. pDsRole->MachineRole != DsRole_RoleStandaloneWorkstation);
  434. DsRoleFreeMemory((PVOID) pDsRole);
  435. } else {
  436. gbInDomain = FALSE;
  437. }
  438. if (gbInDomain) {
  439. //
  440. // Check if we need to update the ds
  441. //
  442. EnterSplSem();
  443. //
  444. // Get spooler policies
  445. //
  446. pIniSpooler->bImmortal = ImmortalPolicy();
  447. BOOL bPublishProhibited = PrinterPublishProhibited();
  448. //
  449. // Run through all the printers and see if any need updating
  450. //
  451. for (pIniPrinter = pIniSpooler->pIniPrinter ; pIniPrinter ; pIniPrinter = pIniPrinter->pNext) {
  452. //
  453. // PublishProhibited not only prohibits new publishing, but also
  454. // removes currently published printers
  455. //
  456. if (bPublishProhibited) {
  457. pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_PUBLISHED;
  458. }
  459. if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
  460. //
  461. // Verify properties not changed by driver or spooler
  462. //
  463. ValidateDsProperties(pIniPrinter);
  464. } else if (pIniPrinter->pszObjectGUID) {
  465. //
  466. // State is unpublished, but we haven't deleted
  467. // the PrintQueue from the DS yet.
  468. //
  469. pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH;
  470. } else {
  471. pIniPrinter->DsKeyUpdate = 0;
  472. }
  473. if (!dwDelay && (pIniPrinter->DsKeyUpdate || pIniPrinter->DsKeyUpdateForeground)) {
  474. //
  475. // Initially sleep a random amount of time
  476. // This keeps network traffic down if there's been a power outage
  477. //
  478. GetSystemTime(&SystemTime);
  479. srand((unsigned) SystemTime.wMilliseconds);
  480. //
  481. // 100 different sleep times from 1 sec - 100 sec
  482. // Typical time to publish printer is 5 seconds. Updates and deletes are just a couple seconds
  483. //
  484. dwDelay = (rand()%100) + 1;
  485. }
  486. }
  487. if (dwDelay)
  488. SpawnDsUpdate(dwDelay);
  489. LeaveSplSem();
  490. if (ThisMachineIsADC()) {
  491. GetSystemTime(&SystemTime);
  492. srand((unsigned) SystemTime.wMilliseconds);
  493. DWORD dwPruningInterval = PruningInterval();
  494. if (dwPruningInterval == INFINITE)
  495. dwPruningInterval = DEFAULT_PRUNING_INTERVAL;
  496. if (dwPruningInterval)
  497. SpawnDsPrune(rand()%dwPruningInterval);
  498. else
  499. SpawnDsPrune(0);
  500. }
  501. }
  502. ServerThreadPolicy(gbInDomain);
  503. return;
  504. }
  505. BOOL
  506. DsUpdateDriverKeys(
  507. HANDLE h,
  508. PINIPRINTER pIniPrinter
  509. )
  510. {
  511. //
  512. // For now, we need this only when a new user defined form is added/deleted.
  513. // For this case, UpdateDsDriverKey is not called before call SetPrinterDS
  514. // We set all pIniPrinters->DsKeyUpdateForeground with DS_KEY_UPDATE_DRIVER so that
  515. // on DsUpdatePrinter we know that UpdateDsDriverKey must be called before
  516. // we try to publish the printer.
  517. //
  518. SplInSem();
  519. pIniPrinter->DsKeyUpdateForeground |= DS_KEY_UPDATE_DRIVER;
  520. return TRUE;
  521. }
  522. BOOL
  523. DsUpdateAllDriverKeys(
  524. HANDLE h,
  525. PINISPOOLER pIniSpooler
  526. )
  527. {
  528. HANDLE hToken = NULL;
  529. //
  530. // Only do this for local spoolers.
  531. //
  532. if (pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL) {
  533. RunForEachPrinter(pIniSpooler, h, DsUpdateDriverKeys);
  534. }
  535. //
  536. // All DS accesses are done by LocalSystem account
  537. //
  538. hToken = RevertToPrinterSelf();
  539. SpawnDsUpdate(1);
  540. if (hToken)
  541. ImpersonatePrinterClient(hToken);
  542. return TRUE;
  543. }