windows-nt/Source/XPSP1/NT/printscan/print/spooler/localspl/server.c
2020-09-26 16:20:57 +08:00

469 lines
12 KiB
C

/*++
Copyright (c) 1990 - 1995 Microsoft Corporation
Module Name:
server.c
Abstract:
Browsing
This module contains the thread for notifying all Printer Servers
Author:
Dave Snipp (DaveSn) 2-Aug-1992
Revision History:
--*/
#include <precomp.h>
#include <lm.h>
DWORD ServerThreadRunning = FALSE;
HANDLE ServerThreadSemaphore = NULL;
DWORD ServerThreadTimeout = TEN_MINUTES;
DWORD RefreshTimesPerDecayPeriod = DEFAULT_REFRESH_TIMES_PER_DECAY_PERIOD;
DWORD BrowsePrintWorkstations = DEFAULT_NUMBER_BROWSE_WORKSTATIONS;
BOOL bNetInfoReady = FALSE; // TRUE when the browse list is "valid"
#define NT_SERVER ( SV_TYPE_SERVER_NT | SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL )
extern FARPROC pfnNetServerEnum;
extern FARPROC pfnNetApiBufferFree;
DWORD
ServerThread(
PVOID
);
BOOL
UpdateServer(
LPCTSTR pszServer
);
BOOL
CreateServerThread(
VOID
)
{
HANDLE ThreadHandle;
DWORD ThreadId;
SplInSem();
if (!ServerThreadRunning) {
ServerThreadSemaphore = CreateEvent( NULL, FALSE, FALSE, NULL );
ThreadHandle = CreateThread( NULL, INITIAL_STACK_COMMIT,
(LPTHREAD_START_ROUTINE)ServerThread,
NULL,
0, &ThreadId );
if (!SetThreadPriority(ThreadHandle,
dwServerThreadPriority))
DBGMSG(DBG_WARNING, ("Setting thread priority failed %d\n",
GetLastError()));
ServerThreadRunning = TRUE;
CloseHandle( ThreadHandle );
}
if( ServerThreadSemaphore != NULL ){
// CreateServerThread is called each time a printer is shared out
// see net.c ShareThisPrinter.
// So if the ServerThread is sleeping wake prematurely so it can start
// to tell the world about this new shared printed.
SetEvent( ServerThreadSemaphore );
}
return TRUE;
}
DWORD
ServerThread(
PVOID pv
)
/*++
Routine Description:
Notify other machines in our domain about our shared printers.
We are going to have to enter and leave, revalidate, enter and leave our
semaphore inside the loop.
Arguments:
Return Value:
--*/
{
DWORD NoReturned, i, Total;
PSERVER_INFO_101 pserver_info_101;
PINIPRINTER pIniPrinter;
PINISPOOLER pIniSpooler;
DWORD ReturnValue=FALSE;
WCHAR ServerName[128];
DWORD StartTickCount;
DWORD TimeForAllServers;
DWORD dwActualWaitTime = ServerThreadTimeout;
UINT cPrintWorkstations;
UINT cPrintServers;
UINT cServersToInform;
UINT cWorkStationsToInform;
ServerName[0] = ServerName[1] = '\\';
while (TRUE) {
SplOutSem();
DBGMSG( DBG_TRACE, ("ServerThread sleeping for %d\n", dwActualWaitTime));
WaitForSingleObject( ServerThreadSemaphore, dwActualWaitTime );
// Wait for a couple of minutes more to avoid the boot time crunch
Sleep(TWO_MINUTES);
if ( !ServerThreadRunning ) {
return FALSE;
}
SPLASSERT( pfnNetServerEnum != NULL );
if (!(*pfnNetServerEnum)(NULL, 101, (LPBYTE *)&pserver_info_101, -1,
&NoReturned, &Total, SV_TYPE_PRINTQ_SERVER,
NULL, NULL)) {
EnterSplSem();
StartTickCount = GetTickCount();
//
// 1 Master + 3 Backup + 1 Backup per 32 Printer Servers.
//
cServersToInform = DEFAULT_NUMBER_MASTER_AND_BACKUP + NoReturned/32 ;
cWorkStationsToInform = BrowsePrintWorkstations;
//
// Count the NT Server and Workstation machines ( which have a printq )
//
for ( i = 0, cPrintServers = 0, cPrintWorkstations = 0;
i < NoReturned;
i++ ) {
if ( pserver_info_101[i].sv101_type & NT_SERVER ) {
cPrintServers++;
} else if ( pserver_info_101[i].sv101_type & SV_TYPE_NT ) {
cPrintWorkstations++;
}
}
//
// If there are no NT Servers to inform then up the number of Workstations
//
if ( cPrintServers == 0 ) {
cWorkStationsToInform = max( cWorkStationsToInform, cServersToInform );
cServersToInform = 0;
} else if ( cPrintServers < cServersToInform ) {
cWorkStationsToInform = max( cWorkStationsToInform, cServersToInform - cPrintServers );
}
DBGMSG( DBG_TRACE, ("ServerThread NetServerEnum returned %d printer servers will inform %d, workstations %d\n", NoReturned, cServersToInform, cWorkStationsToInform ));
//
// Loop Until we have informed the correct Number of WorkStations and Servers
//
for ( i = 0,
cPrintServers = 0,
cPrintWorkstations = 0;
i < NoReturned &&
( cPrintServers < cServersToInform || cPrintWorkstations < cWorkStationsToInform );
i++ ) {
DBGMSG( DBG_TRACE, ("ServerThread Loop Count %d cPrintServer %d cServersToInform %d cPrintWorkstations %d cWorkStationsToInform %d\n",
i, cPrintServers, cServersToInform, cPrintWorkstations, cWorkStationsToInform ));
DBGMSG( DBG_TRACE, ("ServerThread %ws type %x\n",pserver_info_101[i].sv101_name, pserver_info_101[i].sv101_type ));
if (( pserver_info_101[i].sv101_type & NT_SERVER ) ||
( pserver_info_101[i].sv101_type & SV_TYPE_NT && cPrintWorkstations < cWorkStationsToInform )) {
wcscpy(&ServerName[2], pserver_info_101[i].sv101_name);
if( UpdateServer( ServerName )){
// Servers are also counted as WorkStations
cPrintWorkstations++;
if ( pserver_info_101[i].sv101_type & NT_SERVER ) {
cPrintServers++;
}
}
}
}
TimeForAllServers = GetTickCount() - StartTickCount;
DBGMSG( DBG_TRACE, ("ServerThread took %d milliseconds for %d Workstations %d Servers\n",
TimeForAllServers, cPrintWorkstations, cPrintServers ));
//
// Calculate time to wait before we try again.
//
if ( NetPrinterDecayPeriod > TimeForAllServers ) {
dwActualWaitTime = max( ServerThreadTimeout, ( NetPrinterDecayPeriod - TimeForAllServers ) / RefreshTimesPerDecayPeriod );
} else {
dwActualWaitTime = ServerThreadTimeout;
}
//
// Remove WAS Shared Bits
//
//
// Do this for all spoolers.
//
for( pIniSpooler = pLocalIniSpooler;
pIniSpooler;
pIniSpooler = pIniSpooler->pIniNextSpooler ){
for ( pIniPrinter = pIniSpooler->pIniPrinter;
pIniPrinter != NULL;
pIniPrinter = pIniPrinter->pNext ) {
SplInSem();
pIniPrinter->Status &= ~PRINTER_WAS_SHARED;
}
}
LeaveSplSem();
(*pfnNetApiBufferFree)((LPVOID)pserver_info_101);
}
}
return FALSE;
}
typedef struct _UPDATE_SERVER_MAP_DATA {
LPCWSTR pszServer;
BOOL bSuccessfulAdd;
} UPDATE_SERVER_MAP_DATA, *PUPDATE_SERVER_MAP_DATA;
BOOL
UpdateServerPrinterMap(
HANDLE h,
PINIPRINTER pIniPrinter
)
/*++
Routine Description:
Update the a browser server with one pIniPrinter.
Leaves Spooler Section--pIniPrinter may be invalid on return
unless explicitly refcounted by callee.
Arguments:
pIniPrinter - Printer that should be sent to the server.
pszServer - Server that needs to be updated.
pbSuccessfulAdd - Indicates whether the add was successful.
Return Value:
Succes or failure?
--*/
{
PUPDATE_SERVER_MAP_DATA pData = (PUPDATE_SERVER_MAP_DATA)h;
WCHAR string[MAX_PRINTER_BROWSE_NAME];
WCHAR Name[MAX_UNC_PRINTER_NAME];
PRINTER_INFO_1 Printer1;
HANDLE hPrinter;
PINISPOOLER pIniSpooler;
DWORD dwLastError;
Printer1.Flags = 0;
if (( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) ||
( pIniPrinter->Status & PRINTER_WAS_SHARED )) {
//
// Pass our Printer Attributes so that AddNetPrinter can remove
// this printer from the browse list if it is not shared.
//
Printer1.Flags = pIniPrinter->Attributes | PRINTER_ATTRIBUTE_NETWORK;
wsprintf(string, L"%ws\\%ws,%ws,%ws",
pIniPrinter->pIniSpooler->pMachineName,
pIniPrinter->pName,
pIniPrinter->pIniDriver->pName,
pIniPrinter->pLocation ?
pIniPrinter->pLocation :
L"");
Printer1.pDescription = string;
wsprintf(Name, L"%ws\\%ws", pIniPrinter->pIniSpooler->pMachineName,
pIniPrinter->pName);
Printer1.pName = Name;
Printer1.pComment = AllocSplStr(pIniPrinter->pComment);
SplInSem();
LeaveSplSem();
//
// Keep trying until the server is not Too Busy.
//
for ( hPrinter = NULL;
hPrinter == NULL;
Sleep( GetTickCount() & 0xfff ) ) {
hPrinter = AddPrinter( (LPTSTR)pData->pszServer, 1, (LPBYTE)&Printer1 );
dwLastError = GetLastError();
if ( hPrinter == NULL && dwLastError != RPC_S_SERVER_TOO_BUSY ) {
if ( dwLastError != ERROR_PRINTER_ALREADY_EXISTS ) {
pData->bSuccessfulAdd = FALSE;
}
break;
}
}
FreeSplStr(Printer1.pComment);
if ( hPrinter != NULL ) {
DBGMSG( DBG_TRACE,
( "ServerThread AddPrinter(%ws, %ws) hPrinter %x Flags %x OK\n",
pData->pszServer, Printer1.pName, hPrinter, Printer1.Flags));
ClosePrinter( hPrinter );
}
EnterSplSem();
if ( hPrinter == NULL ) {
if ( GetLastError() != ERROR_PRINTER_ALREADY_EXISTS ) {
DBGMSG( DBG_TRACE,
( "ServerThread AddPrinter(%ws, 1) Flags %x failed %d\n",
pData->pszServer, Printer1.Flags, GetLastError()));
// Don't bother with this server if we get an error
return FALSE;
} else {
//
// 3.51 will return a NULL handle ( so it doesn't need closing
// and ERROR_PRINTER_ALREADY_EXISTS on success ( see printer.c addnetprinter )
//
DBGMSG( DBG_TRACE,
( "pszServerThread AddPrinter(%ws, %ws) hPrinter %x Flags %x OK\n",
pData->pszServer, Printer1.pName, hPrinter, Printer1.Flags));
}
}
}
return TRUE;
}
BOOL
UpdateServerSpoolerMap(
HANDLE h,
PINISPOOLER pIniSpooler
)
{
//
// Do this only for spoolers that want this "feature."
//
if( pIniSpooler->SpoolerFlags & SPL_SERVER_THREAD ){
RunForEachPrinter( pIniSpooler, h, UpdateServerPrinterMap );
}
return TRUE;
}
BOOL
UpdateServer(
LPCTSTR pszServer
)
/*++
Routine Description:
Update a server about all the printers on this node.
Arguments:
pszServer - Server to update in the form "\\server."
Return Value:
TRUE - Successfully added.
FALSE - Not.
--*/
{
UPDATE_SERVER_MAP_DATA Data;
Data.bSuccessfulAdd = TRUE;
Data.pszServer = pszServer;
RunForEachSpooler( &Data, UpdateServerSpoolerMap );
return Data.bSuccessfulAdd;
}