|
|
/*****************************************************************/ /** Copyright(c) 1989 Microsoft Corporation. **/ /*****************************************************************/
//***
//
// Filename: init.c
//
// Description: This module contains initialization code for the print
// monitor.
//
// In addition there are the ReadThread and the CaptureThread
// functions.
//
// The following are the functions contained in this module.
// All these functions are exported.
//
// LibMain
// InitializeMonitor
// ReadThread
// CaptureThread
//
//
// History:
//
// Aug 26,1992 frankb Initial version
// June 11,1993. NarenG Bug fixes/clean up
//
#include <windows.h>
#include <winspool.h>
#include <winsplp.h>
#include <winsock.h>
#include <atalkwsh.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lmcons.h>
#include <prtdefs.h>
#ifdef FE_SB
#include <locale.h>
#endif /* FE_SB */
#define ALLOCATE
#include "atalkmon.h"
#include "atmonmsg.h"
#include <bltrc.h>
#include "dialogs.h"
//**
//
// Call: LibMain
//
// Returns: TRUE - Success
// FALSE - Failure
//
// Description:
// This routine is called when a process attaches
// or detaches from the AppleTalk Monitor. On process attach,
// we save the module handle in the global hInst (we assume that
// only one process will attach to the monitor)
//
// On process detach, we free any system resources we've allocated.
//
BOOL LibMain( IN HANDLE hModule, IN DWORD dwReason, IN LPVOID lpRes ) {
UNREFERENCED_PARAMETER(lpRes);
switch(dwReason) { case DLL_PROCESS_ATTACH:
#ifdef FE_SB
setlocale( LC_ALL, "" ); #endif
//
// Save the instance handle
//
hInst = hModule; break;
case DLL_PROCESS_DETACH:
//
// Stop the Capture and I/O threads
//
boolExitThread = TRUE;
//
// Release global resources
//
if (hkeyPorts != NULL) RegCloseKey(hkeyPorts);
if (hevConfigChange != NULL) { SetEvent(hevConfigChange); CloseHandle(hevConfigChange); }
if (hevPrimeRead != NULL) { SetEvent(hevPrimeRead); CloseHandle(hevPrimeRead); }
if (hCapturePrinterThread != NULL) { WaitForSingleObject(hCapturePrinterThread, ATALKMON_DEFAULT_TIMEOUT);
CloseHandle(hCapturePrinterThread); }
if (hReadThread != NULL) { WaitForSingleObject(hReadThread, ATALKMON_DEFAULT_TIMEOUT);
CloseHandle(hReadThread); }
if (hmutexPortList != NULL) CloseHandle(hmutexPortList);
if (hmutexDeleteList != NULL) CloseHandle(hmutexDeleteList);
//
// Release Windows Sockets
//
WSACleanup(); break;
default: break; }
return(TRUE); }
//**
//
// Call: InitializeMonitor
//
// Returns: TRUE - Success
// FALSE - Failure
//
// Description:
// This routine is called when the spooler starts up.
// We allocate per port resources by reading the current port
// list from the registry.
//
BOOL InitializeMonitor( IN LPWSTR pszRegistryRoot ) { LPWSTR lpwsPortsKeyPath; DWORD dwRetCode = NO_ERROR; DWORD tid; DWORD RegFilter; DWORD dwValueType; DWORD dwDisposition; WSADATA WsaData; DWORD dwNameLen;
DBGPRINT (("sfmmon: InitializeMonitor: Entered Initialize Monitor\n"));
//
// Resource clean-up 'loop'
//
do { //
// Setup the event log
//
hEventLog = RegisterEventSource(NULL, ATALKMON_EVENT_SOURCE); lpwsPortsKeyPath = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*((wcslen(pszRegistryRoot)+1) + (wcslen(ATALKMON_PORTS_SUBKEY)+1))); if (lpwsPortsKeyPath == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break ; } wcscpy(lpwsPortsKeyPath, pszRegistryRoot); wcscat(lpwsPortsKeyPath, ATALKMON_PORTS_SUBKEY); //
// Open the ports key
//
if ((dwRetCode = RegCreateKeyEx( HKEY_LOCAL_MACHINE, lpwsPortsKeyPath, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkeyPorts, &dwDisposition)) != ERROR_SUCCESS) { DBGPRINT(("ERROR:Can't open Ports registry key %d\n",dwRetCode)); break ; }
//
// Query the filter option, if specified. By default it is on.
//
dwNameLen = sizeof(RegFilter); dwRetCode = RegQueryValueEx(hkeyPorts, ATALKMON_FILTER_VALUE, NULL, &dwValueType, (PUCHAR)&RegFilter, &dwNameLen); if (dwRetCode == 0) { Filter = (RegFilter != 0); } #ifdef DEBUG_MONITOR
{ HKEY hkeyAtalkmonRoot; HKEY hkeyOptions; LPWSTR pszLogPath = NULL ; DWORD cbLogPath = 0 ; if ((dwRetCode = RegCreateKeyEx( HKEY_LOCAL_MACHINE, pszRegistryRoot, 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyAtalkmonRoot, &dwDisposition)) != ERROR_SUCCESS) { break ; } //
// get Options subkey
//
if ((dwRetCode = RegCreateKeyEx( hkeyAtalkmonRoot, ATALKMON_OPTIONS_SUBKEY, 0, L"", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkeyOptions, &dwDisposition)) != ERROR_SUCCESS) { break ; } RegCloseKey(hkeyAtalkmonRoot) ; //
// setup the log file if we have one
//
RegQueryValueEx( hkeyOptions, ATALKMON_LOGFILE_VALUE, NULL, &dwValueType, (LPBYTE) pszLogPath, &cbLogPath) ; if (cbLogPath > 0) { pszLogPath = LocalAlloc(LPTR, cbLogPath * sizeof(WCHAR)) ; if (pszLogPath == NULL) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break ; } } if ((dwRetCode = RegQueryValueEx( hkeyOptions, ATALKMON_LOGFILE_VALUE, NULL, &dwValueType, (LPBYTE) pszLogPath, &cbLogPath)) == ERROR_SUCCESS) { //
// open the log file
//
hLogFile = CreateFile( pszLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL) ; } DBGPRINT(("ATALKMON LOG FLE OPENED\n\n")) ; } #endif
//
// initialize global variables
//
pPortList = NULL; pDeleteList = NULL;
if ((hmutexBlt = CreateMutex(NULL, FALSE, NULL)) == NULL) { dwRetCode = GetLastError(); break; }
if ((hmutexPortList = CreateMutex(NULL, FALSE, NULL)) == NULL) { dwRetCode = GetLastError(); break; }
if ((hmutexDeleteList = CreateMutex(NULL, FALSE, NULL)) == NULL) { dwRetCode = GetLastError(); break; }
//
// This event should be reset automatically and created signalled
// so that the config thread will capture printers on startup instead
// of waiting for the capture interval
//
if ((hevConfigChange = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) { dwRetCode = GetLastError(); DBGPRINT(("sfmmon: InitializeMonitor: Error in hevConfigChange creation\n")); break; } //
// This event should be reset automatically and created not signalled.
// StartDocPort will signal this event when a job is started, and
// WritePort() will signal the event anytime it wants to post another
// read on the job.
//
if ((hevPrimeRead = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) { dwRetCode = GetLastError(); DBGPRINT(("sfmmon: InitializeMonitor: Error in hevPrimeRead creation\n")); break ; } //
// Get the local computer's name.
//
dwNameLen = MAX_ENTITY+1; if (!GetComputerNameA(chComputerName, &dwNameLen)) { dwRetCode = GetLastError(); DBGPRINT(("sfmmon: InitializeMonitor: Error in GetComputerNameA call\n")); break; } strcat(chComputerName, ATALKMON_CAPTURED_TYPE); //
// initialize ports from registry
//
if ((dwRetCode = LoadAtalkmonRegistry(hkeyPorts)) != NO_ERROR) { ReportEvent( hEventLog, EVENTLOG_ERROR_TYPE, EVENT_CATEGORY_INTERNAL, EVENT_ATALKMON_REGISTRY_ERROR, NULL, 0, sizeof(DWORD), NULL, &dwRetCode); DBGPRINT(("sfmmon: InitializeMonitor: Error in LoadAtalkmonRegistry call\n")); break; } //
// Load and store status strings
//
if ((!LoadString(GetModuleHandle(TEXT("SFMMON")), IDS_BUSY, wchBusy, STATUS_BUFFER_SIZE)) || (!LoadString(GetModuleHandle(TEXT("SFMMON")), IDS_PRINTING, wchPrinting, STATUS_BUFFER_SIZE)) || (!LoadString(GetModuleHandle(TEXT("SFMMON")), IDS_PRINTER_OFFLINE, wchPrinterOffline, STATUS_BUFFER_SIZE)) || (!LoadString(GetModuleHandle(TEXT("SFMMON")), IDS_DLL_NAME, wchDllName, STATUS_BUFFER_SIZE)) || (!LoadString(GetModuleHandle(TEXT("SFMMON")), IDS_PORT_DESCRIPTION, wchPortDescription, STATUS_BUFFER_SIZE)) || (!LoadString(GetModuleHandle(TEXT("SFMMON")), IDS_ERROR, wchPrinterError, STATUS_BUFFER_SIZE))) { dwRetCode = GetLastError(); DBGPRINT(("sfmmon: InitializeMonitor: Error in LoadString SFMMON call\n")); break; } //
// Initialize Windows Sockets
//
if ((dwRetCode = WSAStartup(0x0101, &WsaData)) != NO_ERROR) { DBGPRINT(("WSAStartup fails with %d\n", dwRetCode)) ; ReportEvent( hEventLog, EVENTLOG_ERROR_TYPE, EVENT_CATEGORY_INTERNAL, EVENT_ATALKMON_WINSOCK_ERROR, NULL, 0, sizeof(DWORD), NULL, &dwRetCode); DBGPRINT(("sfmmon: InitializeMonitor: Error in WSAStartup call\n")); break; } //
// Start watchdog thread to keep printers captured
//
hCapturePrinterThread = CreateThread( NULL, 0, CapturePrinterThread, NULL, 0, &tid); if (hCapturePrinterThread == NULL) { dwRetCode = GetLastError(); DBGPRINT(("sfmmon: InitializeMonitor: Error in CapturePrinterThread call\n")); break ; } //
// Start an I/O thread to prime reads from
//
hReadThread = CreateThread( NULL, 0, ReadThread, NULL, 0, &tid); if (hReadThread == NULL) { dwRetCode = GetLastError(); DBGPRINT(("sfmmon: InitializeMonitor: Error in PrimeReadThreadcreation call\n")); break; } } while(FALSE); if (lpwsPortsKeyPath != NULL) LocalFree(lpwsPortsKeyPath); if (dwRetCode != NO_ERROR) { if (hkeyPorts != NULL) { RegCloseKey(hkeyPorts); hkeyPorts=NULL; } if (hevConfigChange != NULL) { CloseHandle(hevConfigChange); hevConfigChange=NULL; } if (hevPrimeRead != NULL) { CloseHandle(hevPrimeRead); hevPrimeRead=NULL; } if (hmutexPortList != NULL) { CloseHandle(hmutexPortList); hmutexPortList=NULL; } if (hmutexDeleteList != NULL) { CloseHandle(hmutexDeleteList); hmutexDeleteList=NULL; }
if (hmutexBlt != NULL) { CloseHandle(hmutexBlt); hmutexBlt=NULL; } ReportEvent( hEventLog, EVENTLOG_ERROR_TYPE, EVENT_CATEGORY_INTERNAL, EVENT_ATALKMON_REGISTRY_ERROR, NULL, 0, sizeof(DWORD), NULL, &dwRetCode);
DBGPRINT(("sfmmon: Initialize Monitor was unsuccessful\n")); return(FALSE); }
DBGPRINT(("sfmmon: Initialize Monitor was successful\n"));
return(TRUE); }
//**
//
// Call: CapturePrinterThread
//
// Returns:
//
// Description:
//
// This is the tread routine for the thread that monitors
// Appletalk printers to insure that they remain in the configured
// state (captured or not). It waits on an event with a timeout where
// the event is signalled whenever the configuration of an Appletalk
// printer is changed through the NT print manager. When the wait
// completes, it walks the list of known Appletalk printers and does
// an NBP lookup for the printer in the expected state. If the lookup
// fails, it does another lookup for the printer in the opposite state.
// If it finds the printer in the wrong state, it sends a job to change
// the NBP name of the printer.
//
// NOTE: The spooler recognizes when it has no printers configured
// to use a port, and calls ClosePort at that time. If someone
// creates a printer to use a port, it then calls OpenPort.
// Capturing of printers should only happen for Open ports,
// so we keep a status of the port state and only do captures
// on Open ports.
//
DWORD CapturePrinterThread( IN LPVOID pParameterBlock ) { PATALKPORT pWalker; BOOL fCapture; BOOL fIsSpooler; DWORD dwIndex; DWORD dwCount;
DBGPRINT(("Enter CapturePrinterThread\n")) ;
while (!boolExitThread) { //
// wait for timeout or a configuration change via ConfigPort.
// Also, this thread will post any reads for the monitor since
// asynch I/O must be handled by a thread that does not die, and
// the monitor threads are all RPC threads which are only
// guaranteed to be around for the duration of the function call.
//
DBGPRINT(("waiting for config event\n")) ;
WaitForSingleObject(hevConfigChange, CONFIG_TIMEOUT);
DBGPRINT(("config event or timeout occurs\n")) ;
//
// Delete and release ports that are pending delete.
//
do { WaitForSingleObject(hmutexDeleteList, INFINITE);
if (pDeleteList != NULL) { pWalker = pDeleteList; pDeleteList = pDeleteList->pNext; ReleaseMutex(hmutexDeleteList); } else { ReleaseMutex(hmutexDeleteList); break; }
//
// If this is a spooler don't bother.
//
if (!(pWalker->fPortFlags & SFM_PORT_IS_SPOOLER)) CapturePrinter(pWalker, FALSE);
FreeAppleTalkPort(pWalker); } while(TRUE);
//
// Recapture or rerelease printers that have been power cycled
//
WaitForSingleObject(hmutexPortList, INFINITE);
dwIndex = 0;
do { //
// Go to the ith element
//
for (dwCount = 0, pWalker = pPortList; ((pWalker != NULL) && (dwCount < dwIndex)); pWalker = pWalker->pNext, dwCount++) ; if (pWalker == NULL) { ReleaseMutex(hmutexPortList); break; } //
// Do not muck with the port if a job is using it
//
if (!(pWalker->fPortFlags & SFM_PORT_IN_USE) && ((pWalker->fPortFlags & SFM_PORT_OPEN) || (pWalker->fPortFlags & SFM_PORT_CLOSE_PENDING))) { fCapture = pWalker->fPortFlags & SFM_PORT_CAPTURED; fIsSpooler = pWalker->fPortFlags & SFM_PORT_IS_SPOOLER; if (pWalker->fPortFlags & SFM_PORT_CLOSE_PENDING) pWalker->fPortFlags &= ~SFM_PORT_CLOSE_PENDING; ReleaseMutex(hmutexPortList); //
// If this is a spooler do not muck with it
//
if (!fIsSpooler) { //
// Try to grab the port for capturing
//
if (WaitForSingleObject(pWalker->hmutexPort, 1) == WAIT_OBJECT_0) { CapturePrinter(pWalker, fCapture); ReleaseMutex(pWalker->hmutexPort); } } WaitForSingleObject(hmutexPortList, INFINITE); } dwIndex++; } while(TRUE); } return(NO_ERROR); }
//**
//
// Call: ReadThread
//
// Returns:
//
// Description:
//
DWORD ReadThread( IN LPVOID pParameterBlock ){
PATALKPORT pWalker;
//
// This thread goes 'till boolExitThread is set
//
while(!boolExitThread) { //
// wait for a signal to do I/O
// Wait here in an alertable fashion. This is needed so that the prime-read
// apc's can be delivered to us.
if (WaitForSingleObjectEx(hevPrimeRead, INFINITE, TRUE) == WAIT_IO_COMPLETION) continue;
DBGPRINT(("received signal to read/close\n")) ;
//
// for each port in our list
//
WaitForSingleObject(hmutexPortList, INFINITE);
for (pWalker = pPortList; pWalker != NULL; pWalker=pWalker->pNext) { if ((pWalker->fPortFlags & (SFM_PORT_IN_USE | SFM_PORT_POST_READ)) == (SFM_PORT_POST_READ | SFM_PORT_IN_USE)) {
DBGPRINT(("prime read for port %ws\n", pWalker->pPortName)) ;
setsockopt(pWalker->sockIo, SOL_APPLETALK, SO_PAP_PRIME_READ, pWalker->pReadBuffer, PAP_DEFAULT_BUFFER);
pWalker->fPortFlags &= ~SFM_PORT_POST_READ; } }
ReleaseMutex(hmutexPortList); }
return NO_ERROR; }
|