windows-nt/Source/XPSP1/NT/admin/netui/macprint/spooler/macps.c
2020-09-26 16:20:57 +08:00

2137 lines
49 KiB
C

////////////////////////////////////////////////////////////////////////////////
//
// MacPrint - Windows NT Print Server for Macintosh Clients
// Copyright (c) Microsoft Corp., 1991, 1992, 1993
//
// Author: Frank D. Byrum
// adapted from MacPrint for LAN Manager Services for Macintosh
// which was adapted from the 3Com product
//
////////////////////////////////////////////////////////////////////////////////
/*
File Name: MacPS.C
General Description:
This is the main module for the WNTSFM (Grimace) Print Service.
The main routine is registered with the NT Service Controller by the
Grimace Service Process, and is invoked when a SERVICE_START
request is received for MacPrint.
The routine immediately registers a service control handler
to field service control requests from the NT Service
Controller. It then retreives configuration information
from the registry and starts a thread for each configured
print queue to manage the print jobs for the queue.
Spooling is done by making each shared NT print queue appear
as a LaserWriter on the AppleTalk network. Each queue is
shared by using AppleTalk PAP to register the queue on
the AppleTalk network. Once the queue name is published, a
read driven loop is entered to support any connections/requests
from AppleTalk clients.
The flow of the Print Service threads is as follows:
main ()
NT Service Control dispatch thread for MacPrint.
MacPrintMain()
Registers a service control handler routine with
the NT Service Controller. If there is an error
registering the handler, MacPrintMain logs a
critical error message and returns. This indicates
to the NT Service Controller that MacPrint has
stopped.
Initializes per queue data structures based on
information from the NT Registry. For any queue
that data structures cannot be initialized, a
warning message is logged, and the control thread
for that queue is not started.
Spawns a thread for each queue that handles print
jobs for that queue
Enters a loop on a flag that is changed when a
service stop request is received. This loop traverses
the list of queues to see if they are still shared
by NT and enumerates the list of shared queues to
see if any new queues need to be published on
the AppleTalk network.
Each service thread:
Each service thread supports a single print queue
on the AppleTalk network.
It publishes the NBP name of the printer on the
AppleTalk network to allow Macintosh clients to
see the print queue from the Chooser.
It posts an ATalkPAPGetNextJob request to service
a print request. This allows Macintosh clients to
connect to the print queue.
It enters a service loop and remains there until
the service is stopped, or that particular queue
is 'unshared' by the NT Print Manager. This service
loop handles all states of a print job (OPEN, READ,
WRITE, CLOSE), and transfers data for the print job
to the NT Print Manager.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <winsvc.h>
#include <winspool.h>
#include <macps.h>
#include <macpsmsg.h>
#include <debug.h>
#define PRINTER_ENUM_BUFFER_SIZE 1024
#define MACPRINT_WAIT_HINT 25*60000 // 25 minutes
BOOL fLocalSystem;
SERVICE_STATUS MacPrintStatus;
/* MacPrintStatus is the global service status structure. It is
read by multiple threads and written ONLY by the service control
handler (it is initialized by MacPrintMain before the service
control handler is started) */
SERVICE_STATUS_HANDLE hMacPrintService;
/* hMacPrintService is the handle to the MacPrint service used in
calls to SetServiceStatus to change the state of the MacPrint
service. It is initialized when the service control handler is
created by MacPrintMain(), and is used only by MacPrintHandler() */
#if DBG
HANDLE hDumpFile = INVALID_HANDLE_VALUE;
#endif
HANDLE hLogFile = INVALID_HANDLE_VALUE;
HANDLE hEventLog = NULL;
ULONG ShareCheckInterval; // number of miliseconds between polls of the NT Print Manager
// to update our queue list
HANDLE mutexQueueList; // provide mutual exclusion to the linked list of active queues
// need to change to be a critical section
HANDLE mutexFlCache; // mutual exclusion for the failCache queue
HANDLE hevStopRequested; // event that is signalled when a stop request is received from
// the service controller. The main thread that dispatches queue
// threads waits on this event (with a timeout) to signal all
// queue threads to die.
HANDLE hevPnpWatch = NULL;
SOCKET sPnPSocket = INVALID_SOCKET;
BOOLEAN fNetworkUp = FALSE;
PQR pqrHead = NULL;
PFAIL_CACHE FlCacheHead = NULL;
//
// Function Prototypes for MacPS.c
//
VOID MacPrintMain(DWORD dwNumServicesArgs, LPTSTR * lpServiceArgs);
VOID UpdateQueueInfo(PQR * ppqrHead);
VOID MacPrintHandler(IN DWORD dwControl);
BOOLEAN PScriptQInit(PQR pqr, LPPRINTER_INFO_2 pPrinter);
PQR FindPrinter(LPPRINTER_INFO_2 pSearch, PQR pqrHead);
void ReadRegistryParameters(void);
#define IsRemote(pPrinter) (((pPrinter)->Attributes & PRINTER_ATTRIBUTE_NETWORK) ? TRUE : FALSE)
/* main()
Purpose:
This is the service control dispatcher thread. It connects
to the NT Service Controller and provides the mechanism to
start the MacPrint service.
Entry:
Standard C arguments that are ignored
Exit:
Exits on service stop with return of 0
*/
__cdecl
main(
int argc,
char ** argv
)
{
SERVICE_TABLE_ENTRY ServiceTable[2];
/* initialize the service table for MacPrint */
ServiceTable[0].lpServiceName = MACPRINT_NAME;
ServiceTable[0].lpServiceProc = &MacPrintMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
StartServiceCtrlDispatcher(ServiceTable);
return(0);
}
/* MacPrintMain()
Purpose:
This is the 'service main function' described in the NT
Service Control model, and it is invoked by the Service
Control Dispatcher for the MacPrint service. It initializes
data structures for the MacPrint service, registers a service
control handler, and dispatches threads to support print
queues shared on the AppleTalk network.
Entry:
dwNumServiceArgs: undefined
lpServiceArgs: undefined
The main function implements the standard service main function
interface, but uses no arguments. The standard arguments are
ignored.
Exit:
The routine returns no argument. It terminates when the MacPrint
service stops.
*/
#define ALLOCATED_QUEUE_MUTEX 0x00000001
#define ALLOCATED_SERVICE_STARTED 0x00000002
#define ALLOCATED_STOP_EVENT 0x00000004
VOID
MacPrintMain(
DWORD dwNumServicesArgs,
LPTSTR * lpServiceArgs
)
{
DWORD fAllocated = 0;
PQR pqr = NULL;
DWORD dwError;
WSADATA WsaData;
DWORD cbSizeNeeded;
HANDLE hProcessToken = INVALID_HANDLE_VALUE;
SID LocalSystemSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID };
BYTE TokenUserBuffer[100];
PFAIL_CACHE pFlCache, nextFlCache;
BOOLEAN fWatchingPnP = FALSE;
HANDLE EventsArray[MACSPOOL_MAX_EVENTS];
DWORD dwNumEventsToWatch=0;
DWORD dwWaitTime;
DWORD index;
TOKEN_USER * pTokenUser = (TOKEN_USER *)TokenUserBuffer;
do
{
//
// prepare the event log. If it doesn't register, there is nothing
// we can do anyway. All calls to ReportEvent will be with a NULL
// handle and will probably fail.
//
hEventLog = RegisterEventSource(NULL, MACPRINT_EVENT_SOURCE);
//
// Initialize global data
//
ReadRegistryParameters();
if ((hevStopRequested = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
{
dwError = GetLastError();
DBGPRINT(("ERROR: unable to create stop request event, error = %d\n", dwError));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_RESOURCES,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
break;
}
else
{
fAllocated |= ALLOCATED_STOP_EVENT;
}
if ((hevPnpWatch = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
{
dwError = GetLastError();
DBGPRINT(("ERROR: unable to create PnP event, error = %d\n", dwError));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_RESOURCES,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
break;
}
if ((mutexQueueList = CreateMutex(NULL, FALSE, NULL)) == NULL)
{
dwError = GetLastError();
DBGPRINT(("ERROR: Unable to create queue mutex object, error = %d\n",
dwError));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_RESOURCES,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
break;
}
else
{
fAllocated |= ALLOCATED_QUEUE_MUTEX;
}
if ((mutexFlCache = CreateMutex(NULL, FALSE, NULL)) == NULL)
{
dwError = GetLastError();
DBGPRINT(("ERROR: Unable to create FailCache mutex object, error = %d\n",
dwError));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_RESOURCES,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
break;
}
DBGPRINT(("\nMacPrint starting\n"));
//
// initialize Windows Sockets
//
if (WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
{
dwError = GetLastError();
DBGPRINT(("WSAStartup fails with %d\n", dwError));
break;
}
//
// register service control handler
//
MacPrintStatus.dwServiceType = SERVICE_WIN32;
MacPrintStatus.dwCurrentState = SERVICE_START_PENDING;
MacPrintStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
MacPrintStatus.dwWin32ExitCode = NO_ERROR;
MacPrintStatus.dwServiceSpecificExitCode = NO_ERROR;
MacPrintStatus.dwCheckPoint = 1;
MacPrintStatus.dwWaitHint = MACPRINT_WAIT_HINT;
hMacPrintService = RegisterServiceCtrlHandler(MACPRINT_NAME,&MacPrintHandler);
if (hMacPrintService == (SERVICE_STATUS_HANDLE) 0)
{
dwError = GetLastError();
DBGPRINT(("ERROR: failed to register service control handler, error=%d\n",dwError));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_CONTROLLER_ERROR,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
break;
}
//
// Determine if we are running in LocalSystem context
//
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
{
dwError = GetLastError();
DBGPRINT(("MacPrintMain: OpenProcessToken returns %d\n", dwError));
if (dwError == ERROR_ACCESS_DENIED)
fLocalSystem = FALSE;
else break;
}
if (!GetTokenInformation(hProcessToken,
TokenUser,
pTokenUser,
sizeof(TokenUserBuffer),
&cbSizeNeeded))
{
dwError = GetLastError();
DBGPRINT(("MacPrintMain:GetTokenInformation returns%d\n",dwError));
if (dwError == ERROR_INSUFFICIENT_BUFFER)
fLocalSystem = FALSE;
else break;
}
else
{
fLocalSystem = EqualSid(pTokenUser->User.Sid, &LocalSystemSid);
}
DBGPRINT(("MacPrintMain:fLocalSystem %d\n", fLocalSystem));
// Create a security object. This is really just a security descriptor
// is self-relative form. This procedure will allocate memory for this
// security descriptor and copy all in the information passed in. This
DBGPRINT(("MacPrintMain: registered service control handler\n"));
//
// show service started
//
MacPrintStatus.dwCurrentState = SERVICE_RUNNING;
if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
{
DBGPRINT(("MacPrintHandler: FAIL - unable to change state. err = %d\n", GetLastError()));
break;
}
DBGPRINT(("changed to SERVICE_RUNNING\n"));
fAllocated |= ALLOCATED_SERVICE_STARTED;
#if 0
ReportEvent(hEventLog,
EVENTLOG_INFORMATION_TYPE,
EVENT_CATEGORY_ADMIN,
EVENT_SERVICE_STARTED,
NULL,
0,
0,
NULL,
NULL);
#endif
EventsArray[MACSPOOL_EVENT_SERVICE_STOP] = hevStopRequested;
EventsArray[MACSPOOL_EVENT_PNP] = hevPnpWatch;
// poll print manager for install/removal of printer objects
while (MacPrintStatus.dwCurrentState == SERVICE_RUNNING)
{
if (!fNetworkUp)
{
dwNumEventsToWatch = 1;
fWatchingPnP = PostPnpWatchEvent();
if (fWatchingPnP)
{
dwNumEventsToWatch = 2;
}
}
//
// If network is available, publish all printers
//
if (fNetworkUp)
{
UpdateQueueInfo(&pqrHead);
dwWaitTime = ShareCheckInterval;
}
// looks like network is still not available: try after 10 seconds
else
{
dwWaitTime = 10000;
}
//
// "sleep" for the specified event. During that time, watch for
// PnP event or the service stopping
//
index = WaitForMultipleObjectsEx(dwNumEventsToWatch,
EventsArray,
FALSE,
dwWaitTime,
FALSE);
if (index == MACSPOOL_EVENT_PNP)
{
HandlePnPEvent();
}
}
} while (FALSE);
//
// wait for all worker threads to die
//
if (fAllocated & ALLOCATED_QUEUE_MUTEX)
{
while (pqrHead != NULL)
{
MacPrintStatus.dwCheckPoint++;
SetServiceStatus(hMacPrintService, &MacPrintStatus);
Sleep(100);
}
CloseHandle(mutexQueueList);
}
// if there were any entries in the failed cache, free them now
for ( pFlCache=FlCacheHead; pFlCache != NULL; pFlCache = nextFlCache )
{
nextFlCache = pFlCache->Next;
LocalFree( pFlCache );
}
if (mutexFlCache != NULL)
{
CloseHandle(mutexFlCache);
}
if (sPnPSocket != INVALID_SOCKET)
{
closesocket(sPnPSocket);
sPnPSocket = INVALID_SOCKET;
}
if (hevPnpWatch)
{
CloseHandle(hevPnpWatch);
}
//
// disconnect from Windows Sockets
//
WSACleanup();
//
// change service state to stopped
//
MacPrintStatus.dwCurrentState = SERVICE_STOPPED;
if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
{
DBGPRINT(("ERROR: unable to change status to SERVICE_STOPPED.%d\n",
GetLastError()));
}
else
{
DBGPRINT(("changed state to SERVICE_STOPPED\n"));
#if 0
ReportEvent(hEventLog,
EVENTLOG_INFORMATION_TYPE,
EVENT_CATEGORY_ADMIN,
EVENT_SERVICE_STOPPED,
NULL,
0,
0,
NULL,
NULL);
#endif
}
if (hProcessToken != INVALID_HANDLE_VALUE)
CloseHandle(hProcessToken);
if (fAllocated & ALLOCATED_STOP_EVENT)
{
CloseHandle(hevStopRequested);
}
if (hLogFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hLogFile);
}
#if DBG
if (hDumpFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hDumpFile);
}
#endif
}
BOOLEAN
PostPnpWatchEvent(
VOID
)
{
SOCKADDR_AT address;
DWORD dwResult;
DWORD dwByteCount;
//
// we must always start with a "fresh" socket!
//
if (sPnPSocket != INVALID_SOCKET)
{
DBGPRINT(("SFMPRINT: sPnPSocket wasn't closed!! Closing now\n"));
closesocket(sPnPSocket);
sPnPSocket = INVALID_SOCKET;
}
sPnPSocket = socket(AF_APPLETALK, SOCK_RDM, ATPROTO_PAP);
if (sPnPSocket == INVALID_SOCKET)
{
DBGPRINT(("PostPnpWatchEvent: socket failed %d\n",GetLastError()));
return(FALSE);
}
address.sat_family = AF_APPLETALK;
address.sat_net = 0;
address.sat_node = 0;
address.sat_socket = 0;
if (bind(sPnPSocket, (PSOCKADDR) &address, sizeof(address)) == SOCKET_ERROR)
{
DBGPRINT(("PostPnpWatchEvent: bind failed %d\n",GetLastError()));
closesocket(sPnPSocket);
sPnPSocket = INVALID_SOCKET;
return(FALSE);
}
if (WSAEventSelect(sPnPSocket,
hevPnpWatch,
(FD_READ | FD_ADDRESS_LIST_CHANGE)) == SOCKET_ERROR)
{
DBGPRINT(("PostPnpWatchEvent: WSAEventSelect failed %d\n",GetLastError()));
closesocket(sPnPSocket);
sPnPSocket = INVALID_SOCKET;
return(FALSE);
}
dwResult = WSAIoctl(sPnPSocket,
SIO_ADDRESS_LIST_CHANGE,
NULL,
0,
NULL,
0,
&dwByteCount,
NULL,
NULL);
if (dwResult == SOCKET_ERROR)
{
dwResult = GetLastError();
if (dwResult != WSAEWOULDBLOCK)
{
DBGPRINT(("PostPnpWatchEvent: WSAIoctl failed %d\n",dwResult));
closesocket(sPnPSocket);
sPnPSocket = INVALID_SOCKET;
return(FALSE);
}
}
fNetworkUp = TRUE;
return(TRUE);
}
BOOLEAN
HandlePnPEvent(
VOID
)
{
DWORD dwErr;
WSANETWORKEVENTS NetworkEvents;
dwErr = WSAEnumNetworkEvents(sPnPSocket, hevPnpWatch, &NetworkEvents);
if (dwErr != NO_ERROR)
{
DBGPRINT(("HandlePnPEvent: WSAEnumNetworkEvents failed %d\n",dwErr));
return(fNetworkUp);
}
if (NetworkEvents.lNetworkEvents & FD_ADDRESS_LIST_CHANGE)
{
dwErr = NetworkEvents.iErrorCode[FD_ADDRESS_LIST_CHANGE_BIT];
if (dwErr != NO_ERROR)
{
DBGPRINT(("HandlePnPEvent: iErrorCode is %d\n",dwErr));
return(fNetworkUp);
}
}
if (fNetworkUp)
{
SetEvent(hevStopRequested);
// sleep till all the threads quit
while (pqrHead != NULL)
{
Sleep(500);
}
ResetEvent(hevStopRequested);
fNetworkUp = FALSE;
}
return(fNetworkUp);
}
////////////////////////////////////////////////////////////////////////////////
// ReadRegistryParameters()
//
// DESCRIPTION: This routine reads all configuration parameters from
// the registry and modifies global variables to make those parameters
// available to the rest of the service. They include:
//
// ShareCheckInterval
// hLogFile
// hDumpFile
////////////////////////////////////////////////////////////////////////////////
void
ReadRegistryParameters(
void
)
{
HKEY hkeyMacPrintRoot = INVALID_HANDLE_VALUE;
HKEY hkeyParameters = INVALID_HANDLE_VALUE;
LONG Status;
DWORD cbShareCheckInterval = sizeof(DWORD);
LPWSTR pszLogPath = NULL;
DWORD cbLogPath = 0;
LPWSTR pszDumpPath = NULL;
DWORD cbDumpPath = 0;
DWORD dwValueType;
DWORD dwError;
//
// resource allocation 'loop'
//
do
{
//
// initialize to defaults
//
ShareCheckInterval = PRINT_SHARE_CHECK_DEF;
#if DBG
#ifndef _WIN64
hLogFile = (HANDLE)STD_OUTPUT_HANDLE;
#endif
hDumpFile = INVALID_HANDLE_VALUE;
#endif
//
// Open the service control key
//
if ((Status = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
HKEY_MACPRINT,
0,
KEY_READ,
&hkeyMacPrintRoot)) != ERROR_SUCCESS)
{
dwError = GetLastError();
if (dwError == ERROR_ACCESS_DENIED)
{
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_REGISTRY_ACCESS_DENIED,
NULL,
0,
0,
NULL,
NULL);
}
else
{
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_REGISTRY_ERROR,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
}
hkeyMacPrintRoot = INVALID_HANDLE_VALUE;
break;
}
//
// Open the parameters key
//
if ((Status = RegOpenKeyEx(
hkeyMacPrintRoot,
HKEY_PARAMETERS,
0,
KEY_READ,
&hkeyParameters)) != ERROR_SUCCESS)
{
dwError = GetLastError();
if (dwError == ERROR_ACCESS_DENIED)
{
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_REGISTRY_ACCESS_DENIED,
NULL,
0,
0,
NULL,
NULL);
}
hkeyParameters = INVALID_HANDLE_VALUE;
break;
}
//
// get the share check interval
//
RegQueryValueEx(
hkeyParameters,
HVAL_SHARECHECKINTERVAL,
NULL,
&dwValueType,
(LPBYTE) &ShareCheckInterval,
&cbShareCheckInterval);
#if DBG
//
// get the log file path
//
RegQueryValueEx(hkeyParameters,
HVAL_LOGFILE,
NULL,
&dwValueType,
(LPBYTE) pszLogPath,
&cbLogPath);
if (cbLogPath > 0)
{
// cbLogPath is really a count of characters
pszLogPath = (LPWSTR)LocalAlloc(LPTR, (cbLogPath + 1) * sizeof(WCHAR));
if (pszLogPath == NULL)
{
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_MEMORY,
NULL,
0,
0,
NULL,
NULL);
break;
}
}
if ((Status = RegQueryValueEx(hkeyParameters,
HVAL_LOGFILE,
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);
if (hLogFile == INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_CREATE_FILE_ERROR,
NULL,
1,
sizeof(DWORD),
&pszLogPath,
&dwError);
}
else
{
ReportEvent(hEventLog,
EVENTLOG_INFORMATION_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_CREATE_LOG_FILE,
NULL,
1,
0,
&pszLogPath,
NULL);
}
}
else
{
hLogFile = INVALID_HANDLE_VALUE;
}
DBGPRINT(("MACPRINT LOG FLE OPENED\n\n"));
//
// get the dump file path
//
RegQueryValueEx(hkeyParameters,
HVAL_DUMPFILE,
NULL,
&dwValueType,
(LPBYTE) pszDumpPath,
&cbDumpPath);
if (cbDumpPath > 0)
{
// cbDumpPath is really a count of characters
pszDumpPath = (LPWSTR)LocalAlloc(LPTR, (cbDumpPath + 1) * sizeof(WCHAR));
if (pszDumpPath == NULL)
{
DBGPRINT(("ERROR: cannot allocate buffer for dump file path\n"));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_MEMORY,
NULL,
0,
0,
NULL,
NULL);
break;
}
}
if ((Status = RegQueryValueEx(hkeyParameters,
HVAL_DUMPFILE,
NULL,
&dwValueType,
(LPBYTE) pszDumpPath,
&cbDumpPath)) != ERROR_SUCCESS)
{
DBGPRINT(("ERROR: no dump path, rc = 0x%lx\n", Status));
}
else
{
//
// open the dump file
//
hDumpFile = CreateFile(pszDumpPath,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
#endif
} while (FALSE);
//
// resource cleanup
//
if (hkeyParameters != INVALID_HANDLE_VALUE)
{
RegCloseKey(hkeyParameters);
}
if (hkeyMacPrintRoot != INVALID_HANDLE_VALUE)
{
RegCloseKey(hkeyMacPrintRoot);
}
#if DBG
if (pszLogPath != NULL)
{
LocalFree (pszLogPath);
}
if (pszDumpPath != NULL)
{
LocalFree (pszDumpPath);
}
#endif
}
////////////////////////////////////////////////////////////////////////////////
// FindPrinter() - locate a printer in our list of printers
//
// DESCRIPTION:
//
// Given an NT printer information structure and a pointer to the head of
// a list of our printer structures, this routine will return a pointer
// to our printer structure that corresponds to the printer described by
// the NT printer information structure. If no such printer is found in
// our list, this routine returns NULL.
//
////////////////////////////////////////////////////////////////////////////////
PQR
FindPrinter(
LPPRINTER_INFO_2 pSearch,
PQR pqrHead
)
{
PQR status = NULL;
PQR pqrCurrent;
for (pqrCurrent = pqrHead; pqrCurrent != NULL; pqrCurrent = pqrCurrent->pNext)
{
if (_wcsicmp(pSearch->pPrinterName, pqrCurrent->pPrinterName) == 0)
{
return (pqrCurrent);
}
}
return (NULL);
}
////////////////////////////////////////////////////////////////////////////////
//
// UpdateQueueInfo() - get new list of printers from NT
//
// DESCRIPTION:
// This routine is called periodically to see if any new NT Printer Objects
// have been created or if any old ones have been destroyed since the last
// time this routine was called. For each new printer object discovered,
// a thread is started to manage that printer object. For each printer object
// destroyed, the thread corresponding to that printer object is signalled
// to quit.
//
// This routine takes a pointer to the head of a list of printers and
// makes certain that the list corresponds to the set of currently
// defined NT Printer Objects.
//
////////////////////////////////////////////////////////////////////////////////
#define ALLOCATED_RELEASE_MUTEX 0x00000001
#define ALLOCATED_ENUM_BUFFER 0x00000002
VOID
UpdateQueueInfo(
PQR * ppqrHead
)
{
DWORD fAllocated = 0;
PQR pqrCurrent;
PQR pqrTemp;
DWORD i;
DWORD dwThreadId;
DWORD cbNeeded = 0;
DWORD cPrinters = 0;
LPBYTE pPrinters = NULL;
LPPRINTER_INFO_2 pinfo2Printer;
HANDLE ahWaitList[MAXIMUM_WAIT_OBJECTS];
DWORD dwReturn;
DWORD dwError;
BOOLEAN boolEnumOK = TRUE;
// DBGPRINT(("Entering UpdateQueueInfo\n"));
do
{
//
// take the QueueList mutex
//
if (WaitForSingleObject(mutexQueueList, INFINITE) == 0)
{
fAllocated |= ALLOCATED_RELEASE_MUTEX;
// DBGPRINT(("UpdateQueueInfo takes mutexQueueList\n"));
}
else
{
//
// fatal error - log a message and stop the service
//
DBGPRINT(("ERROR: problem waiting for queue list mutex, error = %d\n", GetLastError()));
dwReturn = 0;
break;
}
//
// Mark all the queues NOT FOUND
//
for (pqrCurrent = *ppqrHead;
pqrCurrent != NULL;
pqrCurrent = pqrCurrent->pNext)
{
pqrCurrent->bFound = FALSE;
}
// DBGPRINT(("queues marked not found\n"));
//
// Enumerate the local printers
//
cPrinters = 0;
if ((pPrinters = (LPBYTE)LocalAlloc(LPTR, PRINTER_ENUM_BUFFER_SIZE)) != NULL)
{
fAllocated |= ALLOCATED_ENUM_BUFFER;
}
else
{
//
// out of resources - let service continue running
//
DBGPRINT(("ERROR: unable to allocated buffer for printer enum. error = %d\n", GetLastError()));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_MEMORY,
NULL,
0,
0,
NULL,
NULL);
dwReturn = 1;
break;
}
dwReturn = 0;
cbNeeded = PRINTER_ENUM_BUFFER_SIZE;
while (!EnumPrinters(PRINTER_ENUM_SHARED | PRINTER_ENUM_LOCAL,
NULL,
2,
pPrinters,
cbNeeded,
&cbNeeded,
&cPrinters))
{
//
// enum failed - allocate more data if we need it, or fail
//
if ((dwError = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
{
//
// the NT spooler is probably dead - stop the service
//
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SPOOLER_NOT_RESPONDING,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
DBGPRINT (("ERROR: Unable to enumerate printers, error = %d\n",dwError));
boolEnumOK = FALSE;
dwReturn = 0;
break;
}
//
// allocate a larger buffer
//
LocalFree(pPrinters);
cPrinters = 0;
if ((pPrinters = (LPBYTE)LocalAlloc(LPTR, cbNeeded)) == NULL)
{
//
// out of resources, see if service will continue to run
//
fAllocated &= ~ALLOCATED_ENUM_BUFFER;
dwError = GetLastError();
DBGPRINT(("ERROR: unable to reallocate printer enum buffer, error = %d\n",dwError));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_MEMORY,
NULL,
0,
0,
NULL,
NULL);
boolEnumOK = FALSE;
dwReturn = 1;
break;
}
}
if (!boolEnumOK)
{
break;
}
// DBGPRINT(("%d printers enumerated\n", cPrinters));
//
// For each LOCAL_PRINTER, attempt to find it in the
// queue list and change its status to FOUND. If it
// can't be found in the list that add it and start
// a service thread for it
//
dwReturn = 1;
for (i = 0, pinfo2Printer = (LPPRINTER_INFO_2)pPrinters;
i < cPrinters;
i++, pinfo2Printer++)
{
//
// do not count pending_deletion printers as found
//
if (MacPrintStatus.dwCurrentState != SERVICE_RUNNING)
{
DBGPRINT(("Service stopping: quitting UpdateQueueInfo\n"));
break;
}
if (pinfo2Printer->Status & PRINTER_STATUS_PENDING_DELETION)
continue;
pqrCurrent = FindPrinter(pinfo2Printer,*ppqrHead);
if ((pqrCurrent != NULL) &&
(_wcsicmp(pqrCurrent->pDriverName, pinfo2Printer->pDriverName) == 0))
{
//
// printer already going, mark it found
//
pqrCurrent->bFound = TRUE;
}
else
{
//
// start a new thread, but first make sure we are still running
//
DBGPRINT(("Discovered new printer, starting thread\n"));
//
// allocate a new queue record
//
if ((pqrCurrent = (PQR)LocalAlloc(LPTR, sizeof(QUEUE_RECORD))) == NULL)
{
//
// out of memory, but can still enumerate printers, so don't stop
// the service, just quit
//
DBGPRINT(("ERROR: cannont allocate queue record, error = %d\n", GetLastError()));
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_MEMORY,
NULL,
0,
0,
NULL,
NULL);
dwReturn = 1;
break;
}
//
// initialize it
//
if (!PScriptQInit(pqrCurrent, pinfo2Printer))
{
LocalFree (pqrCurrent);
continue;
}
//
// add it to the head of the list
//
pqrCurrent->pNext = *ppqrHead;
*ppqrHead = pqrCurrent;
//
// start a service thread for the queue
//
if ((pqrCurrent->hThread = CreateThread(NULL,
STACKSIZE,
(LPTHREAD_START_ROUTINE)QueueServiceThread,
(LPVOID)pqrCurrent,
0,
&dwThreadId)) == 0)
{
DBGPRINT(("ERROR: unable to start thread routine for %ws\n", pqrCurrent->pPrinterName));
dwError = GetLastError();
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_RESOURCES,
NULL,
0,
sizeof(DWORD),
NULL,
&dwError);
*ppqrHead = pqrCurrent->pNext;
// BUG BUG - memory leak (pqrCurrent->pszXXXX) if can't start threads.
LocalFree(pqrCurrent);
} // end of CreateThread()
} //end of discovering a new printer
} // loop walking list of printers
//
// Walk the list of queues for NOT_FOUND ones and signal
// the service thread for that queue to terminate. Each
// thread will remove itself from the queue list and free
// its own queue entry.
//
// DBGPRINT(("removing lost printers\n"));
pqrCurrent = *ppqrHead;
i = 0;
while (pqrCurrent != NULL)
{
//
// get the address of the next queue record and signal this
// queue record to terminate if necessary. Must save the
// address before requesting termination as once ExitThread
// is set to TRUE, the data structure is no longer accessible
// (the queue thread could free it)
//
pqrTemp = pqrCurrent->pNext;
if (!pqrCurrent->bFound)
{
// terminate the thread
DBGPRINT(("signalling %ws to terminate\n", pqrCurrent->pPrinterName));
ahWaitList[i++] = pqrCurrent->hThread;
pqrCurrent->ExitThread = TRUE;
}
pqrCurrent = pqrTemp;
if (i==MAXIMUM_WAIT_OBJECTS)
{
//
// release the queue mutex so that threads can remove
// themselves from the queue list. The list beyond
// pqrCurrent should not be modified, so it's ok to
// continue from there when we're done
//
ReleaseMutex(mutexQueueList);
WaitForMultipleObjects(i, ahWaitList, TRUE, INFINITE);
// take the mutex again and continue
WaitForSingleObject(mutexQueueList, INFINITE);
i = 0;
}
} // end of walking the queue list for not found printers
//
// wait for all remaining worker threads to die
//
ReleaseMutex(mutexQueueList);
fAllocated &= ~ALLOCATED_RELEASE_MUTEX;
if (i > 0)
{
DBGPRINT(("waiting for terminated queues to die\n"));
WaitForMultipleObjects(i, ahWaitList, TRUE, INFINITE);
}
dwReturn = 1;
} while (FALSE);
//
// resource cleanup
//
if (fAllocated & ALLOCATED_RELEASE_MUTEX)
{
// DBGPRINT(("UpdateQueueInfo releases mutexQueueList\n"));
ReleaseMutex(mutexQueueList);
}
if (fAllocated & ALLOCATED_ENUM_BUFFER)
{
LocalFree(pPrinters);
}
if (dwReturn == 0)
{
//
// unrecoverable error - stop the service
//
MacPrintStatus.dwCurrentState = SERVICE_STOP_PENDING;
if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
{
DBGPRINT(("UpdateQueueInfo: FAIL - unable to change state. err = %ld\n", GetLastError()));
}
else
{
DBGPRINT(("UpdateQueueInfo: changed to SERVICE_STOP_PENDING\n"));
}
SetEvent(hevStopRequested);
}
}
////////////////////////////////////////////////////////////////////////////////
//
// MacPrintHandler() - handles service control requests
//
// DESCRIPTION:
// This routine receives and processes service requests from the NT
// Service Controller. Supported requests include:
//
// SERVICE_CONTROL_STOP
// SERVICE_CONTROL_INTERROGATE
//
// dwControl is the service control request.
////////////////////////////////////////////////////////////////////////////////
VOID
MacPrintHandler (
IN DWORD dwControl
)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
DBGPRINT(("MacPrintHandler: received SERVICE_CONTROL_STOP\n"));
MacPrintStatus.dwCurrentState = SERVICE_STOP_PENDING;
MacPrintStatus.dwCheckPoint = 1;
MacPrintStatus.dwWaitHint = MACPRINT_WAIT_HINT;
if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
{
DBGPRINT(("MacPrintHandler: FAIL - unable to change state. err = %ld\n", GetLastError()));
}
else
{
DBGPRINT(("changed to SERVICE_STOP_PENDING\n"));
}
SetEvent(hevStopRequested);
break;
case SERVICE_CONTROL_INTERROGATE:
DBGPRINT(("MacPrintHandler: received SERVICE_CONTROL_INTERROGATE\n"));
if (!SetServiceStatus(hMacPrintService, &MacPrintStatus))
{
DBGPRINT(("MacPrintHandler: FAIL - unable to report state. err = %ld\n", GetLastError()));
}
else
{
DBGPRINT(("returned status on interrogate\n"));
}
break;
}
}
////////////////////////////////////////////////////////////////////////////////
//
// PScriptQInit() - Initialize a Queue Record
//
// DESCRIPTION:
// This routine initializes a queue record with the PostScript
// capabilities of an NT Printer Object as well as allocates
// the events and system resources necessary to control the
// queue.
//
////////////////////////////////////////////////////////////////////////////////
BOOLEAN
PScriptQInit(
PQR pqr,
LPPRINTER_INFO_2 pPrinter
)
{
BOOLEAN status = TRUE;
PDRIVER_INFO_2 padiThisPrinter = NULL;
DWORD cbDriverInfoBuffer;
HANDLE hPrinter = NULL;
DWORD cbDataFileName;
DWORD rc;
//
// resource allocation 'loop'
//
do
{
pqr->bFound = TRUE;
pqr->hThread = NULL;
pqr->JobCount = 0; // No jobs yet.
pqr->PendingJobs = NULL; // Set pending jobs to none.
pqr->ExitThread = FALSE; // Set thread control.
pqr->sListener = INVALID_SOCKET;
pqr->fonts = NULL;
pqr->MaxFontIndex = 0;
pqr->pPrinterName = NULL;
pqr->pMacPrinterName = NULL;
pqr->pDriverName = NULL;
pqr->IdleStatus = NULL;
pqr->SpoolingStatus = NULL;
//
// convert printer name to Mac ANSI
//
#ifdef DBCS
pqr->pMacPrinterName = (LPSTR)LocalAlloc(LPTR, (wcslen(pPrinter->pPrinterName) + 1) * sizeof(WCHAR));
#else
pqr->pMacPrinterName = (LPSTR)LocalAlloc(LPTR, wcslen(pPrinter->pPrinterName) + 1);
#endif
if (pqr->pMacPrinterName == NULL)
{
DBGPRINT(("out of memory for pMacPrinterName\n"));
status = FALSE;
break;
}
CharToOem(pPrinter->pPrinterName, pqr->pMacPrinterName);
pqr->pPrinterName = (LPWSTR)LocalAlloc(LPTR,
(wcslen(pPrinter->pPrinterName) + 1) * sizeof(WCHAR));
if (pqr->pPrinterName == NULL)
{
DBGPRINT(("out of memory for pPrinterName\n"));
status = FALSE;
break;
}
wcscpy (pqr->pPrinterName, pPrinter->pPrinterName);
pqr->pDriverName = (LPWSTR)LocalAlloc(LPTR,
(wcslen(pPrinter->pDriverName) + 1) * sizeof(WCHAR));
if (pqr->pDriverName == NULL)
{
DBGPRINT(("out of memory for pDriverName\n"));
status = FALSE;
break;
}
wcscpy (pqr->pDriverName, pPrinter->pDriverName);
pqr->pPortName = (LPWSTR)LocalAlloc(LPTR,
(wcslen(pPrinter->pPortName) + 1) * sizeof(WCHAR));
if (pqr->pPortName == NULL)
{
DBGPRINT(("out of memory for pPortName\n"));
status = FALSE;
break;
}
wcscpy (pqr->pPortName, pPrinter->pPortName);
//
// determine the datatype to use
//
if (!OpenPrinter(pqr->pPrinterName, &hPrinter, NULL))
{
status = FALSE;
DBGPRINT(("ERROR: OpenPrinter() fails with %d\n", GetLastError()));
break;
}
//
// start with a guess as to size of driver info buffer
//
cbDriverInfoBuffer = 2*sizeof(DRIVER_INFO_2);
padiThisPrinter = (PDRIVER_INFO_2)LocalAlloc(LPTR, cbDriverInfoBuffer);
if (padiThisPrinter == NULL)
{
status = FALSE;
DBGPRINT(("ERROR: LocalAlloc() failed for padiThisPrinter\n"));
break;
}
if (!GetPrinterDriver(hPrinter,
NULL,
2,
(LPBYTE)padiThisPrinter,
cbDriverInfoBuffer,
&cbDriverInfoBuffer))
{
rc = GetLastError();
DBGPRINT(("WARNING: first GetPrinterDriver call fails with %d\n", rc));
LocalFree(padiThisPrinter);
padiThisPrinter = NULL;
if (rc != ERROR_INSUFFICIENT_BUFFER)
{
status = FALSE;
break;
}
//
// failed with buffer size error. Reallocate and retry
//
padiThisPrinter = (PDRIVER_INFO_2)LocalAlloc(LPTR, cbDriverInfoBuffer);
if (padiThisPrinter == NULL)
{
status = FALSE;
DBGPRINT(("out of memory for second padiThisPrinter\n"));
break;
}
if (!GetPrinterDriver(hPrinter,
NULL,
2,
(LPBYTE)padiThisPrinter,
cbDriverInfoBuffer,
&cbDriverInfoBuffer))
{
DBGPRINT(("ERROR: final GetPrinterDriverA call fails with %d\n", GetLastError()));
status = FALSE;
break;
}
}
//
// driver takes postscript if the datafile is a .PPD file
// otherwise, we'll send it ps2dib
//
pqr->pDataType = NULL;
SetDefaultPPDInfo(pqr);
if (padiThisPrinter->pDataFile != NULL)
{
if ((cbDataFileName = wcslen(padiThisPrinter->pDataFile)) > 3)
{
if (_wcsicmp(padiThisPrinter->pDataFile + cbDataFileName - 3, L"PPD") == 0)
{
if (IsRemote(pPrinter) && fLocalSystem)
{
DBGPRINT(("%ws is remote\n", pPrinter->pPrinterName));
status = FALSE;
break;
}
//
// we are postscript
//
pqr->pDataType = (LPWSTR)LocalAlloc(LPTR,
(wcslen(MACPS_DATATYPE_RAW) + 1) * sizeof(WCHAR));
if (pqr->pDataType == NULL)
{
DBGPRINT(("out of memory for pDataType\n"));
status = FALSE;
break;
}
wcscpy (pqr->pDataType, MACPS_DATATYPE_RAW);
DBGPRINT(("postscript printer, using RAW\n"));
if (!GetPPDInfo(pqr))
{
DBGPRINT(("ERROR: unable to get PPD info for %ws\n", pqr->pPrinterName));
status = FALSE;
break;
}
} // ends in PPD
} // filename longer than 3
} // filename exists
if (pqr->pDataType == NULL)
{
if (IsRemote(pPrinter))
{
DBGPRINT(("%ws is remote\n", pPrinter->pPrinterName));
status = FALSE;
break;
}
//
// we are not postscript
//
pqr->pDataType = (LPWSTR)LocalAlloc(LPTR,
(wcslen(MACPS_DATATYPE_PS2DIB) + 1) * sizeof(WCHAR));
if (pqr->pDataType == NULL)
{
DBGPRINT(("out of memory for PSTODIB pDataType\n"));
status = FALSE;
break;
}
wcscpy (pqr->pDataType, MACPS_DATATYPE_PS2DIB);
DBGPRINT(("non postscript printer, using PS2DIB\n"));
if (!SetDefaultFonts(pqr))
{
DBGPRINT(("ERROR: cannot set to laserwriter PPD info for %ws\n",
pqr->pPrinterName));
status = FALSE;
break;
}
}
} while (FALSE);
//
// resource cleanup
//
if (!status)
{
if (pqr->pPrinterName != NULL)
{
LocalFree(pqr->pPrinterName);
}
if (pqr->pMacPrinterName != NULL)
{
LocalFree(pqr->pMacPrinterName);
}
if (pqr->pDriverName != NULL)
{
LocalFree(pqr->pDriverName);
}
if (pqr->pPortName != NULL)
{
LocalFree(pqr->pPortName);
}
if (pqr->pDataType != NULL)
{
LocalFree(pqr->pDataType);
}
if (pqr->fonts != NULL)
{
LocalFree(pqr->fonts);
}
}
if (hPrinter != NULL)
{
ClosePrinter(hPrinter);
}
if (padiThisPrinter != NULL)
{
LocalFree(padiThisPrinter);
}
return (status);
}
DWORD
CreateListenerSocket(
PQR pqr
)
{
DWORD rc = NO_ERROR;
SOCKADDR_AT address;
WSH_REGISTER_NAME reqRegister;
DWORD cbWritten;
ULONG fNonBlocking;
LPWSTR pszuStatus = NULL;
LPWSTR apszArgs[1] = {NULL};
DWORD cbMessage;
DBGPRINT(("enter CreateListenerSocker()\n"));
//
// resource allocation 'loop'
//
do
{
//
// create a socket
//
pqr->sListener = socket(AF_APPLETALK, SOCK_RDM, ATPROTO_PAP);
if (pqr->sListener == INVALID_SOCKET)
{
rc = GetLastError();
DBGPRINT(("socket() fails with %d\n", rc));
break;
}
//
// bind the socket
//
address.sat_family = AF_APPLETALK;
address.sat_net = 0;
address.sat_node = 0;
address.sat_socket = 0;
if (bind(pqr->sListener, (PSOCKADDR) &address, sizeof(address)) == SOCKET_ERROR)
{
rc = GetLastError();
DBGPRINT(("bind() fails with %d\n", rc));
break;
}
//
// post a listen on the socket
//
if (listen(pqr->sListener, 5) == SOCKET_ERROR)
{
rc = GetLastError();
DBGPRINT(("listen() fails with %d\n", rc));
break;
}
//
// set the PAP Server Status
//
if ((apszArgs[0] = LocalAlloc(LPTR, sizeof(WCHAR) * (strlen(pqr->pMacPrinterName) + 1))) == NULL)
{
rc = GetLastError();
DBGPRINT(("LocalAlloc(args) fails with %d\n", rc));
break;
}
OemToChar(pqr->pMacPrinterName, apszArgs[0]);
if ((cbMessage = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
STRING_SPOOLER_ACTIVE,
LANG_NEUTRAL,
(LPWSTR)&pszuStatus,
128,
(va_list *)apszArgs)) == 0)
{
rc = GetLastError();
DBGPRINT(("FormatMessage() fails with %d\n", rc));
break;
}
if (pszuStatus == NULL)
{
rc = ERROR_NOT_ENOUGH_MEMORY;
DBGPRINT(("FormatMessage could not allocate memory for pszuStatus \n"));
break;
}
//
// remove trailing ret/lf
//
pszuStatus[cbMessage - 2] = 0;
/* MSKK NaotoN modified for MBCS system 11/13/93 */
// change JAPAN -> DBCS 96/08/13 v-hidekk
#ifdef DBCS
if ((pqr->SpoolingStatus = LocalAlloc(LPTR, cbMessage * sizeof(USHORT))) == NULL)
#else
if ((pqr->SpoolingStatus = LocalAlloc(LPTR, cbMessage)) == NULL)
#endif
{
rc = GetLastError();
DBGPRINT(("LocalAlloc(SpoolingStatus) fails with %d\n", rc));
break;
}
CharToOem(pszuStatus, pqr->SpoolingStatus);
if ((cbMessage = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
STRING_SPOOLER_IDLE,
LANG_NEUTRAL,
(LPWSTR)&pszuStatus,
128,
(va_list *)apszArgs)) == 0)
{
rc = GetLastError();
DBGPRINT(("FormatMessage() fails with %d\n", rc));
break;
}
//
// remove trailing ret/lf
//
pszuStatus[cbMessage - 2] = 0;
#ifdef DBCS
if ((pqr->IdleStatus = LocalAlloc(LPTR, cbMessage * sizeof(WCHAR))) == NULL)
#else
if ((pqr->IdleStatus = LocalAlloc(LPTR, cbMessage)) == NULL)
#endif
{
rc = GetLastError();
DBGPRINT(("LocalAlloc(IdleStatus) fails with %d\n", rc));
break;
}
CharToOem(pszuStatus, pqr->IdleStatus);
DBGPRINT(("setting status to %s\n", pqr->IdleStatus));
if ((setsockopt(pqr->sListener,
SOL_APPLETALK,
SO_PAP_SET_SERVER_STATUS,
pqr->IdleStatus,
strlen(pqr->IdleStatus))) == SOCKET_ERROR)
{
rc = GetLastError();
DBGPRINT(("setsockopt(status) fails with %d\n", rc));
break;
}
//
// register a name on the socket
//
reqRegister.ZoneNameLen = sizeof(DEF_ZONE) - 1;
reqRegister.TypeNameLen = sizeof(LW_TYPE) - 1;
reqRegister.ObjectNameLen = (CHAR) strlen(pqr->pMacPrinterName);
// Silently truncate the name if it exceeds the max. allowed
if ((reqRegister.ObjectNameLen&0x000000ff) > MAX_ENTITY)
reqRegister.ObjectNameLen = MAX_ENTITY;
memcpy (reqRegister.ZoneName, DEF_ZONE, sizeof(DEF_ZONE) - 1);
memcpy (reqRegister.TypeName, LW_TYPE, sizeof(LW_TYPE) - 1);
memcpy (reqRegister.ObjectName, pqr->pMacPrinterName, reqRegister.ObjectNameLen&0x000000ff);
cbWritten = sizeof(reqRegister);
if (setsockopt(pqr->sListener,
SOL_APPLETALK,
SO_REGISTER_NAME,
(char *) &reqRegister,
cbWritten) == SOCKET_ERROR)
{
rc = GetLastError();
if (CheckFailedCache(pqr->pPrinterName, PSP_ADD) != PSP_ALREADY_THERE)
{
DWORD dwEvent;
dwEvent = (rc == WSAEADDRINUSE)? EVENT_NAME_DUPNAME_EXISTS :
EVENT_NAME_REGISTRATION_FAILED;
ReportEvent(hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
dwEvent,
NULL,
1,
0,
&apszArgs[0],
NULL);
}
DBGPRINT(("setsockopt(SO_REGISTER_NAME) fails with %d\n", rc));
break;
}
//
// make the socket non-blocking
//
fNonBlocking = 1;
if (ioctlsocket(pqr->sListener, FIONBIO, &fNonBlocking) == SOCKET_ERROR)
{
rc = GetLastError();
DBGPRINT(("ioctlsocket(FIONBIO) fails with %d\n", rc));
break;
}
} while (FALSE);
//
// resource cleanup
//
if (apszArgs[0] != NULL)
{
LocalFree (apszArgs[0]);
}
if (pszuStatus != NULL)
{
LocalFree (pszuStatus);
}
//
// if this printer had failed previous initialization attempts, it will be in our
// failed cache: remove it here.
//
if ((rc == NO_ERROR))
{
CheckFailedCache(pqr->pPrinterName, PSP_DELETE);
}
else
{
// close the listener
DBGPRINT(("%ws: closing listener socket, error = %d\n", pqr->pPrinterName,rc));
if (pqr->sListener != INVALID_SOCKET)
{
closesocket(pqr->sListener);
pqr->sListener = INVALID_SOCKET;
}
}
return rc;
}
////////////////////////////////////////////////////////////////////////////////
//
// CheckFailedCache(): find an entry, and add/delete the entry depending on dwAction
//
// DESCRIPTION:
// This routine caches all names of all printers that fail to initialize. The current use
// of such a cache is to avoid logging too many entries in event log for the same printer
// which fails over and over.
// In reality, we don't expect more than 1 (usually 0) entries in this cache!
////////////////////////////////////////////////////////////////////////////////
DWORD
CheckFailedCache(LPWSTR pPrinterName, DWORD dwAction)
{
PFAIL_CACHE pFlCache, prevFlCache;
BOOLEAN bFound=FALSE;
DWORD dwRetCode;
DWORD dwSize;
WaitForSingleObject(mutexFlCache, INFINITE);
for ( pFlCache=prevFlCache=FlCacheHead; pFlCache != NULL; pFlCache = pFlCache->Next )
{
if (_wcsicmp(pFlCache->PrinterName, pPrinterName) == 0)
{
bFound = TRUE;
break;
}
prevFlCache = pFlCache;
}
switch( dwAction )
{
case PSP_ADD:
if (bFound)
{
ReleaseMutex(mutexFlCache);
return(PSP_ALREADY_THERE);
}
dwSize = sizeof(FAIL_CACHE) + (wcslen(pPrinterName)+1)*sizeof(WCHAR);
pFlCache = (PFAIL_CACHE)LocalAlloc(LPTR, dwSize);
if (pFlCache == NULL)
{
DBGPRINT(("CheckFailedCache: LocalAlloc failed!\n"));
ReleaseMutex(mutexFlCache);
// nothing evil should happen if we fail here, other than may be multiple
// event log entries (which is what we are fixing now!)
return(PSP_OPERATION_FAILED);
}
wcscpy (pFlCache->PrinterName, pPrinterName);
pFlCache->Next = FlCacheHead;
FlCacheHead = pFlCache;
dwRetCode = PSP_OPERATION_SUCCESSFUL;
break;
case PSP_DELETE:
if (!bFound)
{
ReleaseMutex(mutexFlCache);
return(PSP_NOT_FOUND);
}
if (pFlCache == FlCacheHead)
{
FlCacheHead = pFlCache->Next;
}
else
{
prevFlCache->Next = pFlCache->Next;
}
LocalFree(pFlCache);
dwRetCode = PSP_OPERATION_SUCCESSFUL;
break;
}
ReleaseMutex(mutexFlCache);
return( dwRetCode );
}