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.
783 lines
16 KiB
783 lines
16 KiB
/*****************************************************************/
|
|
/** 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;
|
|
}
|
|
|
|
|