windows-nt/Source/XPSP1/NT/net/tcpip/services/lpd/process.c

526 lines
16 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*************************************************************************
* Microsoft Windows NT *
* *
* Copyright(c) Microsoft Corp., 1994-1997. *
* *
* Revision History: *
* *
* Jan. 24,94 Koti Created *
* 03-May-97 MohsinA Performance Thread Pooling *
* Description: *
* *
* This file contains functions that process requests from LPR clients *
* *
*************************************************************************/
#include "lpd.h"
VOID CleanupConn( PSOCKCONN pscConn);
// ========================================================================
//
// SYNOPSIS: Thread Pooling Performance Fix.
// AUTHOR: MohsinA, 25-Apr-97.
// HISTORY: Boeing needs scalable lpd servers.
//
// Notes:
// This is a worker thread
// that pulls pscConn from the global queue and services them.
// It is created from LoopOnAccept when there are many jobs and
// too few WorkerThread(s).
// WorkerThread dies when there are too many idle threads or
// when shutting down.
//
DWORD WorkerThread( PSOCKCONN pscConn )
{
DWORD threadid = GetCurrentThreadId();
int stayalive = 1; // bool, loop break in cs.
int fIamLastThread= 0;
int SLEEP_TIME = 4000; // in ms, constant per thread.
int time_slept = 0; // in ms, sum.
int num_jobs = 0; // ordinal sum.
COMMON_LPD local_common;
#ifdef PROFILING
time_t time_start = time(NULL);
time_t time_done = 0;
#endif
// We randomize the sleep time, as we don't want all the
// threads to wake up together. srand must be seeded for each thread.
#ifdef PROFILING
srand( time_start );
#endif
SLEEP_TIME = 2000 + (rand() & 0x7ff); // 2000 to 4000.
// We can't use this pscConn as another thread could have pulled it out.
// Instead we go and pull another pscConn from the queue.
pscConn = NULL;
while( stayalive ){
//
// Shutdown after emptying the queue below.
// fShuttingDownGLB will clean the job in ServiceTheClient.
//
EnterCriticalSection( &csConnSemGLB );
{
if( scConnHeadGLB.pNext ){
// == Remove one from the head.
pscConn = scConnHeadGLB.pNext;
scConnHeadGLB.pNext = pscConn->pNext;
pscConn->pNext = NULL;
Common.QueueLength--;
// == Remove one from the tail.
// PSOCKCONN x = &scConnHeadGLB;
// int count = Common.QueueLength;
//
// while( x->pNext->pNext ){
// x = x->pNext;
// --count;
// assert( 0 < count );
// }
// pscConn = x->pNext;
// Common.QueueLength--;
// x->pNext = NULL;
}else{
//
// One thread dies after 16 idle SLEEP_TIME.
//
if( fShuttingDownGLB || ( Common.IdleCounter > 32 ) ){
Common.IdleCounter /= 2;
stayalive = 0;
}else{
Common.IdleCounter++;
}
pscConn = NULL;
}
assert( Common.AliveThreads >= 0 );
assert( Common.QueueLength >= 0 );
local_common = Common; // struct copy, for readonly.
}
LeaveCriticalSection( &csConnSemGLB );
if( pscConn )
{
num_jobs++;
ServiceTheClient( pscConn );
}
else if( stayalive )
{
// LOGIT(( "PROFILING: thread %3d sleeping %d, IdleCounter=%d\n",
// threadid, SLEEP_TIME, local_common.IdleCounter ));
Sleep( SLEEP_TIME );
time_slept += SLEEP_TIME;
}
} // while stayalive.
// ====================================================================
#ifdef PROFILING
time_done = time(NULL);
LOGIT(("PROFILING: thread %3d ends, jobs=%d, life=%d, slept=%d,\n"
" AliveThreads=%d -1, MaxThreads=%d,\n"
" TotalAccepts=%d, TotalErrors=%d, IdleCounter=%d\n"
" Time now is %s"
,
threadid, num_jobs, time_done - time_start, time_slept/1000,
local_common.AliveThreads, local_common.MaxThreads,
local_common.TotalAccepts, local_common.TotalErrors,
local_common.IdleCounter,
ctime(&time_done)
));
#else
LOGIT(("WorkerThread: thread %3d ends.\n", threadid ));
#endif
EnterCriticalSection( &csConnSemGLB );
{
Common.AliveThreads--;
fIamLastThread = (Common.AliveThreads < 1 );
}
LeaveCriticalSection( &csConnSemGLB );
if( fIamLastThread && fShuttingDownGLB ){
LOGIT(("WorkerThread: Last worker thread exiting\n"));
SetEvent( hEventLastThreadGLB );
}
return NO_ERROR; // Thread Ends.
}
/*
****************************************************************************
* *
* ServiceTheClient(): *
* This function reads and interprets the request from the LPR client and *
* takes appropriate action. In that sense, this routine is the heart of *
* LPD service. *
* *
* Returns: *
* NO_ERROR (always) *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* History: *
* Jan.24, 94 Koti Created *
* *
****************************************************************************
*/
DWORD ServiceTheClient( PSOCKCONN pscConn )
{
DWORD dwErrcode;
DWORD dwResponse;
CHAR chCmdCode;
DWORD threadid = GetCurrentThreadId();
pscConn->fLogGenericEvent = TRUE;
pscConn->dwThread = threadid;
pscConn->hPrinter = (HANDLE)INVALID_HANDLE_VALUE;
#ifdef PROFILING
pscConn->time_start = time(NULL);
#endif
if ( fShuttingDownGLB ){
LOGIT(("ServiceTheClient: Thread %3d shutting down.\n", threadid ));
goto ServiceTheClient_BAIL;
}
// who are we connected to?
GetClientInfo( pscConn );
//
// get server ip address, since print clustering allows one
// node to have multiple ip addresses. Depending on the ip
// address, we'll go to different sets of print queues on the node.
//
// Albert Ting cluster change, MohsinA, 07-Mar-97.
//
GetServerInfo( pscConn );
// get command from the client
// ----------------- command 02 => "Receive Job"
// | 02 | Queue LF | Queue => Queue or Printer to print on
// -----------------
if ( GetCmdFromClient( pscConn ) != NO_ERROR )
{
// didn't get a command from client: it's bad news!
LPD_DEBUG( "GetCmdFromClient() failed in ServiceTheClient()!\n" );
goto ServiceTheClient_BAIL;
}
// get name of the queue (printer) from the command. If it's not
// formatted properly, quit!
if ( !ParseQueueName( pscConn ) )
{
PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 );
pscConn->fLogGenericEvent = FALSE;
LPD_DEBUG( "ParseQueueName() failed in ServiceTheClient()!\n" );
goto ServiceTheClient_BAIL;
}
// ====================================================================
chCmdCode = pscConn->pchCommand[0];
switch( chCmdCode )
{
case LPDC_RECEIVE_JOB:
pscConn->wState = LPDS_RECEIVE_JOB;
ProcessJob( pscConn );
CleanupConn( pscConn );
if ( pscConn->wState != LPDS_ALL_WENT_WELL )
{
AbortThisJob( pscConn );
if ( pscConn->fLogGenericEvent )
{
PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
LpdReportEvent( LPDLOG_DIDNT_WORK, 1, aszStrings, 0 );
}
}
if (pscConn->fMustFreeLicense)
{
NtLSFreeHandle(pscConn->LicenseHandle);
}
break;
case LPDC_RESUME_PRINTING:
pscConn->wState = LPDS_RESUME_PRINTING;
if ( fAllowPrintResumeGLB )
{
dwResponse = ( ResumePrinting( pscConn ) == NO_ERROR ) ?
LPD_ACK : LPD_NAK;
}
else
{
dwResponse = LPD_NAK;
}
dwErrcode = ReplyToClient( pscConn, (WORD)dwResponse );
break;
case LPDC_SEND_SHORTQ:
case LPDC_SEND_LONGQ:
pscConn->wState = LPDS_SEND_LONGQ;
if ( ParseQueueRequest( pscConn, FALSE ) != NO_ERROR )
{
LPD_DEBUG( "ServiceTheClient(): ParseQueueRequest() failed\n" );
break;
}
SendQueueStatus( pscConn, LPD_LONG );
break;
case LPDC_REMOVE_JOBS:
if ( !fJobRemovalEnabledGLB )
{
break;
}
pscConn->wState = LPDS_REMOVE_JOBS;
if ( ParseQueueRequest( pscConn, TRUE ) != NO_ERROR )
{
LPD_DEBUG( "ServiceTheClient(): ParseQueueRequest() failed\n" );
break;
}
if ( RemoveJobs( pscConn ) == NO_ERROR )
{
ReplyToClient( pscConn, LPD_ACK );
}
break;
default:
break;
}
// ====================================================================
if ( pscConn->wState != LPDS_ALL_WENT_WELL ){
goto ServiceTheClient_BAIL;
}
#ifdef PROFILING
pscConn->time_done = time(NULL);
LOGIT(("PROFILING: ok, thread %3d, time_queued %s"
" wait=%d, work=%d\n",
threadid,
ctime(&pscConn->time_queued),
pscConn->time_start - pscConn->time_queued,
pscConn->time_done - pscConn->time_start
));
#endif
// close the connection down and terminate the thread
TerminateConnection( pscConn );
pscConn = NULL;
return NO_ERROR;
// ====================================================================
// if we reached here, then a non-recoverable error occured somewhere:
// try to inform the client (by sending a NAK) and terminate the thread
ServiceTheClient_BAIL:
#ifdef PROFILING
pscConn->time_done = time(NULL);
LOGIT(("PROFILING: bail, thread %3d, job times %8d, wait=%d work=%d\n",
threadid,
pscConn->time_queued,
pscConn->time_start - pscConn->time_queued,
pscConn->time_done - pscConn->time_start
));
#endif
LPD_DEBUG( "Reached ServiceTheClient_BAIL.\n" );
ReplyToClient( pscConn, LPD_NAK );
TerminateConnection( pscConn );
pscConn = NULL;
return NO_ERROR;
} // end ServiceTheClient()
/*****************************************************************************
* *
* TerminateConnection(): *
* This function releases all the memory that was allocated while *
* processing the client's requests, closes the printer, closes the *
* socket connection, removes its structure (pscConn) from the global *
* linked list and frees the memory allocated for pscConn itself. *
* Also, if the main thread is waiting on this thread for shutdown then *
* this function sets hEventLastThreadGLB event to tell the main thread *
* that this thread is done. *
* *
* Returns: *
* Nothing *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* History: *
* Jan.24, 94 Koti Created *
* *
*****************************************************************************/
VOID TerminateConnection( PSOCKCONN pscConn )
{
// PSOCKCONN pscCurrent;
// BOOL fIamLastThread=FALSE;
// it should never be NULL at this point! But check it anyway!
if ( pscConn == (PSOCKCONN) NULL )
{
LPD_DEBUG( "TerminateConnection(): pscConn NULL at entry\n" );
return;
}
ShutdownPrinter( pscConn );
if ( pscConn->hPrinter != (HANDLE)INVALID_HANDLE_VALUE )
{
LPD_DEBUG( "TerminateConnection: hPrinter not closed\n" );
}
// close the socket
if ( pscConn->sSock != INVALID_SOCKET )
{
SureCloseSocket( pscConn->sSock );
}
//
// release memory in every field of the structure
//
if ( pscConn->pchCommand != NULL )
LocalFree( pscConn->pchCommand );
if ( pscConn->pchPrinterName != NULL )
LocalFree( pscConn->pchPrinterName );
//
// no memory was allocated for ppchUsers[] and adwJobIds[]. They just
// pointed to parts of what's freed by ( pscConn->pchCommand ) above.
//
if ( pscConn->pqStatus != NULL )
LocalFree( pscConn->pqStatus );
// EnterCriticalSection( &csConnSemGLB );
// {
//
// if( Common.AliveThreads <= 1 ){
// fIamLastThread = TRUE;
// }
//
// //
// // // remove this structure from the link
// //
// // pscCurrent = &scConnHeadGLB;
// //
// // while( pscCurrent ){
// // if (pscConn == pscCurrent->pNext)
// // break;
// // pscCurrent = pscCurrent->pNext;
// //
// // // what if we can't find our pscConn in the list at all?
// // // this should NEVER ever happen, but good to check!
// //
// // if( pscCurrent == NULL)
// // {
// // LocalFree( pscConn );
// // LPD_DEBUG( "TerminateConnection(): "
// // "couldn't find pscConn "
// // " in the list!\n" );
// // LeaveCriticalSection( &csConnSemGLB );
// // return;
// // }
// // }
// // pscCurrent->pNext = pscConn->pNext;
// }
// LeaveCriticalSection( &csConnSemGLB );
memset( pscConn, 0, sizeof( SOCKCONN ) );
LocalFree( pscConn );
// //
// // if shutdown is in progress and we are the last active thread, tell
// // the poor main thread (blocked for us to finish) that we're done!
// //
//
// if( fIamLastThread && fShuttingDownGLB ){
// LOGIT(("TerminateConnection: Last worker thread exiting\n"));
// SetEvent( hEventLastThreadGLB );
// }
return;
}