windows-nt/Source/XPSP1/NT/admin/netui/macprint/monitor/init.c

784 lines
15 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************/
/** 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;
}