/*****************************************************************/ /** 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 #include #include #include #include #include #include #include #include #include #ifdef FE_SB #include #endif /* FE_SB */ #define ALLOCATE #include "atalkmon.h" #include "atmonmsg.h" #include #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; }