|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Abstract:
This module provides functionality for ADs within spooler
Author:
Steve Wilson (NT) July 1997
Revision History:
--*/
#include <precomp.h>
#pragma hdrstop
#include "dsprune.hxx"
#include "clusspl.h"
DWORD WINAPI DsUpdate(PDWORD pdwDelay); VOID ValidateDsProperties(PINIPRINTER pIniPrinter); HANDLE ghUpdateNow = NULL;
extern DWORD dwUpdateFlag;
extern "C" HANDLE ghDsUpdateThread; extern "C" DWORD gdwDsUpdateThreadId;
HANDLE ghDsUpdateThread = NULL; DWORD gdwDsUpdateThreadId;
BOOL gbInDomain; BOOL gdwLogDsEvents = LOG_ALL_EVENTS;
DWORD SpawnDsUpdate( DWORD dwDelay ) { DWORD dwError; PDWORD pdwDelay;
SplInSem();
if (!ghDsUpdateThread && !dwUpgradeFlag) { if (pdwDelay = (PDWORD) AllocSplMem(sizeof(DWORD))) { *pdwDelay = dwDelay;
if(!(ghDsUpdateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DsUpdate, (PVOID) pdwDelay, 0, &gdwDsUpdateThreadId))) { dwError = GetLastError(); FreeSplMem(pdwDelay); } else { CloseHandle(ghDsUpdateThread); dwError = ERROR_SUCCESS; } } else { dwError = GetLastError(); } } else { if (ghUpdateNow) SetEvent(ghUpdateNow);
dwError = ERROR_BUSY; }
return dwError; }
BOOL DsUpdatePrinter( HANDLE h, PINIPRINTER pIniPrinter ) { HANDLE hPrinter; PWSTR pszPrinterName = NULL; PDSUPDATEDATA pData = (PDSUPDATEDATA)h; DWORD dwAction;
PRINTER_DEFAULTS Defaults;
SplInSem();
Defaults.pDatatype = NULL; Defaults.pDevMode = NULL; Defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER;
//
// dwAction and DsKeyUpdateForeground are the foreground (client) thread's requested
// action and state. DsKeyUpdate is the background (DsUpdate) thread's state
// Foreground state always has priority over background, so sync up if needed.
// When both the foreground and background actions are 0, then the publish state
// is up to date.
//
DBGMSG( DBG_EXEC, ("\nBACKGROUND UPDATE: Printer \"%ws\", dwAction = %x, DsKeyUpdate = %x, DsKeyUpdateForeground = %x, Attributes = %x\n", pIniPrinter->pName, pIniPrinter->dwAction, pIniPrinter->DsKeyUpdate, pIniPrinter->DsKeyUpdateForeground, pIniPrinter->Attributes ) );
if (dwAction = pIniPrinter->dwAction) { pIniPrinter->dwAction = 0; // set to 0 so we know when client thread sets it
pIniPrinter->DsKeyUpdate |= pIniPrinter->DsKeyUpdateForeground; pIniPrinter->DsKeyUpdateForeground = 0;
//
// Mask off possible conflicts in DS_KEY_PUBLISH, REPUBLISH, and UNPUBLISH actions
//
pIniPrinter->DsKeyUpdate &= ~(DS_KEY_PUBLISH | DS_KEY_REPUBLISH | DS_KEY_UNPUBLISH); if (dwAction == DSPRINT_PUBLISH) { pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH; } else if (dwAction == DSPRINT_REPUBLISH) { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; } else if (dwAction == DSPRINT_UNPUBLISH) { pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH; } } else { //
// If DS_KEY_UPDATE_DRIVER is set by AddForm or DeleteForm foreground threads
// in DsUpdateDriverKeys(). We have to copy that to pIniPrinter->DsKeyUpdate
// even if dwAction is not set.
//
pIniPrinter->DsKeyUpdate |= (pIniPrinter->DsKeyUpdateForeground & DS_KEY_UPDATE_DRIVER); pIniPrinter->DsKeyUpdateForeground &= ~DS_KEY_UPDATE_DRIVER; }
UpdatePrinterIni(pIniPrinter, UPDATE_DS_ONLY);
if (pIniPrinter->DsKeyUpdate) {
pData->bAllUpdated = FALSE;
LeaveSplSem();
//
// If this printer is pending deletion, delete by GUID because OpenPrinter
// will fail.
//
// We check pIniPrinter->bDsPendingDeletion instead of
// pIniPrinter->Status ~ PRINTER_PENDING_DELETION because PRINTER_PENDING_DELETION
// is set in InternalDeletePrinter after it leaves Spooler CS. This gives the DS thread
// a chance to check PRINTER_PENDING_DELETION flag before it is set.
// The reason we cannot set PRINTER_PENDING_DELETION before we leave Spooler CS is because
// we want OpenPrinter calls that come from PrinterDriverEvent to succeed.
// See how InternalDeletePrinter sets the printer on PRINTER_NO_MORE_JOBS to reject incoming jobs
// but accept OpenPrinter calls.
//
if (pIniPrinter->bDsPendingDeletion) { //
// This will DECPRINTERREF to match DECPRINTERREF in SplDeletePrinter.
// UnpublishByGUID won't call DeletePrinterCheck when it DECPRINTERREF.
// RunForEachPrinter will do that.
//
UnpublishByGUID(pIniPrinter);
} else {
EnterSplSem(); pszPrinterName = pszGetPrinterName( pIniPrinter, TRUE, NULL ); LeaveSplSem();
if (pszPrinterName) { if(LocalOpenPrinter(pszPrinterName, &hPrinter, &Defaults) == ROUTER_SUCCESS) {
EnterSplSem(); if( (pIniPrinter->DsKeyUpdate & DS_KEY_UPDATE_DRIVER) && !(pIniPrinter->DsKeyUpdate & DS_KEY_REPUBLISH)) {
//
// We update the Registry with the Form data and then
// set DS_KEY_PUBLISH. Eventually SetPrinterDS() would
// update the DS.
UpdateDsDriverKey(hPrinter); pIniPrinter->DsKeyUpdate &= ~DS_KEY_UPDATE_DRIVER;
if(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) { pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH; } } if (pIniPrinter->DsKeyUpdate & DS_KEY_REPUBLISH) {
SetPrinterDs(hPrinter, DSPRINT_UNPUBLISH, TRUE);
// Unpublishing & republishing printer doesn't rewrite DS keys,
// so on Republish, we should also rewrite DS keys so we know
// everything is synched up
SplDeletePrinterKey(hPrinter, SPLDS_DRIVER_KEY); SplDeletePrinterKey(hPrinter, SPLDS_SPOOLER_KEY); UpdateDsDriverKey(hPrinter); UpdateDsSpoolerKey(hPrinter, 0xffffffff);
SetPrinterDs(hPrinter, DSPRINT_PUBLISH, TRUE);
} else if (pIniPrinter->DsKeyUpdate & DS_KEY_UNPUBLISH) { SetPrinterDs(hPrinter, DSPRINT_UNPUBLISH, TRUE);
} else if (pIniPrinter->DsKeyUpdate & DS_KEY_PUBLISH) { SetPrinterDs(hPrinter, DSPRINT_PUBLISH, TRUE);
} else { //
// If the printer is not published and DS_KEY_UPDATE_DRIVER
// is set, then we will reach here and
// DsKeyUpdate will have the DS_KEY_DRIVER set by
// UpdateDsDriverKey(). So we just clear it here.
//
pIniPrinter->DsKeyUpdate = 0; UpdatePrinterIni(pIniPrinter, UPDATE_DS_ONLY); } LeaveSplSem();
SplClosePrinter(hPrinter); } FreeSplStr(pszPrinterName); } } EnterSplSem();
if (pIniPrinter->DsKeyUpdate) { pData->bSleep = TRUE; // Only sleep if the DS is down
gdwLogDsEvents = LOG_INFO | LOG_SUCCESS; // Only report Warnings & Errors for first printer failures
} } return TRUE; }
BOOL DsUpdateSpooler( HANDLE h, PINISPOOLER pIniSpooler ) { //
// Only do this for local spoolers.
//
if (pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL) { RunForEachPrinter(pIniSpooler, h, DsUpdatePrinter); } return TRUE; }
DWORD WINAPI DsUpdate( PDWORD pdwDelay ) { DWORD dwSleep; DWORD dwError = ERROR_SUCCESS; HRESULT hr; DSUPDATEDATA Data; DWORD dwWaitTime = 0; SplOutSem();
ghUpdateNow = CreateEvent((LPSECURITY_ATTRIBUTES) NULL, FALSE, FALSE, NULL); if (ghUpdateNow) {
if (RegisterGPNotification(ghUpdateNow, TRUE)) {
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (SUCCEEDED(hr)) {
DBGMSG( DBG_EXEC, ("************** ENTER DSUPDATE\n" ) ); //
// Force initial sleep to be within 1 sec & 5 minutes
//
dwSleep = (*pdwDelay >= 1 && *pdwDelay < 300) ? *pdwDelay : 1;
FreeSplMem(pdwDelay);
if (dwSleep > 1) {
Sleep(dwSleep*1000); }
Data.dwSleepTime = dwSleep * 1000;
gdwLogDsEvents = LOG_ALL_EVENTS;
EnterSplSem();
//
// The logic of this loop changed from between Win2K to Whistler.
// On Win2k, the DS background thread used to die if there were no DS
// actions to be made.If the "Check published state" was changed,
// Spooler couldn't reflect this change unless another DS action
// created the DS thread.
// On Whistler we keep it alive but sleeping.
//
do {
Data.bAllUpdated = TRUE; Data.bSleep = FALSE;
//
// Run through and update each printer.
//
RunForEachSpooler(&Data, DsUpdateSpooler); //
// If all printers are updated or the DS is not responding,
// then put the DS thread to sleep.
//
if (Data.bAllUpdated || Data.bSleep) {
dwWaitTime = GetDSSleepInterval(&Data);
//
// If the VerifyPublishedState Policy is set, then we need to verify
// we're published based on the schedule specified by the policy.
// However, if updating is failing, we should revert to the background
// updating schedule rather than the "check published state" schedule.
//
LeaveSplSem();
DBGMSG( DBG_EXEC, ("BACKGROUND UPDATE SLEEP: %d\n", dwWaitTime));
dwError = WaitForSingleObject(ghUpdateNow, dwWaitTime);
if (dwError == WAIT_FAILED) {
//
// There is one case when the DS thread can still die.
// If this wait fails, we don't want the thread indefinitely spinning.
//
DBGMSG(DBG_WARNING, ("VerifyPublishedState Wait Failed: %d\n", GetLastError())); dwError = GetLastError(); break; }
EnterSplSem(); //
// If the "Check published state" policy is enabled,CheckPublishedPrinters will force the DS update
// for published printers.If the object doesn't exist in DS, the printer is republished.(see GetPublishPoint)
// The call returns 0 if there is the "check published state" policy
// is disabled or it is enabled and there are no published printers.
// We could actually break the loop and kill the thread in the case when we don't have published printers,
// because we don't care for policy changes.
//
CheckPublishedPrinters(); }
} while (TRUE);
LeaveSplSem(); SplOutSem();
CoUninitialize();
} else {
dwError = HRESULT_CODE(hr); }
UnregisterGPNotification(ghUpdateNow);
} else {
dwError = GetLastError(); }
CloseHandle(ghUpdateNow); ghUpdateNow = NULL;
} else {
dwError = GetLastError(); }
DBGMSG(DBG_EXEC, ("************ LEAVE DSUPDATE\n"));
ghDsUpdateThread = NULL;
return dwError; }
VOID ValidateDsProperties( PINIPRINTER pIniPrinter ) { // Properties not generated by driver, spooler, or user should be checked here.
// Currently, that means only the Server name. Note that we republish the object
// if the server name changes, so the UNCName property gets fixed as well.
DWORD dwError = ERROR_SUCCESS; BOOL dwAction = 0; HRESULT hr;
SplInSem();
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler; WCHAR pData[INTERNET_MAX_HOST_NAME_LENGTH + 3]; DWORD cbNeeded; DWORD dwType; struct hostent *pHostEnt; HKEY hKey = NULL;
dwError = OpenPrinterKey(pIniPrinter, KEY_READ | KEY_WRITE, &hKey, SPLDS_SPOOLER_KEY, TRUE); if (dwError != ERROR_SUCCESS) { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; return; }
INCPRINTERREF(pIniPrinter); LeaveSplSem();
//
// Set to publish by default. This will verify that the printer is published, but won't
// write anything if there's nothing to update.
//
pIniPrinter->DsKeyUpdate |= DS_KEY_PUBLISH;
// Check Server Name
//
// If we were unable to find a DNS name for this machine, then gethostbyname failed.
// In this case, let's just treat this attribute as being correct. If other attributes
// cause us to update, we'll probably fail because the network is down or something. But
// then again, we also might succeed.
//
if (pIniSpooler->pszFullMachineName) {
cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData; dwType = REG_SZ; dwError = SplRegQueryValue( hKey, SPLDS_SERVER_NAME, &dwType, (PBYTE) pData, &cbNeeded, pIniSpooler);
if (dwError != ERROR_SUCCESS || _wcsicmp((PWSTR) pData, pIniSpooler->pszFullMachineName)) { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; goto error; } }
//
// Check Short Server Name
//
cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData; dwType = REG_SZ; dwError = SplRegQueryValue( hKey, SPLDS_SHORT_SERVER_NAME, &dwType, (PBYTE) pData, &cbNeeded, pIniSpooler);
if (dwError != ERROR_SUCCESS || _wcsicmp((PWSTR) pData, pIniSpooler->pMachineName + 2)) { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; goto error; }
//
// Check Version Number
//
cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData; dwType = REG_DWORD; dwError = SplRegQueryValue( hKey, SPLDS_VERSION_NUMBER, &dwType, (PBYTE) pData, &cbNeeded, pIniSpooler);
if (dwError != ERROR_SUCCESS || *((PDWORD) pData) != DS_PRINTQUEUE_VERSION_WIN2000) { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; goto error; }
//
// Check Immortal flag
//
cbNeeded = (INTERNET_MAX_HOST_NAME_LENGTH + 3)*sizeof *pData; dwType = REG_DWORD; dwError = SplRegQueryValue( hKey, SPLDS_FLAGS, &dwType, (PBYTE) pData, &cbNeeded, pIniSpooler);
if (dwError != ERROR_SUCCESS) { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; goto error; } else if (*((PDWORD) pData) != (DWORD) pIniSpooler->bImmortal) { dwError = SplRegSetValue( hKey, SPLDS_FLAGS, dwType, (PBYTE) &pIniSpooler->bImmortal, sizeof pIniSpooler->bImmortal, pIniSpooler);
if (dwError == ERROR_SUCCESS) { pIniPrinter->DsKeyUpdate |= DS_KEY_SPOOLER; } else { pIniPrinter->DsKeyUpdate = DS_KEY_REPUBLISH; goto error; } }
error:
if (hKey) SplRegCloseKey(hKey, pIniSpooler);
EnterSplSem(); DECPRINTERREF(pIniPrinter); SplInSem();
return; }
extern "C" VOID InitializeDS( PINISPOOLER pIniSpooler ) { PINIPRINTER pIniPrinter = NULL; PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL; DWORD dwError = ERROR_SUCCESS; SYSTEMTIME SystemTime; DWORD dwDelay = 0;
//
// Verify that we're in a domain
//
dwError = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *) &pDsRole);
if (pDsRole) { gbInDomain = (dwError == ERROR_SUCCESS && pDsRole->MachineRole != DsRole_RoleStandaloneServer && pDsRole->MachineRole != DsRole_RoleStandaloneWorkstation);
DsRoleFreeMemory((PVOID) pDsRole);
} else {
gbInDomain = FALSE; }
if (gbInDomain) {
//
// Check if we need to update the ds
//
EnterSplSem(); //
// Get spooler policies
//
pIniSpooler->bImmortal = ImmortalPolicy(); BOOL bPublishProhibited = PrinterPublishProhibited();
//
// Run through all the printers and see if any need updating
//
for (pIniPrinter = pIniSpooler->pIniPrinter ; pIniPrinter ; pIniPrinter = pIniPrinter->pNext) {
//
// PublishProhibited not only prohibits new publishing, but also
// removes currently published printers
//
if (bPublishProhibited) { pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_PUBLISHED; }
if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) { //
// Verify properties not changed by driver or spooler
//
ValidateDsProperties(pIniPrinter);
} else if (pIniPrinter->pszObjectGUID) { //
// State is unpublished, but we haven't deleted
// the PrintQueue from the DS yet.
//
pIniPrinter->DsKeyUpdate = DS_KEY_UNPUBLISH; } else { pIniPrinter->DsKeyUpdate = 0; }
if (!dwDelay && (pIniPrinter->DsKeyUpdate || pIniPrinter->DsKeyUpdateForeground)) { //
// Initially sleep a random amount of time
// This keeps network traffic down if there's been a power outage
//
GetSystemTime(&SystemTime);
srand((unsigned) SystemTime.wMilliseconds); //
// 100 different sleep times from 1 sec - 100 sec
// Typical time to publish printer is 5 seconds. Updates and deletes are just a couple seconds
//
dwDelay = (rand()%100) + 1; } }
if (dwDelay) SpawnDsUpdate(dwDelay);
LeaveSplSem();
if (ThisMachineIsADC()) {
GetSystemTime(&SystemTime); srand((unsigned) SystemTime.wMilliseconds);
DWORD dwPruningInterval = PruningInterval();
if (dwPruningInterval == INFINITE) dwPruningInterval = DEFAULT_PRUNING_INTERVAL;
if (dwPruningInterval) SpawnDsPrune(rand()%dwPruningInterval); else SpawnDsPrune(0); } }
ServerThreadPolicy(gbInDomain);
return; }
BOOL DsUpdateDriverKeys( HANDLE h, PINIPRINTER pIniPrinter ) { //
// For now, we need this only when a new user defined form is added/deleted.
// For this case, UpdateDsDriverKey is not called before call SetPrinterDS
// We set all pIniPrinters->DsKeyUpdateForeground with DS_KEY_UPDATE_DRIVER so that
// on DsUpdatePrinter we know that UpdateDsDriverKey must be called before
// we try to publish the printer.
//
SplInSem();
pIniPrinter->DsKeyUpdateForeground |= DS_KEY_UPDATE_DRIVER; return TRUE; } BOOL DsUpdateAllDriverKeys( HANDLE h, PINISPOOLER pIniSpooler ) { HANDLE hToken = NULL; //
// Only do this for local spoolers.
//
if (pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL) { RunForEachPrinter(pIniSpooler, h, DsUpdateDriverKeys); }
//
// All DS accesses are done by LocalSystem account
//
hToken = RevertToPrinterSelf(); SpawnDsUpdate(1); if (hToken) ImpersonatePrinterClient(hToken); return TRUE; }
|