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.
 
 
 
 
 
 

928 lines
27 KiB

/*++
Copyright (c) 1990-1994 Microsoft Corporation
All rights reserved
Module Name:
init.c
Abstract:
Author:
Environment:
User Mode -Win32
Revision History:
4-Jan-1999 Khaleds
Added Code for optimiziting the load time of the spooler by decoupling
the startup dependency between spoolsv and spoolss
--*/
#include "precomp.h"
#include "local.h"
#include <wmi.h>
#include "ncmgr.hxx"
#pragma hdrstop
WCHAR szDefaultPrinterNotifyInfoDataSize[] = L"DefaultPrinterNotifyInfoDataSize";
WCHAR szFailAllocs[] = L"FailAllocs";
WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 3];
#define DEFAULT_PRINTER_NOTIFY_DATA 0x80
DWORD cDefaultPrinterNotifyInfoData = DEFAULT_PRINTER_NOTIFY_DATA;
WCHAR szRouterCacheSize[] = L"RouterCacheSize";
WCHAR szUpgradeInProgKey[] = L"UpgradeInProgress";
WCHAR szMiniSetupInProgKey[] = L"MiniSetupInProgress";
fnWinSpoolDrv fnClientSide;
BOOL bWinspoolInitialized = FALSE;
HANDLE hEventInit = NULL;
BOOL Initialized = FALSE;
DWORD dwUpgradeFlag = 0;
SERVICE_STATUS_HANDLE ghSplHandle = NULL;
extern CRITICAL_SECTION RouterCriticalSection;
extern CRITICAL_SECTION DeviceArrivalCS;
extern PROUTERCACHE RouterCacheTable;
extern DWORD RouterCacheSize;
extern LPWSTR *ppszOtherNames;
BOOL
SpoolerInitAll();
VOID
RegisterForPnPEvents(
VOID
);
LPPROVIDOR
InitializeProvidor(
LPWSTR pProvidorName,
LPWSTR pFullName)
{
BOOL bRet = FALSE;
HANDLE hModule = NULL;
LPPROVIDOR pProvidor = NULL;
HANDLE hToken = NULL;
UINT ErrorMode;
hToken = RevertToPrinterSelf();
if (!hToken)
{
goto Cleanup;
}
//
// WARNING-WARNING-WARNING, we null set the print providor
// structure. older version of the print providor have different print
// providor sizes so they will set only some function pointers and not
// all of them
//
if ( !(pProvidor = (LPPROVIDOR)AllocSplMem(sizeof(PROVIDOR))) ||
!(pProvidor->lpName = AllocSplStr(pProvidorName)) ) {
DBGMSG(DBG_ERROR,
("InitializeProvidor can't allocate memory for %ws\n",
pProvidorName));
goto Cleanup;
}
//
// Make sure we don't get any dialogs popping up while this goes on.
//
ErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
hModule = pProvidor->hModule = LoadLibraryEx( pProvidorName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
SetErrorMode( ErrorMode );
if ( !hModule ) {
DBGMSG(DBG_WARNING,
("InitializeProvider failed LoadLibrary( %ws ) error %d\n",
pProvidorName, GetLastError() ));
goto Cleanup;
}
pProvidor->fpInitialize = GetProcAddress(hModule, "InitializePrintProvidor");
if ( !pProvidor->fpInitialize )
goto Cleanup;
bRet = (BOOL)pProvidor->fpInitialize(&pProvidor->PrintProvidor,
sizeof(PRINTPROVIDOR),
pFullName);
if ( !bRet ) {
DBGMSG(DBG_WARNING,
("InitializePrintProvider failed for providor %ws error %d\n",
pProvidorName, GetLastError()));
}
//
// It is not a critical error if ImpersonatePrinterClient fails.
// If fpInitialize succeeds and ImpersonatePrinterClient fails,
// then if we set bRet to FALSE we forcefully unload the initialized
// provider DLL and can cause resource leaks.
//
ImpersonatePrinterClient(hToken);
Cleanup:
if ( bRet ) {
//
// Fixup any NULL entrypoints.
//
FixupOldProvidor( &pProvidor->PrintProvidor );
return pProvidor;
} else {
if ( hModule )
FreeLibrary(hModule);
if ( pProvidor ) {
FreeSplStr(pProvidor->lpName);
FreeSplMem(pProvidor);
}
return NULL;
}
}
BOOL
DllMain(
HINSTANCE hInstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
BOOL Failed = FALSE;
BOOL ThreadInitted = FALSE,
WPCInitted = FALSE,
RouterCritSecInit = TRUE,
DevArrCritSecInit = TRUE;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
if( !bSplLibInit(NULL)){
Failed = TRUE;
goto Done;
}
DisableThreadLibraryCalls(hInstDLL);
if(!InitializeCriticalSectionAndSpinCount(&RouterCriticalSection, 0x80000000))
{
Failed = TRUE;
RouterCritSecInit = FALSE;
goto Done;
}
if(!InitializeCriticalSectionAndSpinCount(&DeviceArrivalCS, 0x80000000))
{
Failed = TRUE;
DevArrCritSecInit = FALSE;
goto Done;
}
if (!WPCInit()) {
Failed = TRUE;
goto Done;
} else {
WPCInitted = TRUE;
}
if (!ThreadInit()) {
Failed = TRUE;
goto Done;
} else {
ThreadInitted = TRUE;
}
//
// Create our global init event (manual reset)
// This will be set when we are initialized.
//
hEventInit = CreateEvent(NULL,
TRUE,
FALSE,
NULL);
if (!hEventInit) {
Failed = TRUE;
goto Done;
}
Done:
if (Failed)
{
if (RouterCritSecInit) {
DeleteCriticalSection(&RouterCriticalSection);
}
if (DevArrCritSecInit)
{
DeleteCriticalSection(&DeviceArrivalCS);
}
if (hEventInit) {
CloseHandle(hEventInit);
}
if (WPCInitted) {
WPCDestroy();
}
if (ThreadInitted) {
ThreadDestroy();
}
WmiTerminateTrace(); // Unregisters spoolss from WMI.
return FALSE;
}
break;
case DLL_PROCESS_DETACH:
ThreadDestroy();
WPCDestroy();
CloseHandle(hEventInit);
break;
}
return TRUE;
}
BOOL
InitializeRouter(
IN RouterInitializationParams *pRouterParams
)
/*++
Routine Description:
This function will Initialize the Routing layer for the Print Providors.
This will involve scanning the win.ini file, loading Print Providors, and
creating instance data for each.
Arguments:
pRouterParams - Parameters passed in to the router.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
LPPROVIDOR pProvidor;
DWORD cbDll;
WCHAR ProvidorName[MAX_PATH], Dll[MAX_PATH], szFullName[MAX_PATH];
HKEY hKey, hKey1;
LONG Status;
LPWSTR lpMem = NULL;
LPWSTR psz = NULL;
DWORD dwRequired = 0;
BOOL bRet = FALSE;
DWORD SpoolerPriorityClass = 0;
NT_PRODUCT_TYPE NtProductType;
DWORD dwCacheSize = 0;
DWORD dwType;
DWORD cbData;
DWORD i;
extern DWORD cOtherNames;
WCHAR szSetupKey[] = L"System\\Setup";
//
// First, assign the server side exports to our global variable to keep
// track of them.
//
gpServerExports = (PrintSpoolerServerExports*)(pRouterParams->pExports);
//
// WMI Trace Events. Registers spoolss with WMI.
//
WmiInitializeTrace();
//
// Initliaize the name cache
//
{
HRESULT hr;
hr = CacheInitNameCache();
if (SUCCEEDED(hr))
{
hr = InitializePnPIPAddressChangeListener(CacheRefresh);
}
if (FAILED(hr))
{
DBGMSG(DBG_ERROR, ("Failed initializing the cache hr %x\n", hr));
ExitProcess(0);
}
}
ghSplHandle = pRouterParams->SpoolerStatusHandle;
//
// We are now assume that the other services and drivers have
// initialized. The loader of this dll must do this syncing.
//
// spoolss\server does this by using the GroupOrderList
// SCM will try load load parallel and serial before starting
// the spooler service.
//
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szPrintKey,
0,
KEY_QUERY_VALUE,
&hKey)) {
cbData = sizeof(SpoolerPriorityClass);
//
// SpoolerPriority
//
Status = RegQueryValueEx(hKey,
L"SpoolerPriority",
NULL,
&dwType,
(LPBYTE)&SpoolerPriorityClass,
&cbData);
if (Status == ERROR_SUCCESS &&
(SpoolerPriorityClass == IDLE_PRIORITY_CLASS ||
SpoolerPriorityClass == NORMAL_PRIORITY_CLASS ||
SpoolerPriorityClass == HIGH_PRIORITY_CLASS)) {
Status = SetPriorityClass(GetCurrentProcess(), SpoolerPriorityClass);
}
cbData = sizeof(cDefaultPrinterNotifyInfoData);
//
// Ignore failure case since we can use the default
//
RegQueryValueEx(hKey,
szDefaultPrinterNotifyInfoDataSize,
NULL,
&dwType,
(LPBYTE)&cDefaultPrinterNotifyInfoData,
&cbData);
RegCloseKey(hKey);
}
// Is it an upgrade?
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szSetupKey,
0,
KEY_QUERY_VALUE,
&hKey)) {
/*++
You can tell if you are inside gui setup by looking for
HKLM\System\Setup\SystemSetupInProgress -- non zero means gui-setup is running.
The following description is outdated.
Description: the query update flag is set up by TedM. We will read this flag
if the flag has been set, we will set a boolean variable saying that we're in
the upgrade mode. All upgrade activities will be carried out based on this flag.
For subsequents startups of the spooler, this flag will be unvailable so we
won't run the spooler in upgrade mode.
--*/
dwUpgradeFlag = 0;
cbData = sizeof(dwUpgradeFlag);
Status = RegQueryValueEx(hKey,
L"SystemSetupInProgress",
NULL,
&dwType,
(LPBYTE)&dwUpgradeFlag,
&cbData);
if (Status != ERROR_SUCCESS) {
dwUpgradeFlag = 0;
}
DBGMSG(DBG_TRACE, ("The Spooler Upgrade flag is %d\n", dwUpgradeFlag));
//
// In case of OutOfBoxExperience(OOBE) the SystemSetupInProgress key is set as well.
// However, we want to run spooler in normal mode in this case. So, if we find that
// OOBE is running we are going to reset dwUpgradeFlag to zero.
//
if (dwUpgradeFlag)
{
DWORD dwUpgradeInProgFlag = 0;
DWORD dwMiniSetupInProgFlag = 0;
cbData = sizeof(dwUpgradeInProgFlag);
Status = RegQueryValueEx(hKey,
szUpgradeInProgKey,
NULL,
NULL,
(LPBYTE)&dwUpgradeInProgFlag,
&cbData);
if (Status == ERROR_SUCCESS)
{
cbData = sizeof(dwMiniSetupInProgFlag);
Status = RegQueryValueEx(hKey,
szMiniSetupInProgKey,
NULL,
NULL,
(LPBYTE)&dwMiniSetupInProgFlag,
&cbData);
}
//
// If we successfully read all the registry keys, and the minisetup flag is
// not set and the upgrade flag is set. It means that OOBE is running, so we
// want to run spooler in normal mode.
//
if (Status == ERROR_SUCCESS && !dwMiniSetupInProgFlag && dwUpgradeInProgFlag)
{
dwUpgradeFlag = 0;
}
}
RegCloseKey(hKey);
}
// Setup machine names
szMachineName[0] = szMachineName[1] = L'\\';
i = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerName(szMachineName+2, &i)) {
DBGMSG(DBG_ERROR, ("Failed to get computer name %d\n", GetLastError()));
ExitProcess(0);
}
if (!BuildOtherNamesFromMachineName(&ppszOtherNames, &cOtherNames)) {
DBGMSG(DBG_TRACE, ("Failed to determine other machine names %d\n", GetLastError()));
}
if (!(pLocalProvidor = InitializeProvidor(szLocalSplDll, NULL))) {
DBGMSG(DBG_WARN, ("Failed to initialize local print provider, error %d\n", GetLastError() ));
ExitProcess(0);
}
pProvidor = pLocalProvidor;
Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegistryProvidors, 0,
KEY_READ, &hKey);
if (Status == ERROR_SUCCESS) {
//
// Now query szCacheSize for the RouterCacheSize value
// if there is no RouterCacheSize replace it with the
// default value.
//
RouterCacheSize = ROUTERCACHE_DEFAULT_MAX;
cbData = sizeof(dwCacheSize);
Status = RegQueryValueEx(hKey,
szRouterCacheSize,
NULL, NULL,
(LPBYTE)&dwCacheSize,
&cbData);
if (Status == ERROR_SUCCESS) {
DBGMSG(DBG_TRACE, ("RouterCacheSize = %d\n", dwCacheSize));
if (dwCacheSize > 0) {
RouterCacheSize = dwCacheSize;
}
}
if ((RouterCacheTable = AllocSplMem(RouterCacheSize *
sizeof(ROUTERCACHE))) == NULL) {
DBGMSG(DBG_ERROR, ("Error: Cannot create RouterCache Table\n"));
RouterCacheSize = 0;
}
//
// Now query szRegistryProvidors for the Order value
// if there is no Order value for szRegistryProvidors
// RegQueryValueEx will return ERROR_FILE_NOT_FOUND
// if that's the case, then quit, because we have
// no providors to initialize.
//
Status = RegQueryValueEx(hKey, szOrder, NULL, NULL,
(LPBYTE)NULL, &dwRequired);
//
// If RegQueryValueEx returned ERROR_SUCCESS, then
// call it again to determine how many bytes were
// allocated. Note, if Order does exist, but it has
// no data then dwReturned will be zero, in which
// don't allocate any memory for it, and don't
// bother to call RegQueryValueEx a second time.
//
if (Status == ERROR_SUCCESS) {
if (dwRequired != 0) {
lpMem = (LPWSTR) AllocSplMem(dwRequired);
if (lpMem == NULL) {
Status = GetLastError();
} else {
Status = RegQueryValueEx(hKey,
szOrder,
NULL,
NULL,
(LPBYTE)lpMem,
&dwRequired);
}
}
}
if (Status == ERROR_SUCCESS) {
cbDll = sizeof(Dll);
pProvidor = pLocalProvidor;
// Now parse the string retrieved from \Providors{Order = "....."}
// Remember each string is separated by a null terminator char ('\0')
// and the entire array is terminated by two null terminator chars
// Also remember, that if there was no data in Order, then
// psz = lpMem = NULL, and we have nothing to parse, so
// break out of the while loop, if psz is NULL as well
psz = lpMem;
while (psz && *psz) {
//
// Truncate the provider name if it does not fit in
// the stack allocated buffer.
//
lstrcpyn(ProvidorName, psz, COUNTOF(ProvidorName));
psz = psz + lstrlen(psz) + 1; // skip (length) + 1
// lstrlen returns length sans '\0'
if (RegOpenKeyEx(hKey, ProvidorName, 0, KEY_READ, &hKey1)
== ERROR_SUCCESS) {
cbDll = sizeof(Dll);
if (RegQueryValueEx(hKey1,
L"Name",
NULL,
NULL,
(LPBYTE)Dll,
&cbDll) == ERROR_SUCCESS)
{
if((StrNCatBuff(szFullName,
COUNTOF(szFullName),
szRegistryProvidors,
L"\\",
ProvidorName,
NULL)==ERROR_SUCCESS))
{
if (pProvidor->pNext = InitializeProvidor(Dll, szFullName))
{
pProvidor = pProvidor->pNext;
}
}
} //close RegQueryValueEx
RegCloseKey(hKey1);
} // closes RegOpenKeyEx on ERROR_SUCCESS
} // end of while loop parsing REG_MULTI_SZ
// Now free the buffer allocated for RegQuery
// (that is if you have allocated - if dwReturned was
// zero, then no memory was allocated (since none was
// required (Order was empty)))
if (lpMem) {
FreeSplMem(lpMem);
}
} // closes RegQueryValueEx on ERROR_SUCCESS
RegCloseKey(hKey);
}
//
// We are now initialized!
//
SetEvent(hEventInit);
Initialized=TRUE;
//
// Register for PnP events we care about
//
RegisterForPnPEvents();
bRet = SpoolerInitAll();
//
// Free the passed in router parameters.
//
FreeSplMem(pRouterParams);
// When we return this thread goes away
//
// NOTE-NOTE-NOTE-NOTE-NOTE KrishnaG 12/22/93
// This thread should go away, however the HP Monitor relies on this
// thread. HPMon calls the initialization function on this thread which
// calls an asynchronous receive for data. While the data itself is
// picked up by hmon!_ReadThread, if the thread which initiated the
// receive goes away, we will not be able to receive the data.
//
//
// Instead of sleeping infinite, let's use it to for providors that
// just want FFPCNs to poll. This call never returns.
//
HandlePollNotifications();
return bRet;
}
VOID
WaitForSpoolerInitialization(
VOID)
{
HANDLE hPhase2Init;
HANDLE hImpersonationToken = NULL;
if (!Initialized)
{
//
// Impersonate the spooler service token
//
hImpersonationToken = RevertToPrinterSelf();
//
// Start phase 2 initialization. hPhase2Init may set multiple times, but that
// is OK since there is only 1 thread waiting once on this event.
//
hPhase2Init = OpenEvent(EVENT_ALL_ACCESS,FALSE,L"RouterPreInitEvent");
if (hPhase2Init == NULL)
{
//
// Fail if the event is not created
//
DBGMSG(DBG_ERROR, ("Failed to create Phase2Init Event in WaitForSpoolerInitialization, error %d\n", GetLastError()));
ExitProcess(0);
}
SetEvent(hPhase2Init);
CloseHandle(hPhase2Init);
//
// Revert back to the client token
//
if (hImpersonationToken)
{
if (!ImpersonatePrinterClient(hImpersonationToken))
{
DBGMSG(DBG_ERROR, ("Failed to impersonate the client, error %d\n", GetLastError()));
ExitProcess(0);
}
}
WaitForSingleObject(hEventInit, INFINITE);
}
}
VOID
ShutDownProvidor(
LPPROVIDOR pProvidor)
{
if (pProvidor->PrintProvidor.fpShutDown) {
(*pProvidor->PrintProvidor.fpShutDown)(NULL);
}
FreeSplStr(pProvidor->lpName);
FreeLibrary(pProvidor->hModule);
FreeSplMem(pProvidor);
return;
}
VOID
SplShutDownRouter(
VOID
)
{
DBGMSG(DBG_TRACE, ("SplShutDownRouter:\n"));
//
// WMI Trace Events. Unregisters spoolss from WMI.
//
WmiTerminateTrace();
}
BOOL
SplInitializeWinSpoolDrv(
pfnWinSpoolDrv pfnList)
{
HANDLE hWinSpoolDrv;
// Check if the client side handles are available in fnClientSide
if (!bWinspoolInitialized) {
if (!(hWinSpoolDrv = LoadLibrary(TEXT("winspool.drv")))) {
// Could not load the client side of the spooler
return FALSE;
}
fnClientSide.pfnOpenPrinter = (BOOL (*)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS))
GetProcAddress( hWinSpoolDrv,"OpenPrinterW" );
fnClientSide.pfnClosePrinter = (BOOL (*)(HANDLE))
GetProcAddress( hWinSpoolDrv,"ClosePrinter" );
fnClientSide.pfnDocumentProperties = (LONG (*)(HWND, HANDLE, LPWSTR, PDEVMODE,
PDEVMODE, DWORD))
GetProcAddress( hWinSpoolDrv,"DocumentPropertiesW" );
fnClientSide.pfnDevQueryPrint = (BOOL (*)(HANDLE, LPDEVMODE, DWORD *, LPWSTR, DWORD))
GetProcAddress( hWinSpoolDrv,"SpoolerDevQueryPrintW" );
fnClientSide.pfnPrinterEvent = (BOOL (*)(LPWSTR, INT, DWORD, LPARAM, DWORD *))
GetProcAddress( hWinSpoolDrv,"SpoolerPrinterEvent" );
fnClientSide.pfnLoadPrinterDriver = (HANDLE (*)(HANDLE))
GetProcAddress( hWinSpoolDrv,
(LPCSTR)MAKELPARAM( 212, 0 ));
fnClientSide.pfnRefCntLoadDriver = (HANDLE (*)(LPWSTR, DWORD, DWORD, BOOL))
GetProcAddress( hWinSpoolDrv,
(LPCSTR)MAKELPARAM( 213, 0 ));
fnClientSide.pfnRefCntUnloadDriver = (BOOL (*)(HANDLE, BOOL))
GetProcAddress( hWinSpoolDrv,
(LPCSTR)MAKELPARAM( 214, 0 ));
fnClientSide.pfnForceUnloadDriver = (BOOL (*)(LPWSTR))
GetProcAddress( hWinSpoolDrv,
(LPCSTR)MAKELPARAM( 215, 0 ));
if ( fnClientSide.pfnOpenPrinter == NULL ||
fnClientSide.pfnClosePrinter == NULL ||
fnClientSide.pfnDocumentProperties == NULL ||
fnClientSide.pfnPrinterEvent == NULL ||
fnClientSide.pfnDevQueryPrint == NULL ||
fnClientSide.pfnLoadPrinterDriver == NULL ||
fnClientSide.pfnRefCntLoadDriver == NULL ||
fnClientSide.pfnRefCntUnloadDriver == NULL ||
fnClientSide.pfnForceUnloadDriver == NULL ) {
FreeLibrary(hWinSpoolDrv);
return FALSE;
}
// Use these pointers for future calls to SplInitializeWinspoolDrv
bWinspoolInitialized = TRUE;
}
pfnList->pfnOpenPrinter = fnClientSide.pfnOpenPrinter;
pfnList->pfnClosePrinter = fnClientSide.pfnClosePrinter;
pfnList->pfnDocumentProperties = fnClientSide.pfnDocumentProperties;
pfnList->pfnDevQueryPrint = fnClientSide.pfnDevQueryPrint;
pfnList->pfnPrinterEvent = fnClientSide.pfnPrinterEvent;
pfnList->pfnLoadPrinterDriver = fnClientSide.pfnLoadPrinterDriver;
pfnList->pfnRefCntLoadDriver = fnClientSide.pfnRefCntLoadDriver;
pfnList->pfnRefCntUnloadDriver = fnClientSide.pfnRefCntUnloadDriver;
pfnList->pfnForceUnloadDriver = fnClientSide.pfnForceUnloadDriver;
return TRUE;
}
BOOL
SpoolerHasInitialized(
VOID
)
{
return Initialized;
}
/*++
Routine Name
SplPowerEvent
Routine Description:
Checks if the spooler is ready for power management events like hibernation/stand by.
Arguments:
Event - power management event
Return Value:
TRUE - the spooler allowd the system to be powered down
FALSE - the spooler denies the request for powering down
--*/
BOOL
SplPowerEvent(
DWORD Event
)
{
BOOL bRet = TRUE;
//
// We need the router to be completely initialized and having loaded
// all print providers in order to check if we can allow powering down
// the system
//
if (SpoolerHasInitialized())
{
HMODULE hLib = NULL;
typedef BOOL (*PACPIFUNC)(DWORD);
PACPIFUNC pfn;
if ((hLib = LoadLibrary(L"localspl.dll")) &&
(pfn = (PACPIFUNC)GetProcAddress(hLib, "SplPowerEvent")))
{
bRet = (*pfn)(Event);
}
else
{
bRet = FALSE;
}
if (hLib)
{
FreeLibrary(hLib);
}
}
return bRet;
}