/*++ 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 #include 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; }