//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2000 // // File: srequest.cxx // // Contents: Server side of catalog/query requests // // Classes: CPipeServer // CRequestServer // CRequestQueue // // Functions: StartCiSvcWork // StopCiSvcWork // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "secutil.hxx" extern CLocateDocStore g_svcDocStoreLocator; extern CStaticMutexSem g_mtxStartStop; CRequestQueue * g_pRequestQueue = 0; const CRequestServer::ProxyMessageFunction CRequestServer::_aMsgFunctions[] = { DoConnect, DoDisconnect, DoCreateQuery, DoFreeCursor, DoGetRows, DoRatioFinished, DoCompareBmk, DoGetApproximatePosition, DoSetBindings, DoGetNotify, DoSendNotify, DoSetWatchMode, DoGetWatchInfo, DoShrinkWatchRegion, DoRefresh, DoGetQueryStatus, DoObsolete, //WidToPath DoCiState, DoObsolete, //BeginCacheTransaction DoObsolete, //SetupCache DoObsolete, //EndCacheTransaction DoObsolete, //AddScope DoObsolete, //RemoveScope DoObsolete, //AddVirtualScope DoObsolete, //RemoveVirtualScope DoForceMerge, DoAbortMerge, DoObsolete, //SetPartition DoFetchValue, DoWorkIdToPath, DoUpdateDocuments, DoGetQueryStatusEx, DoRestartPosition, DoStopAsynch, DoStartWatching, DoStopWatching, DoSetCatState }; const BOOL CRequestServer::_afImpersonate[] = { TRUE, // DoConnect, FALSE, // DoDisconnect, TRUE, // DoCreateQuery, FALSE, // DoFreeCursor, TRUE, // DoGetRows, FALSE, // DoRatioFinished, FALSE, // DoCompareBmk, FALSE, // DoGetApproximatePosition, FALSE, // DoSetBindings, FALSE, // DoGetNotify, FALSE, // DoSendNotify, FALSE, // DoSetWatchMode, FALSE, // DoGetWatchInfo, FALSE, // DoShrinkWatchRegion, FALSE, // DoRefresh, FALSE, // DoGetQueryStatus, FALSE, // DoObsolete, //DoWidToPath, FALSE, // DoCiState, TRUE, // DoBeginCacheTransaction, TRUE, // DoSetupCache, TRUE, // DoEndCacheTransaction, FALSE, // DoObsolete, //AddScope FALSE, // DoObsolete, //RemoveScope FALSE, // DoObsolete, //AddVirtualScope FALSE, // DoObsolete, //RemoveVirtualScope TRUE, // DoForceMerge, TRUE, // DoAbortMerge, FALSE, // DoObsolete, //SetPartition TRUE, // DoFetchValue, TRUE, // DoWorkIdToPath, TRUE, // DoUpdateDocuments, FALSE, // DoGetQueryStatusEx, FALSE, // DoRestartPosition, FALSE, // DoStopAsynch, FALSE, // DoStartWatching, FALSE, // DoStopWatching, TRUE, // DoSetCatState }; //+------------------------------------------------------------------------- // // Member: CPipeServer::CPipeServer, public // // Synopsis: Constructor for a server pipe. The pipe is created such // that any client can connect. // // Arguments: [pwcName] - Name for the pipe // [cmsDefaultClientTimeout] - Default timeout for clients // trying to get a pipe instance // [pSecurityDescriptor] - Security for the pipe // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- CPipeServer::CPipeServer( const WCHAR * pwcName, ULONG cmsDefaultClientTimeout, SECURITY_DESCRIPTOR * pSecurityDescriptor ) : _eventWrite( (HANDLE) 0 ) // defer creation of the notification event { RtlZeroMemory( &_overlapped, sizeof _overlapped ); SECURITY_ATTRIBUTES saPipeSecurity; saPipeSecurity.nLength = sizeof SECURITY_ATTRIBUTES; saPipeSecurity.bInheritHandle = FALSE; saPipeSecurity.lpSecurityDescriptor = pSecurityDescriptor; // note: the read/write buffers are allocated from non-paged pool, // and are grown if too small to reflect usage _hPipe = CreateNamedPipe( pwcName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, // read buffer size 2048, // write buffer size cmsDefaultClientTimeout, &saPipeSecurity ); if ( INVALID_HANDLE_VALUE == _hPipe ) THROW( CException() ); prxDebugOut(( DEB_ITRACE, "created pipe 0x%x\n", _hPipe )); } //CPipeServer //+------------------------------------------------------------------------- // // Member: CPipeServer::Connect, public // // Synopsis: Enables a client to connect to the pipe. // // Returns: TRUE if connected, FALSE if GetEvent() will be signalled // on connection, or throws on error. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- BOOL CPipeServer::Connect() { prxDebugOut(( DEB_ITRACE, "connecting from pipe 0x%x\n", _hPipe )); _overlapped.hEvent = _event.GetHandle(); BOOL fEventWait = ConnectNamedPipe( _hPipe, &_overlapped ); if ( !fEventWait ) { DWORD dwError = GetLastError(); prxDebugOut(( DEB_ITRACE, "CPipeServer::Connect.. GetLastError() == %d\n", dwError )); if ( ERROR_PIPE_CONNECTED == dwError ) return TRUE; else if ( ERROR_IO_PENDING != dwError ) THROW( CException() ); } return FALSE; } //Connect //+------------------------------------------------------------------------- // // Member: CPipeServer::ReadRemainingSync, public // // Synopsis: Reads the remainder of a message from the pipe (if any) // // Arguments: [pvSoFar] - Part of the message read so far // [cbSoFar] - in: # of bytes read so far for the message // out: # bytes total in message // // Returns: Pointer to memory containing entire message or 0 if // there really wasn't anything more to read // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- BYTE * CPipeServer::ReadRemainingSync( void * pvSoFar, DWORD & cbSoFar ) { DWORD cbLeft; if ( ! PeekNamedPipe( _hPipe, 0, 0, 0, 0, &cbLeft ) ) THROW( CException() ); if ( 0 == cbLeft ) { prxDebugOut(( DEB_WARN, "peek says no more to read\n" )); return 0; } XArray xMsg( cbSoFar + cbLeft ); RtlCopyMemory( xMsg.Get(), pvSoFar, cbSoFar ); CEventSem evt; OVERLAPPED o; RtlZeroMemory( &o, sizeof o ); o.hEvent = evt.GetHandle(); DWORD cbRead; if ( ! ReadFile( _hPipe, xMsg.Get() + cbSoFar, cbLeft, &cbRead, &o ) ) { if ( ERROR_IO_PENDING == GetLastError() ) { if ( !GetOverlappedResult( _hPipe, &o, &cbRead, TRUE ) ) THROW( CException() ); } else THROW( CException() ); } Win4Assert( cbRead == cbLeft ); cbSoFar += cbLeft; return xMsg.Acquire(); } //ReadRemainingSync //+------------------------------------------------------------------------- // // Member: CPipeServer::WriteSync, public // // Synopsis: Does a synchronous write to a pipe. There can be at most // one caller at a time to this method. // // Arguments: [pv] - Buffer to write // [cb] - # of bytes to write // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CPipeServer::WriteSync( void * pv, DWORD cb ) { prxDebugOut(( DEB_ITRACE, "WriteSync on this 0x%x, pipe 0x%x\n", this, _hPipe )); OVERLAPPED o; RtlZeroMemory( &o, sizeof o ); // Create and keep the event, since if we do it once (for notifications) // we'll likely do it again soon. if ( 0 == _eventWrite.GetHandle() ) _eventWrite.Create(); o.hEvent = _eventWrite.GetHandle(); if ( ! WriteFile( _hPipe, pv, cb, 0, &o ) ) { if ( ERROR_IO_PENDING == GetLastError() ) { DWORD cbWritten; if ( !GetOverlappedResult( _hPipe, &o, &cbWritten, TRUE ) ) THROW( CException() ); Win4Assert( cbWritten == cb ); } else THROW( CException() ); } prxDebugOut(( DEB_ITRACE, "WriteSync completed on this 0x%x, pipe 0x%x\n", this, _hPipe )); } //WriteSync //+------------------------------------------------------------------------- // // Member: CRequestServer::CRequestServer, public // // Synopsis: Constructor for the request server // // Arguments: [pwcPipe] - Name of the pipe to use // [cmsDefaultClientTimeout] - Timeout for clients waiting for // and instance // [requestQueue] - The 1 and only request queue // [workQueue] - The work queue // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- CRequestServer::CRequestServer( const WCHAR * pwcPipe, ULONG cmsDefaultClientTimeout, CRequestQueue & requestQueue, CWorkQueue & workQueue ) : CPipeServer( pwcPipe, cmsDefaultClientTimeout, requestQueue.GetSecurityDescriptor() ), PWorkItem( sigCRequestServer ), _state( pipeStateNone ), _cRefs( 1 ), _iClientVersion( 0 ), _pQuery( 0 ), _fClientIsRemote( FALSE ), _pWorkThread( 0 ), _hWorkThread( INVALID_HANDLE_VALUE ), _cbFetchedValueSoFar( 0 ), _cbPendingWrite( 0 ), _scPendingStatus( S_OK ), _requestQueue( requestQueue ), _workQueue( workQueue ), _pevtDone( 0 ), _dwLastTouched( GetTickCount() ) { requestQueue.AddToListNoThrow( this ); } //CRequestServer //+------------------------------------------------------------------------- // // Member: CRequestServer::Release, public // // Synopsis: Releases and if approprate deletes the object // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::Release() { Win4Assert( 0 != _cRefs ); CLock lock( _requestQueue.GetTheMutex() ); // Since the AddRef() is not done under lock (and we don't want to) // InterlockedDecrement is used even though we're under lock. if ( 0 == InterlockedDecrement( & _cRefs ) ) { _requestQueue.RemoveFromListNoThrow( this ); // If someone is waiting for this to go out of the list, // tell them now. if ( 0 != _pevtDone ) { _pevtDone->Set(); _pevtDone = 0; } delete this; } } //Release //+------------------------------------------------------------------------- // // Member: CRequestServer::DoIt, public // // Synopsis: Implements the work queue DoIt method by scheduling // the first read from the client of the pipe. The work queue // is needed since apc completion routines must be run by // the thread that submitted the i/o. // // Arguments: [pThread] - Thread being used (for refcounting) // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::DoIt( CWorkThread *pThread ) { TRY { Win4Assert( pipeStateNone == _state ); Win4Assert( 0 == _pWorkThread ); Win4Assert( INVALID_HANDLE_VALUE == _hWorkThread ); prxDebugOut(( DEB_ITRACE, "doit CRequestServer %p\n", this )); _pWorkThread = pThread; _hWorkThread = pThread->GetThreadHandle(); _workQueue.AddRef( pThread ); _state = pipeStateRead; Read( _Buffer(), _BufferSize(), APCRoutine ); if ( IsBeingRemoved() ) CancelIO(); } CATCH( CException, ex ) { prxDebugOut(( DEB_ITRACE, "failed initial read error 0x%x, pipe %p\n", ex.GetErrorCode(), GetPipe() )); _state = pipeStateNone; _requestQueue.RecycleRequestServerNoThrow( this ); } END_CATCH; } //DoIt //+------------------------------------------------------------------------- // // Member: CRequestServer::DeferredAPC, public // // Synopsis: Performs an APC that was earlier deferred. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::DeferredAPC() { DoAPC( _dwDeferredAPCError, _cbDeferredAPCTransferred ); } //DeferredAPC //+------------------------------------------------------------------------- // // Function: TranslateOldPropsToNewProps // // Synopsis: Translates ver 5 client properties into version 6+ props // // Arguments: [oldProps] - source of translated values // [newProps] - destination of translated values // // History: 31-May-97 dlee Created. // //-------------------------------------------------------------------------- void TranslateOldPropsToNewProps( CDbProperties & oldProps, CDbProperties & newProps ) { const GUID guidFsClientPropset = DBPROPSET_FSCIFRMWRK_EXT; const GUID guidQueryCorePropset = DBPROPSET_CIFRMWRKCORE_EXT; // pluck out the catalog and scope information from the old set WCHAR *pwcCatalog = 0; BSTR bstrMachine = 0; CDynArrayInPlace aDepths(2); CDynArrayInPlace aScopes(2); LONG lQueryType = 0; for ( ULONG i = 0; i < oldProps.Count(); i++ ) { CDbPropSet & propSet = oldProps.GetPropSet( i ); DBPROPSET *pSet = propSet.CastToStruct(); if ( guidQueryCorePropset == pSet->guidPropertySet ) { ULONG cProp = pSet->cProperties; DBPROP * pProp = pSet->rgProperties; for ( ULONG p = 0; p < pSet->cProperties; p++, pProp++ ) { PROPVARIANT &v = (PROPVARIANT &) pProp->vValue; switch ( pProp->dwPropertyID ) { case DBPROP_MACHINE : { Win4Assert( VT_BSTR == v.vt ); if ( VT_BSTR == v.vt ) bstrMachine = v.bstrVal; break; } } } } else if ( guidFsClientPropset == pSet->guidPropertySet ) { ULONG cProp = pSet->cProperties; DBPROP * pProp = pSet->rgProperties; for ( ULONG p = 0; p < pSet->cProperties; p++, pProp++ ) { PROPVARIANT &v = (PROPVARIANT &) pProp->vValue; switch ( pProp->dwPropertyID ) { case DBPROP_CI_INCLUDE_SCOPES : { Win4Assert( (VT_VECTOR|VT_LPWSTR) == v.vt ); if ( (VT_VECTOR|VT_LPWSTR) == v.vt ) for ( ULONG s = 0; s < v.calpwstr.cElems; s++ ) aScopes[s] = v.calpwstr.pElems[s]; break; } case DBPROP_CI_DEPTHS : { Win4Assert( (VT_VECTOR|VT_I4) == v.vt ); if ( (VT_VECTOR|VT_I4) == v.vt ) for ( ULONG s = 0; s < v.cal.cElems; s++ ) aDepths[s] = v.cal.pElems[s]; break; } case DBPROP_CI_CATALOG_NAME : { Win4Assert( VT_LPWSTR == v.vt ); if ( VT_LPWSTR == v.vt ) pwcCatalog = v.pwszVal; break; } case DBPROP_CI_QUERY_TYPE : { Win4Assert( VT_I4 == v.vt ); lQueryType = v.lVal; break; } } } } } if ( 0 == pwcCatalog || 0 == bstrMachine ) THROW( CException( STATUS_INVALID_PARAMETER_MIX ) ); prxDebugOut(( DEB_ITRACE, "Converting old props to new props, catalog '%ws'", pwcCatalog )); prxDebugOut(( DEB_ITRACE, "type %d, %d scopes, %d depths\n", lQueryType, aScopes.Count(), aDepths.Count() )); // create a new set of properties based on the old info CDynArrayInPlace aBSTR( aScopes.Count() + 2 ); DBPROPSET aNewSet[2]; DBPROP aFSProps[4]; RtlZeroMemory( aFSProps, sizeof aFSProps ); aNewSet[0].cProperties = 0; aNewSet[0].guidPropertySet = guidFsClientPropset; aNewSet[0].rgProperties = aFSProps; aFSProps[0].dwPropertyID = DBPROP_CI_CATALOG_NAME; aFSProps[0].vValue.vt = VT_BSTR; aFSProps[0].vValue.bstrVal = SysAllocString( pwcCatalog ); aBSTR[ aBSTR.Count() ] = aFSProps[0].vValue.bstrVal; aNewSet[0].cProperties++; aFSProps[1].dwPropertyID = DBPROP_CI_QUERY_TYPE; aFSProps[1].vValue.vt = VT_I4; aFSProps[1].vValue.lVal = lQueryType; aNewSet[0].cProperties++; DBPROP & propDepth = aFSProps[ aNewSet[0].cProperties ]; propDepth.dwPropertyID = DBPROP_CI_DEPTHS; SAFEARRAY saDepth = { 1, // Dimension FADF_AUTO, // Flags: on stack sizeof(LONG), // Size of an element 1, // Lock count. 1 for safety. (void *)aDepths.GetPointer(), // The data { aDepths.Count(), 0 } }; // Bounds (element count, low bound) if ( 1 == aDepths.Count() ) { propDepth.vValue.vt = VT_I4; propDepth.vValue.lVal = aDepths[0]; aNewSet[0].cProperties++; } else if ( aDepths.Count() > 1 ) { propDepth.vValue.vt = ( VT_ARRAY | VT_I4 ); propDepth.vValue.parray = & saDepth; aNewSet[0].cProperties++; } DBPROP & propScope = aFSProps[ aNewSet[0].cProperties ]; propScope.dwPropertyID = DBPROP_CI_INCLUDE_SCOPES; SAFEARRAY saScope = { 1, // Dimension FADF_AUTO | FADF_BSTR, // Flags: on stack, contains BSTRs sizeof(BSTR), // Size of an element 1, // Lock count. 1 for safety. (void *)0, // The data { aScopes.Count(), 0 } }; // Bounds (element count, low bound) if ( 1 == aScopes.Count() ) { propScope.vValue.vt = VT_BSTR; BSTR bstr = SysAllocString( aScopes[ 0 ] ); aBSTR[ aBSTR.Count() ] = bstr; propScope.vValue.bstrVal = bstr; aNewSet[0].cProperties++; } else if ( aScopes.Count() > 1 ) { propScope.vValue.vt = ( VT_ARRAY | VT_BSTR ); propScope.vValue.parray = & saScope; BSTR * pBSTR = (BSTR *) aBSTR.GetPointer(); pBSTR += aBSTR.Count(); for ( ULONG x = 0; x < aScopes.Count(); x++ ) aBSTR[ aBSTR.Count() ] = SysAllocString( aScopes[ x ] ); saScope.pvData = (void *) pBSTR; aNewSet[0].cProperties++; } DBPROP aQueryProps[1]; RtlZeroMemory( aQueryProps, sizeof aQueryProps ); aNewSet[1].cProperties = 1; aNewSet[1].guidPropertySet = guidQueryCorePropset; aNewSet[1].rgProperties = aQueryProps; aQueryProps[0].dwPropertyID = DBPROP_MACHINE; aQueryProps[0].vValue.vt = VT_BSTR; aQueryProps[0].vValue.bstrVal = bstrMachine; // Verify all the bstrs were allocated for ( ULONG iBstr = 0; iBstr < aBSTR.Count(); iBstr++ ) { if ( 0 == aBSTR[ iBstr ] ) { for ( ULONG i = 0; i < aBSTR.Count(); i++ ) { if ( 0 != aBSTR[ i ] ) SysFreeString( aBSTR[ i ] ); } THROW( CException( E_OUTOFMEMORY ) ); } } SCODE sc = newProps.SetProperties( 2, aNewSet ); for ( ULONG ibstr = 0; ibstr < aBSTR.Count(); ibstr++ ) SysFreeString( aBSTR[ ibstr ] ); if ( FAILED( sc ) ) THROW( CException( sc ) ); } //TranslateOldPropsToNewProps //+------------------------------------------------------------------------- // // Member: CRequestServer::DoConnect, private // // Synopsis: Handles a connect message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoConnect( DWORD cbRequest, DWORD & cbToWrite ) { CPMConnectIn &request = * (CPMConnectIn *) _ActiveBuffer(); XPtrST xTemp( _xTempBuffer.Acquire() ); Win4Assert( pmConnect == request.GetMessage() ); // guard against attack Win4Assert( 0 == _pQuery ); Win4Assert( _xDocStore.IsNull() ); if ( !_xDocStore.IsNull() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); // save the client version, isRemote, machine name, and user name _iClientVersion = request.GetClientVersion(); // don't support old clients if ( GetClientVersion() < 5 ) THROW( CException( STATUS_INVALID_PARAMETER_MIX ) ); request.ValidateCheckSum( GetClientVersion(), cbRequest ); _fClientIsRemote = request.IsClientRemote(); WCHAR *pwcMachine = request.GetClientMachineName(); ULONG cwcMachine = 1 + wcslen( pwcMachine ); // Check if the machine name looks mal-formed if ( ( cwcMachine * sizeof WCHAR ) >= __min( 1024, cbRequest ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); WCHAR *pwcUser = request.GetClientUserName(); ULONG cwcUser = 1 + wcslen( pwcUser ); // Check if the user name looks mal-formed if ( ( ( cwcMachine + cwcUser ) * sizeof WCHAR ) >= __min( 1024, cbRequest ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); _xClientMachine.Init( cwcMachine ); RtlCopyMemory( _xClientMachine.Get(), pwcMachine, _xClientMachine.SizeOf() ); _xClientUser.Init( cwcUser ); RtlCopyMemory( _xClientUser.Get(), pwcUser, _xClientUser.SizeOf() ); // // Retreive the db properties. // if ( 5 == GetClientVersion() ) { // unmarshall the old-style properties, and translate into // the new-style properties if ( request.GetBlobSize() > 0 ) { if ( request.GetBlobSize() >= cbRequest ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CMemDeSerStream stmDeser( request.GetBlobStartAddr(), request.GetBlobSize() ); XInterface xOldDbP( new CDbProperties ); if ( xOldDbP.IsNull() || ! xOldDbP->UnMarshall( stmDeser ) ) THROW( CException( E_OUTOFMEMORY ) ); XInterface xNewDbP( new CDbProperties ); if ( xNewDbP.IsNull() ) THROW( CException( E_OUTOFMEMORY ) ); TranslateOldPropsToNewProps( xOldDbP.GetReference(), xNewDbP.GetReference() ); _xDbProperties.Set( xNewDbP.Acquire() ); } } else { Win4Assert( GetClientVersion() >= 6 ); if ( GetClientVersion() < 5 ) THROW( CException( STATUS_INVALID_PARAMETER ) ); if ( request.GetBlob2Size() > 0 ) { if ( ( 0 == request.GetBlob2StartAddr() ) || ( ( request.GetBlobSize() + request.GetBlob2Size() ) >= cbRequest ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CMemDeSerStream stmDeser( request.GetBlob2StartAddr(), request.GetBlob2Size() ); XInterface xDbP( new CDbProperties ); if ( xDbP.IsNull() || !xDbP->UnMarshall( stmDeser ) ) THROW( CException( E_OUTOFMEMORY ) ); _xDbProperties.Set( xDbP.Acquire() ); } } prxDebugOut(( DEB_ITRACE|DEB_PRX_LOG, "connect clientver %d, remote %d, machine '%ws', user '%ws'\n", _iClientVersion, _fClientIsRemote, pwcMachine, pwcUser )); XInterface xLocator( _requestQueue.DocStoreLocator() ); Win4Assert( !xLocator.IsNull() ); CPMConnectOut & reply = *(CPMConnectOut *) _Buffer(); cbToWrite = sizeof reply; reply.ServerVersion() = pmServerVersion; // // Never fault in the docstore since docstores have been opened by now. // Win4Assert( _requestQueue.AreDocStoresOpen() ); ICiCDocStore * pDocStore = 0; SCODE sc = xLocator->LookUpDocStore( _xDbProperties.GetPointer(), &pDocStore, TRUE ); if ( SUCCEEDED(sc) ) { // sc will be CI_S_NO_DOCSTORE if this is the CIADMIN catalog if ( 0 != pDocStore ) { _xDocStore.Set( pDocStore ); // This QI is just about guaranteed to work. XInterface xAdviseStatus; sc = pDocStore->QueryInterface( IID_ICiCAdviseStatus, xAdviseStatus.GetQIPointer() ); if ( S_OK != sc ) THROW( CException(sc) ); xAdviseStatus->IncrementPerfCounterValue( CI_PERF_RUNNING_QUERIES ); } } else reply.SetStatus( CI_E_NO_CATALOG ); return stateContinue; } //DoConnect //+------------------------------------------------------------------------- // // Member: CRequestServer::DoDisconnect, private // // Synopsis: Handles a disconnect message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // Returns: stateDisconnect -- the connection will be closed // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoDisconnect( DWORD cbRequest, DWORD & cbToWrite ) { CProxyMessage &msg = * (CProxyMessage *) _Buffer(); Win4Assert( pmDisconnect == msg.GetMessage() ); return stateDisconnect; } //DoDisconnect //+------------------------------------------------------------------------- // // Member: CRequestServer::DoCreateQuery, private // // Synopsis: Handles a create query message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoCreateQuery( DWORD cbRequest, DWORD & cbToWrite ) { // if this hits, there is probably a cursor refcounting bug Win4Assert( 0 == _pQuery ); if ( _xDocStore.IsNull() || ( 0 != _pQuery ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); XInterface xDocStoreEx; SCODE sc = _xDocStore->QueryInterface( IID_ICiCDocStoreEx, xDocStoreEx.GetQIPointer() ); if ( SUCCEEDED(sc) ) { BOOL fNoQuery = FALSE; sc = xDocStoreEx->IsNoQuery( &fNoQuery ); if ( S_OK == sc ) { if ( fNoQuery ) THROW( CException( QUERY_S_NO_QUERY ) ); } else THROW ( CException( sc ) ); } else THROW ( CException( sc ) ); RequestState state = stateContinue; // The request is either in the standard buffer or the temp buffer. // Take ownership of the temp buffer. CPMCreateQueryIn &request = * (CPMCreateQueryIn *) _ActiveBuffer(); XPtrST xTemp( _xTempBuffer.Acquire() ); // marshalling / unmarshalling is inconsistent if alignment is different Win4Assert( isQWordAligned( &request ) ); Win4Assert( isQWordAligned( request.Data() ) ); request.ValidateCheckSum( GetClientVersion(), cbRequest ); // unpickle the query XColumnSet cols; XRestriction rst; XSortSet sort; XCategorizationSet categ; CRowsetProperties Props; XPidMapper pidmap; UnPickle( GetClientVersion(), cols, rst, sort, categ, Props, pidmap, (BYTE *) request.Data(), cbRequest - sizeof CProxyMessage ); if ( Props.GetMaxResults() > 0 && Props.GetFirstRows() > 0 ) THROW( CException( E_INVALIDARG ) ); // Compute # of cursors to create and where to put them unsigned cCursors = 1; if ( 0 != categ.GetPointer() ) cCursors += categ->Size(); // verify the output buffer is large enough if ( _BufferSize() < ( cCursors * sizeof ULONG + sizeof CPMCreateQueryOut ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); // get reference to CLangList to pass to CQParse objects... XInterface xICiManager; sc = _xDocStore->GetContentIndex( xICiManager.GetPPointer() ); if ( FAILED(sc) ) THROW( CException(sc) ); XInterface xICiFrameworkQuery; sc = xICiManager->QueryInterface( IID_ICiFrameworkQuery, xICiFrameworkQuery.GetQIPointer() ); if ( FAILED(sc) ) THROW( CException(sc) ); CLangList * pLangList = 0; sc = xICiFrameworkQuery->GetLangList((void**)&pLangList); if ( FAILED(sc) ) THROW( CException(sc) ); // // Set up a property mapper. Used for restriction parsing and sort/output // column translation. // XInterface xPropMapper; sc = _xDocStore->GetPropertyMapper( xPropMapper.GetPPointer() ); if ( FAILED( sc ) ) { vqDebugOut(( DEB_ERROR, "CRequestServer::DoCreateQuery, GetPropertyMapper failed\n" )); THROW( CException( sc ) ); } // // Adjust pidmap to translate properties. // CPidConverter PidConverter( xPropMapper.GetPointer() ); pidmap->SetPidConverter( &PidConverter ); // parse the query -- expand phrases, etc. XRestriction rstParsed; DWORD dwQueryStatus = 0; if ( !rst.IsNull() ) { CQParse qparse( pidmap.GetReference(), *pLangList ); rstParsed.Set( qparse.Parse( rst.GetPointer() ) ); DWORD dwParseStatus = qparse.GetStatus(); if ( ( 0 != ( dwParseStatus & CI_NOISE_PHRASE ) ) && ( rst->Type() != RTVector ) && ( rst->Type() != RTOr ) ) { vqDebugOut(( DEB_WARN, "Query contains phrase composed " "entirely of noise words.\n" )); THROW( CException( QUERY_E_ALLNOISE ) ); } const DWORD dwCiNoise = CI_NOISE_IN_PHRASE | CI_NOISE_PHRASE; if ( 0 != ( dwCiNoise & dwParseStatus ) ) dwQueryStatus |= STAT_NOISE_WORDS; } // // Re-map property ids. // // // TODO: Get rid of this whole pid remap thing. We should // really be able to do it earlier now that the pidmap // can be set up to convert fake to real pids. // XInterface pidremap( new CPidRemapper( pidmap.GetReference(), xPropMapper, 0, // rstParsed.GetPointer(), cols.GetPointer(), sort.GetPointer() ) ); // // WorkID may be added to the columns requested in SetBindings. // Be sure it's in the pidremap from the beginning. // CFullPropSpec psWorkId( guidQuery, DISPID_QUERY_WORKID ); pidremap->NameToReal( &psWorkId ); XInterface xQueryPropMapper; sc = pidremap->QueryInterface( IID_ICiQueryPropertyMapper, xQueryPropMapper.GetQIPointer() ); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "DoCreateQuery - QI for property mapper failed 0x%x\n", sc )); THROW ( CException( sc ) ) ; } XInterface xQuerySession; sc = _xDocStore->GetQuerySession( xQuerySession.GetPPointer() ); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "DoCreateQuery - GetQuerySession failed 0x%x\n", sc )); THROW ( CException( sc ) ) ; } // // Initialize the query session // sc = xQuerySession->Init( 0, 0, _xDbProperties.GetPointer(), xQueryPropMapper.GetPointer() ); if ( FAILED( sc ) ) THROW( CException( sc ) ); // // Optimize the query // XQueryOptimizer xqopt( new CQueryOptimizer( xQuerySession, _xDocStore.GetPointer(), rstParsed, cols.GetReference(), sort.GetPointer(), pidremap.GetReference(), Props, dwQueryStatus ) ); prxDebugOut(( DEB_ITRACE, "Query has %s1 component%s\n", xqopt->IsMultiCursor() ? "> " : "", xqopt->IsMultiCursor() ? "s" : "" )); prxDebugOut(( DEB_ITRACE, "Current component of query %s fully sorted\n", xqopt->IsFullySorted() ? "is" : "is not" )); prxDebugOut(( DEB_ITRACE, "Current component of query %s positionable\n", xqopt->IsPositionable() ? "is" : "is not" )); prxDebugOut(( DEB_ITRACE, "Rowset props flags: 0x%x\n", Props.GetPropertyFlags() )); prxDebugOut(( DEB_ITRACE, "Rowset props maxresults: %d\n", Props.GetMaxResults() )); prxDebugOut(( DEB_ITRACE, "Rowset props cmdtimeout: %d\n", Props.GetCommandTimeout() )); CPMCreateQueryOut &reply = * (CPMCreateQueryOut *) _Buffer(); ULONG *aCursors = reply.GetCursors(); reply.IsWorkIdUnique() = xqopt->IsWorkidUnique(); reply.SetServerCookie( (ULONG_PTR) this ); // create either a true sequential or bigtable query if ( ( (Props.GetPropertyFlags() & eLocatable) == 0) && ( !xqopt->IsMultiCursor() ) && ( xqopt->IsFullySorted() ) && ( 0 == categ.GetPointer() ) ) { _pQuery = new CSeqQuery( xqopt, cols, aCursors, pidremap, _xDocStore.GetPointer() ); reply.IsTrueSequential() = TRUE; } else { reply.IsTrueSequential() = FALSE; // If we've been instructed to create a synchronous cursor, // put the request into a pending state BOOL fSync = ( 0 == ( Props.GetPropertyFlags() & eAsynchronous ) ); XInterface xMe; if ( fSync ) { // Refcount ourselves in case the async write fails and // the request server is recycled before this thread returns. AddRef(); xMe.Set( this ); state = statePending; _state = pipeStatePending; _scPendingStatus = S_OK; _cbPendingWrite = sizeof CPMCreateQueryOut + ( sizeof ULONG * cCursors ); } TRY { CRequestQueue & requestQueue = _requestQueue; PQuery *pQ = new CAsyncQuery( xqopt, cols, sort, categ, cCursors, aCursors, pidremap, (Props.GetPropertyFlags() & eWatchable) != 0, _xDocStore.GetPointer(), fSync ? this : 0 ); // If synchronous, the query may be done and freed // by the client app by now. the CRequestServer is still // valid due to the AddRef above. if ( fSync ) requestQueue.IncrementPendingItems(); else _pQuery = pQ; } CATCH( CException, e ) { _pQuery = 0; _state = pipeStateRead; RETHROW(); } END_CATCH } cbToWrite = sizeof CPMCreateQueryOut + ( sizeof ULONG * cCursors ); return state; } //DoCreateQuery //+------------------------------------------------------------------------- // // Member: CRequestServer::DoFreeCursor, private // // Synopsis: Handles a free cursor message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoFreeCursor( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMFreeCursorIn &request = * (CPMFreeCursorIn *) _Buffer(); CPMFreeCursorOut &reply = * (CPMFreeCursorOut *) _Buffer(); reply.CursorsRemaining() = _pQuery->FreeCursor( request.GetCursor() ); // The client is allowed to serially do multiple queries on 1 connection. // The way a query is released is by freeing all of its cursors. // If this is the last cursor reference to the query, delete the query. if ( 0 == reply.CursorsRemaining() ) FreeQuery(); cbToWrite = sizeof reply; return stateContinue; } //DoFreeCursor //+------------------------------------------------------------------------- // // Member: CRequestServer::DoGetRows, private // // Synopsis: Handles a get rows message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // 12-Nov-99 KLam Adjust results for Win64 server to // Win32 client. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoGetRows( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMGetRowsIn & request = * (CPMGetRowsIn *) _Buffer(); request.ValidateCheckSum( GetClientVersion(), cbRequest ); // The input buffer must be as large as the parameter buffer // plus the size of the serialized seek description. Win4Assert( _BufferSize() >= sizeof CPMGetRowsOut + request.GetSeekSize() ); if ( _BufferSize() < sizeof CPMGetRowsOut + request.GetSeekSize() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); unsigned cbReserved = request.GetReservedSize(); unsigned cbRowWidth = request.GetRowWidth(); unsigned cbClientRead = request.GetReadBufferSize(); unsigned cRowsToTransfer = request.GetRowsToTransfer(); BOOL fFwdFetch = request.GetFwdFetch(); ULONG_PTR ulClientBase = request.GetClientBase(); ULONG hCursor = request.GetCursor(); // validate params agains attack if ( 0 == cbRowWidth || cbRowWidth > cbClientRead || cbReserved >= cbClientRead ) { Win4Assert( 0 != cbRowWidth ); Win4Assert( cbRowWidth <= cbClientRead ); Win4Assert( cbReserved < cbClientRead ); THROW( CException( STATUS_INVALID_PARAMETER ) ); } // Deserialize the row seek description Win4Assert( isQWordAligned( (void*) UIntToPtr( cbReserved ) ) ); Win4Assert( isQWordAligned( request.GetDesc() ) ); CMemDeSerStream stmDeser( request.GetDesc(), request.GetSeekSize() ); XPtr pRowSeek; UnmarshallRowSeekDescription( stmDeser, GetClientVersion(), pRowSeek, FALSE ); CPMGetRowsOut *pReply; // use a temporary buffer if the client is doing a large read if ( cbClientRead <= _BufferSize() ) { pReply = (CPMGetRowsOut *) _Buffer(); } else { _xTempBuffer.Init( cbClientRead ); pReply = new( _xTempBuffer.Get() ) CPMGetRowsOut; } #if CIDBG == 1 RtlFillMemory( ( (BYTE *) pReply ) + sizeof CPMGetRowsOut, cbClientRead - sizeof CPMGetRowsOut, 0xca ); #endif // CIDBG == 1 // cbReserved includes room for the the CPMGetRowsOut and the marshalled // seek description. All other space in the buffer is for row data. unsigned cbRealRowWidth = cbRowWidth; #ifdef _WIN64 if ( !IsClient64() ) // replying to a 32 bit client cbRealRowWidth = _cbRowWidth64; #endif CFixedVarBufferAllocator Alloc( pReply, ulClientBase, cbClientRead, cbRealRowWidth, cbReserved ); CGetRowsParams Fetch( cRowsToTransfer, fFwdFetch, cbRealRowWidth, Alloc ); XPtr xRowSeekOut; SCODE scRet = _pQuery->GetRows( hCursor, pRowSeek.GetReference(), Fetch, xRowSeekOut ); if ( ( SUCCEEDED( scRet ) ) || ( ( STATUS_BUFFER_TOO_SMALL == scRet ) && ( Fetch.RowsTransferred() > 0 ) ) ) { if ( STATUS_BUFFER_TOO_SMALL == scRet ) pReply->SetStatus( DB_S_BLOCKLIMITEDROWS ); else pReply->SetStatus( scRet ); pReply->RowsReturned() = Fetch.RowsTransferred(); #ifdef _WIN64 if ( !IsClient64() ) // replying to a 32 bit client { // Reply + CPMGetRowsOut + seek info => table info BYTE *pbResults = ((BYTE *)pReply) + cbReserved; unsigned cbResults = cbClientRead - cbReserved; unsigned cRows = Fetch.RowsTransferred(); // // Adjust the sizes of the results to fit for a Win32 client // FixRows ( (BYTE *)pReply, pbResults, cbResults, ulClientBase, cRows, cbRowWidth ); } #endif // // We can't say everything is ok and fetch 0 rows. This assert was // added because we hit this situation on the client side during // stress. // Win4Assert( ! ( ( S_OK == scRet ) && ( 0 == Fetch.RowsTransferred() ) ) ); if ( xRowSeekOut.IsNull() ) { ULONG *pul = (ULONG *) pReply->GetSeekDesc(); *pul = 0; } else { Win4Assert( ( xRowSeekOut->MarshalledSize() + sizeof CPMGetRowsOut ) <= cbReserved ); CMemSerStream stmMem( pReply->GetSeekDesc(), xRowSeekOut->MarshalledSize() ); xRowSeekOut->Marshall( stmMem ); } // Have to transfer the entire buffer since variable data is // written from the top of the buffer down, and fixed data from // the bottom of the buffer up. cbToWrite = cbClientRead; } else { // This exception is too commmon in testing... QUIETTHROW( CException( scRet ) ); } prxDebugOut(( DEB_ITRACE, "status at end of getrows: 0x%x\n", pReply->GetStatus() )); return stateContinue; } //DoGetRows #ifdef _WIN64 //+------------------------------------------------------------------------- // // Member: CRequestServer::FixRows, private // // Synopsis: Adjusts Win64 rows into Win32 rows. // // Arguments: [pbReply] - Reply buffer // [pbResults] - Buffer containting result data // [cbResults] - Number of bytes in results buffer // [ulpClientBase] - Offset of buffer on the client // [cRows] - Count of rows // [cbRowWidth] - Bytes in a row // // History: 12-Nov-99 KLam created // //-------------------------------------------------------------------------- void CRequestServer::FixRows ( BYTE * pbReply, BYTE * pbResults, ULONG cbResults, ULONG_PTR ulpClientBase, ULONG cRows, ULONG cbRowWidth ) { prxDebugOut(( DEB_ITRACE, "64bit server replying to 32bit client, fixing rows!\n" )); Win4Assert( !IsClient64() ); unsigned cCols = _xCols64->Count(); // Save the result buffer BYTE *pbCurrentResults = new BYTE[cbResults]; XPtrST xbCurrentResults( pbCurrentResults ); prxDebugOut(( DEB_ITRACE, "\tCopying %d bytes from 0x%I64x to 0x%I64x\n", cbResults, pbResults, pbCurrentResults )); memcpy( pbCurrentResults, pbResults, cbResults ); #if CIDBG == 1 RtlFillMemory( pbResults, cRows * _cbRowWidth64, 0xba ); #endif // CIDBG == 1 prxDebugOut(( DEB_ITRACE, "\tReturning %d rows and %d cols each row being %d bytes (32bit: %d) for a total of %d in a %d bytes buffer\n", cRows, cCols, _cbRowWidth64, cbRowWidth, cRows * _cbRowWidth64, cbResults )); prxDebugOut(( DEB_ITRACE, "\t64bit results are at: 0x%I64x 32bit results will be at: 0x%I64x\n", pbCurrentResults, pbResults )); // Fix the results for ( unsigned iRow = 0; iRow < cRows; iRow++ ) { prxDebugOut(( DEB_ITRACE, "\n\tRow: %d Reply Buffer: 0x%I64x Data Buffer: 0x%I64x\n", iRow, pbCurrentResults, pbResults )); for ( unsigned iCol = 0; iCol < cCols; iCol++ ) { prxDebugOut(( DEB_ITRACE, "\n\t\tColumn: %d\n", iCol )); prxDebugOut(( DEB_ITRACE, "\t\t_xCols64 0x%I64x\t_xCols32 0x%I64x\n", _xCols64.GetPointer(), _xCols32.GetPointer() )); CTableColumn *pCol64 = _xCols64->Get(iCol); CTableColumn *pCol32 = _xCols32->Get(iCol); if ( pCol64->IsValueStored() ) { ULONG cbSize32 = 0; if ( pCol64->GetStoredType() == VT_VARIANT ) { PROPVARIANT *pVar64 = (PROPVARIANT *) (pbCurrentResults + pCol64->GetValueOffset()); PROPVARIANT32 *pVar32 = (PROPVARIANT32 *) (pbResults + pCol32->GetValueOffset()); prxDebugOut(( DEB_ITRACE, "\t\t Col64 Data: 0x%I64x Offset: %d Col32 Data: 0x%I64x Offset: %d\n", pVar64, pCol64->GetValueOffset(), pVar32, pCol32->GetValueOffset() )); cbSize32 = sizeof( PROPVARIANT32 ) + ((CTableVariant *)pVar64)->VarDataSize32( pbReply, ulpClientBase ); pVar32->wReserved2 = (WORD) cbSize32; FixVariantPointers ( pVar32, pVar64, pbReply, ulpClientBase ); } else if ( pCol64->GetStoredType() == VT_I4 ) { ULONG *pul64 = (ULONG *) (pbCurrentResults + pCol64->GetValueOffset()); ULONG *pul32 = (ULONG *) (pbResults + pCol32->GetValueOffset()); prxDebugOut(( DEB_ITRACE, "\t\t VT_I4: %d Col64 Data: 0x%I64x Offset: %d Col32 Data: 0x%I64x Offset: %d\n", *pul64, pul64, pCol64->GetValueOffset(), pul32, pCol32->GetValueOffset() )); cbSize32 = 4; *pul32 = *pul64; } else { // Unhandled type Win4Assert ( pCol64->GetStoredType() == VT_I4 || pCol64->GetStoredType() == VT_VARIANT ); } if ( pCol64->IsStatusStored() ) { BYTE *pbStatus64 = (BYTE *) (pbCurrentResults + pCol64->GetStatusOffset()); BYTE *pbStatus32 = (BYTE *) (pbResults + pCol32->GetStatusOffset()); *pbStatus32 = *pbStatus64; prxDebugOut (( DEB_ITRACE, "\t\tStatus = 0x%x\n", *pbStatus32 )); } if ( pCol64->IsLengthStored() ) { LONG *plLength64 = (LONG *) (pbCurrentResults + pCol64->GetLengthOffset()); LONG *plLength32 = (LONG *) (pbResults + pCol32->GetLengthOffset()); prxDebugOut (( DEB_ITRACE, "\t\tSize32 = %d Size64 = %d\n", cbSize32, *plLength64 )); *plLength32 = cbSize32; prxDebugOut (( DEB_ITRACE, "\t\tLength = %d\n", *plLength32 )); } } } // Move to the next row pbResults += cbRowWidth; pbCurrentResults += _cbRowWidth64; } } // FixRows //+------------------------------------------------------------------------- // // Member: CRequestServer::FixVariantPointers, private // // Synopsis: Adjusts Win64 variants to fit into Win32 variants. // // Arguments: [pVar32] - Pointer to 32 bit variant // [pVar64] - Pointer to 64 bit variant // [pbResults] - Buffer containing variant data // [ulClientBase] - Offset of buffer on the client // // History: 21-Oct-99 KLam created // 13-Feb-2000 KLam assert needed to check element not pointer // 15-Feb-2000 DLee DECIMAL is special case (16 bits) // //-------------------------------------------------------------------------- void CRequestServer::FixVariantPointers ( PROPVARIANT32 *pVar32, PROPVARIANT *pVar64, BYTE *pbResults, ULONG_PTR ulClientBase ) { pVar32->vt = pVar64->vt; // reference or a vector or an array if ( CTableVariant::IsByRef ( pVar64->vt ) || 0 != (pVar64->vt & (VT_VECTOR | VT_ARRAY)) ) { // blob used here as a generic holder for a size and pointer pVar32->blob.cbSize = pVar64->blob.cbSize; pVar32->blob.pBlob = (PTR32)(UINT_PTR) (pVar64->blob.pBlobData); prxDebugOut(( DEB_ITRACE, "\t\t Fixing reference. ClientBase: 0x%I64x Buffer: 0x%I64x at 0x%x\n", ulClientBase, pbResults, pVar32->blob.pBlob )); if ( VT_LPWSTR == pVar64->vt || VT_LPSTR == pVar64->vt || VT_BSTR == pVar64->vt ) prxDebugOut(( DEB_ITRACE, "\t\tvt: %d pStr: 0x%I64x\n", pVar64->vt, ((UINT_PTR)pVar64->calpwstr.pElems - ulClientBase) + pbResults )); else prxDebugOut(( DEB_ITRACE, "\t\tvt: %d size: 0x%I64x pBlob: 0x%I64x\n", pVar64->vt, pVar64->blob.cbSize, pVar64->blob.pBlobData )); // Vector of strings if ( (VT_VECTOR|VT_LPWSTR) == pVar64->vt || (VT_VECTOR|VT_LPSTR) == pVar64->vt || (VT_VECTOR|VT_BSTR) == pVar64->vt ) { // Get a pointer to the vector in the variable section of the return buffer void ** ppvVector = (void **)( ((UINT_PTR)pVar64->calpwstr.pElems - ulClientBase ) + pbResults ); ULONG * pulVector = (ULONG *) ppvVector; prxDebugOut(( DEB_ITRACE, "\t\t Fixing vector 0x%I64x\n", ppvVector )); for ( unsigned iElement = 0; iElement < pVar64->calpwstr.cElems; ++iElement ) pulVector[iElement] = (ULONG) (UINT_PTR) (ppvVector[iElement]); } // Vector of variants else if ( (VT_VARIANT|VT_VECTOR) == pVar64->vt ) // not currently supported { PROPVARIANT * pVarVector64 = (PROPVARIANT *)( ((UINT_PTR)pVar64->capropvar.pElems - ulClientBase ) + pbResults ); PROPVARIANT32 * pVarVector32 = (PROPVARIANT32 *) pVarVector64; // Recursively fix each variant in the vector for ( unsigned iVarElement = 0; iVarElement < pVar64->capropvar.cElems; ++iVarElement ) FixVariantPointers ( &pVarVector32[iVarElement], &pVarVector64[iVarElement], pbResults, ulClientBase ); } // Arrays (Safearrays) else if ( ( 0 != (VT_ARRAY & pVar64->vt) ) || ( VT_SAFEARRAY == (pVar64->vt & VT_TYPEMASK) ) ) { // Get a pointer to the vector in the variable section of the return buffer SAFEARRAY * psa = (SAFEARRAY *)(( (UINT_PTR)pVar64->pparray - ulClientBase ) + pbResults ); SAFEARRAY32 * psa32 = (SAFEARRAY32 *) psa; psa32->pvData = (PTR32) (UINT_PTR) psa->pvData; // memmove is smart enough to move the overlapping bytes first memmove ( psa32->rgsabound, psa->rgsabound, psa32->cDims * sizeof (SAFEARRAYBOUND) ); if ( VT_BSTR == (pVar64->vt & VT_TYPEMASK) ) { // Pointing to an array of 32bit pointers so adjust the size psa32->cbElements = sizeof ( PTR32 ); // Get the number of elements in the safe array unsigned cBstrElements = psa32->rgsabound[0].cElements; for ( unsigned j = 1; j < psa32->cDims; j++ ) cBstrElements *= psa32->rgsabound[j].cElements; ULONG *pulBstr = (ULONG *) ((psa32->pvData - ulClientBase) + pbResults ); void **ppvBstr = (void **) pulBstr; for ( j = 0; j < cBstrElements; j++ ) { // Make sure Win64 isn't passing to big of a pointer if ( 0 != (((ULONG_PTR)ppvBstr[j]) & 0xFFFFFFFF00000000) ) { prxDebugOut(( DEB_ERROR, "Non 32 bit pointer 0x%I64x !!!\n", (ULONG_PTR)ppvBstr[j] )); Win4Assert( 0 == (((ULONG_PTR)ppvBstr[j]) & 0xFFFFFFFF00000000) ); } pulBstr[j] = (ULONG) (ULONG_PTR) ppvBstr[j]; } } else if ( VT_VARIANT == (pVar64->vt & VT_TYPEMASK) ) { // Pointing to an array of 32bit variants so adjust the size psa32->cbElements = sizeof ( PROPVARIANT32 ); // Get the number of elements in the safe array unsigned cVariants = psa32->rgsabound[0].cElements; for ( unsigned j = 1; j < psa32->cDims; j++ ) cVariants *= psa32->rgsabound[j].cElements; PROPVARIANT * avar64 = (PROPVARIANT *) ((psa32->pvData - ulClientBase) + pbResults ); PROPVARIANT32 * avar32 = (PROPVARIANT32 *) avar64; prxDebugOut(( DEB_ITRACE, "\t\t Found %d Variants at 0x%I64x\n", cVariants, avar64 )); // Recursively fix each variant in the array for ( unsigned v = 0; v < cVariants; v++ ) FixVariantPointers ( &avar32[v], &avar64[v], pbResults, ulClientBase ); } } } else if ( VT_DECIMAL == pVar64->vt ) { RtlCopyMemory ( pVar32, pVar64, sizeof PROPVARIANT32 ); } else { pVar32->uhVal = (ULONGLONG)(UINT_PTR) pVar64->pulVal; prxDebugOut(( DEB_ITRACE, "\t\tvt: %d value: 0x%I64x\n\n", pVar64->vt, pVar64->uhVal )); } } //FixVariantPointers #endif // _WIN64 //+------------------------------------------------------------------------- // // Member: CRequestServer::DoRestartPosition, private // // Synopsis: Handles a restart position message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 17-Apr-97 emilyb created // //-------------------------------------------------------------------------- RequestState CRequestServer::DoRestartPosition( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMRestartPositionIn & request = * (CPMRestartPositionIn *) _Buffer(); _pQuery->RestartPosition( request.GetCursor(), request.GetChapter() ); return stateContinue; } //DoRestartPosition //+------------------------------------------------------------------------- // // Member: CRequestServer::DoStopAsynch, private // // Synopsis: Handles a stop processing of async rowset message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 17-Apr-97 emilyb created // //-------------------------------------------------------------------------- RequestState CRequestServer::DoStopAsynch( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMStopAsynchIn & request = * (CPMStopAsynchIn *) _Buffer(); _pQuery->StopAsynch( request.GetCursor() ); return stateContinue; } //DoStopAsynch //+------------------------------------------------------------------------- // // Member: CRequestServer::DoStartWatching, private // // Synopsis: Handles a start watch all behavior for rowset message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 17-Apr-97 emilyb created // //-------------------------------------------------------------------------- RequestState CRequestServer::DoStartWatching( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMStartWatchingIn & request = * (CPMStartWatchingIn *) _Buffer(); _pQuery->StartWatching( request.GetCursor() ); return stateContinue; } //DoStartWatching //+------------------------------------------------------------------------- // // Member: CRequestServer::DoStopWatching, private // // Synopsis: Handles a stop watch all behavior for rowset message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 17-Apr-97 emilyb created // //-------------------------------------------------------------------------- RequestState CRequestServer::DoStopWatching( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMStopWatchingIn & request = * (CPMStopWatchingIn *) _Buffer(); _pQuery->StopWatching( request.GetCursor() ); return stateContinue; } //DoStopWatching //+------------------------------------------------------------------------- // // Member: CRequestServer::DoRatioFinished, private // // Synopsis: Handles a ratio finished message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoRatioFinished( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMRatioFinishedIn &request = * (CPMRatioFinishedIn *) _Buffer(); CPMRatioFinishedOut &reply = * (CPMRatioFinishedOut *) _Buffer(); DBCOUNTITEM den, num, rows; _pQuery->RatioFinished( request.GetCursor(), den, num, rows, reply.NewRows() ); reply.Denominator() = (ULONG) den; reply.Numerator() = (ULONG) num; reply.RowCount() = (ULONG) rows; cbToWrite = sizeof reply; return stateContinue; } //DoRatioFinished //+------------------------------------------------------------------------- // // Member: CRequestServer::DoCompareBmk, private // // Synopsis: Handles a compare bookmark message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoCompareBmk( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMCompareBmkIn &request = * (CPMCompareBmkIn *) _Buffer(); CPMCompareBmkOut &reply = * (CPMCompareBmkOut *) _Buffer(); _pQuery->Compare( request.GetCursor(), request.GetChapter(), request.GetBmkFirst(), request.GetBmkSecond(), reply.Comparison() ); cbToWrite = sizeof reply; return stateContinue; } //DoCompareBmk //+------------------------------------------------------------------------- // // Member: CRequestServer::DoGetApproximatePosition, private // // Synopsis: Handles a get approximate position message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoGetApproximatePosition( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMGetApproximatePositionIn &request = * (CPMGetApproximatePositionIn *)_Buffer(); CPMGetApproximatePositionOut &reply = * (CPMGetApproximatePositionOut *)_Buffer(); DBCOUNTITEM cNum,cDen; _pQuery->GetApproximatePosition( request.GetCursor(), request.GetChapter(), request.GetBmk(), & cNum, & cDen ); reply.Numerator() = (ULONG) cNum; reply.Denominator() = (ULONG) cDen; cbToWrite = sizeof reply; return stateContinue; } //DoGetApproximatePosition //+------------------------------------------------------------------------- // // Member: CRequestServer::DoSetBindings, private // // Synopsis: Handles a set bindings message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // 05-Oct-99 KLam Handle Win32 client to Win64 server // //-------------------------------------------------------------------------- RequestState CRequestServer::DoSetBindings( DWORD cbRequest, DWORD & cbToWrite ) { Win4Assert( 0 != _pQuery || IsBeingRemoved() ); if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); // The request is either in the standard buffer or the temp buffer. // Take ownership of the temp buffer. CPMSetBindingsIn &request = * (CPMSetBindingsIn *) _ActiveBuffer(); XPtrST xTemp( _xTempBuffer.Acquire() ); Win4Assert( isQWordAligned( request.GetDescription() ) ); request.ValidateCheckSum( GetClientVersion(), cbRequest ); CMemDeSerStream stmDeser( request.GetDescription(), request.GetBindingDescLength() ); XPtr Pidmap( new CPidMapper() ); XPtr Cols( new CTableColumnSet ( stmDeser, Pidmap.GetReference() ) ); #ifdef _WIN64 // Create a new TableColumnSet with 64-bit sizes if ( !IsClient64() ) { prxDebugOut(( DEB_ITRACE, "32bit client querying 64bit server, fixing columns!\n" )); // // The client gave us a 32-bit column description // Fix the column descriptions to work on Win64 // FixColumns ( Cols.Acquire() ); _cbRowWidth32 = request.GetRowLength(); // SetBindings with the 64 bit table _pQuery->SetBindings( request.GetCursor(), _cbRowWidth64, _xCols64.GetReference(), Pidmap.GetReference() ); prxDebugOut(( DEB_ITRACE, "\tRow Length: Win32 %d Win64 %d\n", _cbRowWidth32, _cbRowWidth64 )); prxDebugOut(( DEB_ITRACE, "\t\t_xCols64 0x%I64x\n\t\t_xCols32 0x%I64x\n", _xCols64.GetPointer(), _xCols32.GetPointer() )); } else { #endif _pQuery->SetBindings( request.GetCursor(), request.GetRowLength(), Cols.GetReference(), Pidmap.GetReference() ); #ifdef _WIN64 } #endif return stateContinue; } //DoSetBindings #ifdef _WIN64 //+------------------------------------------------------------------------- // // Member: CRequestServer::FixColumns, private // // Synopsis: Adjusts a Win32 column description to a Win64 column description // and saves both descriptions // // Arguments: [pCols32] - 32 bit table column set description // // History: 12-Nov-99 KLam Created. // //-------------------------------------------------------------------------- void CRequestServer::FixColumns ( CTableColumnSet * pCols32 ) { USHORT iEnd = 0; USHORT iAlignment; // Make sure these were freed from FreeQuery if ( !_xCols64.IsNull() && !_xCols32.IsNull() ) THROW ( CException( STATUS_INVALID_PARAMETER ) ); // Create a 64 bit column description _xCols64.Set( new CTableColumnSet ( pCols32->Count() ) ); _xCols32.Set( pCols32 ); for (unsigned iCol = 0; iCol < _xCols32->Count(); iCol++) { CTableColumn *pColumn = _xCols32->Get(iCol); VARTYPE vt = pColumn->GetStoredType(); Win4Assert ( VT_VARIANT == vt|| VT_I4 == vt ); XPtr xColumn64 (new CTableColumn( iCol )); if ( pColumn->IsValueStored() ) { prxDebugOut(( DEB_ITRACE, "\n\tFound value type: %d width: %d offset: %d\n", pColumn->GetStoredType(), pColumn->GetValueSize(), pColumn->GetValueOffset() )); if ( VT_VARIANT == vt ) { iAlignment = (USHORT)AlignBlock ( iEnd, sizeof(LONGLONG) ); iEnd = iAlignment + sizeof (PROPVARIANT); xColumn64->SetValueField( VT_VARIANT, iAlignment, sizeof (PROPVARIANT) ); } else if ( VT_I4 == vt ) { iAlignment = (USHORT)AlignBlock ( iEnd, sizeof(ULONG) ); iEnd = iAlignment + sizeof (ULONG); xColumn64->SetValueField( VT_I4, iAlignment, sizeof (ULONG) ); } prxDebugOut(( DEB_ITRACE, "\t Replacing with width: %d offset: %d\n", xColumn64->GetValueSize(), xColumn64->GetValueOffset() )); } if ( pColumn->IsStatusStored() ) { prxDebugOut(( DEB_ITRACE, "\tFound status width: %d offset: %d\n", pColumn->GetStatusSize(), pColumn->GetStatusOffset() )); iAlignment = (USHORT)AlignBlock ( iEnd, sizeof (BYTE) ); iEnd = iAlignment + sizeof (BYTE); xColumn64->SetStatusField( iAlignment, sizeof (BYTE) ); prxDebugOut(( DEB_ITRACE, "\t Replacing with width: %d offset: %d\n", xColumn64->GetStatusSize(), xColumn64->GetStatusOffset() )); } if ( pColumn->IsLengthStored() ) { prxDebugOut(( DEB_ITRACE, "\tFound length width: %d offset: %d\n", pColumn->GetLengthSize(), pColumn->GetLengthOffset() )); iAlignment = (USHORT)AlignBlock ( iEnd, sizeof (ULONG) ); iEnd = iAlignment + sizeof (ULONG); xColumn64->SetLengthField( iAlignment, sizeof (ULONG) ); prxDebugOut(( DEB_ITRACE, "\t Replacing with width: %d offset: %d\n", xColumn64->GetLengthSize(), xColumn64->GetLengthOffset() )); } // Store the column in the column set _xCols64->Add( xColumn64.GetPointer(), iCol ); xColumn64.Acquire(); } _cbRowWidth64 = AlignBlock( iEnd, sizeof(ULONGLONG) ); } // FixColumns #endif // _WIN64 //+------------------------------------------------------------------------- // // Member: CRequestServer::DoGetNotify, private // // Synopsis: Handles a get notify message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoGetNotify( DWORD cbRequest, DWORD & cbToWrite ) { // _pQuery will be zero if the notify thread comes in after the last // FreeCursor. This is a rare non-fatal condition and it is ok to // throw an error. if ( 0 == _pQuery ) QUIETTHROW( CException( STATUS_INVALID_PARAMETER ) ); CProxyMessage &msg = * (CProxyMessage *) _Buffer(); Win4Assert( pmGetNotify == msg.GetMessage() ); CNotificationSync sync( this ); DBWATCHNOTIFY wn; SCODE sc = _pQuery->GetNotifications( sync, wn ); // If STATUS_PENDING, just return the pmGetNotify and the client will // be expecting 0 or 1 pmSendNotify messages at some later time. // Otherwise, the notification is available and it is returned here. if ( STATUS_PENDING != sc ) { Win4Assert( S_OK == sc ); // GetNotifications throws on failure CPMSendNotifyOut *pReply = new( _Buffer() ) CPMSendNotifyOut( wn ); cbToWrite = sizeof CPMSendNotifyOut; } return stateContinue; } //DoGetNotify //+------------------------------------------------------------------------- // // Member: CRequestServer::DoSendNotify, private // // Synopsis: Handles a send notify message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoSendNotify( DWORD cbRequest, DWORD & cbToWrite ) { Win4Assert( FALSE && !"pmSendNotify is server to client only!" ); return stateDisconnect; } //DoSendNotify //+------------------------------------------------------------------------- // // Member: CRequestServer::DoSetWatchMode, private // // Synopsis: Handles a set watch mode message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoSetWatchMode( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMSetWatchModeIn &request = * (CPMSetWatchModeIn *) _Buffer(); HWATCHREGION hRegion = request.GetRegion(); _pQuery->SetWatchMode( &hRegion, request.GetMode() ); CPMSetWatchModeOut &reply = * (CPMSetWatchModeOut *) _Buffer(); reply.Region() = hRegion; cbToWrite = sizeof reply; return stateContinue; } //DoSetWatchMode //+------------------------------------------------------------------------- // // Member: CRequestServer::DoGetWatchInfo, private // // Synopsis: Handles a get watch info message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoGetWatchInfo( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMGetWatchInfoIn &request = * (CPMGetWatchInfoIn *) _Buffer(); CPMGetWatchInfoOut &reply = * (CPMGetWatchInfoOut *) _Buffer(); DBCOUNTITEM cRows; _pQuery->GetWatchInfo( request.GetRegion(), & reply.Mode(), & reply.Chapter(), & reply.Bookmark(), & cRows ); reply.RowCount() = (ULONG) cRows; cbToWrite = sizeof reply; return stateContinue; } //DoGetWatchInfo //+------------------------------------------------------------------------- // // Member: CRequestServer::DoShrinkWatchRegion, private // // Synopsis: Handles a shrink watch region message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoShrinkWatchRegion( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMShrinkWatchRegionIn &request = * (CPMShrinkWatchRegionIn *) _Buffer(); _pQuery->ShrinkWatchRegion( request.GetRegion(), request.GetChapter(), request.GetBookmark(), request.GetRowCount() ); return stateContinue; } //DoShrinkWatchRegion //+------------------------------------------------------------------------- // // Member: CRequestServer::DoRefresh, private // // Synopsis: Handles a refresh message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoRefresh( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); _pQuery->Refresh(); return stateContinue; } //DoRefresh //+------------------------------------------------------------------------- // // Member: CRequestServer::DoGetQueryStatus, private // // Synopsis: Handles a get query status message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoGetQueryStatus( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMGetQueryStatusIn &request = * (CPMGetQueryStatusIn *) _Buffer(); CPMGetQueryStatusOut &reply = * (CPMGetQueryStatusOut *) _Buffer(); _pQuery->GetQueryStatus( request.GetCursor(), reply.QueryStatus() ); cbToWrite = sizeof reply; return stateContinue; } //DoGetQueryStatus //+------------------------------------------------------------------------- // // Member: CRequestServer::DoGetQueryStatusEx, private // // Synopsis: Handles a get query status message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoGetQueryStatusEx( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMGetQueryStatusExIn &request = * (CPMGetQueryStatusExIn *) _Buffer(); CPMGetQueryStatusExOut &reply = * (CPMGetQueryStatusExOut *) _Buffer(); DBCOUNTITEM den, num, iBmk, cRows; _pQuery->GetQueryStatusEx( request.GetCursor(), reply.QueryStatus(), reply.FilteredDocuments(), reply.DocumentsToFilter(), den, num, request.GetBookmark(), iBmk, cRows ); reply.RatioFinishedDenominator() = (ULONG) den; reply.RatioFinishedNumerator() = (ULONG) num; reply.RowBmk() = (ULONG) iBmk; reply.RowsTotal() = (ULONG) cRows; cbToWrite = sizeof reply; return stateContinue; } //DoGetQueryStatusEx //+------------------------------------------------------------------------- // // Member: CRequestServer::DoCiState, private // // Synopsis: Handles a ci state message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoCiState( DWORD cbRequest, DWORD & cbToWrite ) { CPMCiStateInOut &request = * (CPMCiStateInOut *) _Buffer(); if ( _xDocStore.IsNull() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); XInterface xFsCiAdmin; SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin, xFsCiAdmin.GetQIPointer() ); if ( S_OK == sc ) { sc = xFsCiAdmin->CiState( &(request.GetState()) ); request.SetStatus( sc ); } cbToWrite = sizeof CProxyMessage + request.GetState().cbStruct; return stateContinue; } //DoCiState //+------------------------------------------------------------------------- // // Member: CRequestServer::DoObsolete, private // // Synopsis: Handles an obsolete message // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoObsolete( DWORD cbRequest, DWORD & cbToWrite ) { // This request is obsolete THROW( CException( STATUS_INVALID_PARAMETER ) ); return stateDisconnect; } //DoObsolete //+------------------------------------------------------------------------- // // Member: CRequestServer::DoForceMerge, private // // Synopsis: Handles a force merge message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoForceMerge( DWORD cbRequest, DWORD & cbToWrite ) { VerifyThreadHasAdminPrivilege(); if ( _xDocStore.IsNull() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMForceMergeIn &request = * (CPMForceMergeIn *) _Buffer(); XInterface xFsCiAdmin; SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin, xFsCiAdmin.GetQIPointer() ); if ( SUCCEEDED(sc) ) sc = xFsCiAdmin->ForceMerge( request.GetPartID() ); if ( !SUCCEEDED(sc) ) THROW( CException(sc) ); return stateContinue; } //DoForceMerge //+------------------------------------------------------------------------- // // Member: CRequestServer::DoAbortMerge, private // // Synopsis: Handles an abort merge message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoAbortMerge( DWORD cbRequest, DWORD & cbToWrite ) { VerifyThreadHasAdminPrivilege(); if ( _xDocStore.IsNull() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMAbortMergeIn &request = * (CPMAbortMergeIn *) _Buffer(); XInterface xFsCiAdmin; SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin, xFsCiAdmin.GetQIPointer() ); if ( SUCCEEDED(sc) ) sc = xFsCiAdmin->AbortMerge( request.GetPartID() ); if ( !SUCCEEDED(sc) ) THROW( CException(sc) ); return stateContinue; } //DoAbortMerge //+------------------------------------------------------------------------- // // Member: CRequestServer::DoSetCatState, private // // Synopsis: Handles a SetCiCatState message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 14-Apr-98 kitmanh Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoSetCatState( DWORD cbRequest, DWORD & cbToWrite ) { ciDebugOut(( DEB_ITRACE, "DoSetCatState is called\n" )); VerifyThreadHasAdminPrivilege(); CPMSetCatStateIn &request = * (CPMSetCatStateIn *) _Buffer(); CPMSetCatStateOut &reply = * (CPMSetCatStateOut *) _Buffer(); XInterface xLocator( _requestQueue.DocStoreLocator() ); Win4Assert( !xLocator.IsNull() ); BOOL fAbsUnWritable = FALSE; //temp value DWORD dwOldState; // hack to check if all catalogs are up, catalog name is ignored if ( CICAT_ALL_OPENED == request.GetNewState() ) { reply.GetOldState() = _requestQueue.AreDocStoresOpen(); // make a constant cbToWrite = sizeof reply; return stateContinue; } // end of hack ICiCDocStore * pDocStore = 0; // Make sure we have a valid catalog name WCHAR *szCatalog = request.GetCatName(); if ( 0 == szCatalog ) THROW( CException( STATUS_INVALID_PARAMETER ) ); unsigned cwc = wcslen( szCatalog ); if ( ( 0 == cwc) || ( cwc >= ( MAX_PATH-1 ) ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); // check old state of docstore SCODE sc = xLocator->GetDocStoreState( szCatalog, &pDocStore, &dwOldState ); reply.GetOldState() = dwOldState; if ( SUCCEEDED(sc) ) { _xDocStore.Set( pDocStore ); if ( 0 != pDocStore ) { // This QI is just about guaranteed to work. XInterface xAdviseStatus; sc = pDocStore->QueryInterface( IID_ICiCAdviseStatus, xAdviseStatus.GetQIPointer() ); if ( S_OK != sc ) THROW( CException(sc) ); xAdviseStatus->IncrementPerfCounterValue( CI_PERF_RUNNING_QUERIES ); } //Push an item onto the _stateChangeArray for the (make this a function name DoDisConnect SCWorkItem newItem; ciDebugOut(( DEB_ITRACE, "request.GetNewState == %d\n", request.GetNewState() )); switch ( request.GetNewState() ) { case CICAT_GET_STATE: break; case CICAT_STOPPED: if ( 0 == (CICAT_STOPPED & dwOldState) ) { newItem.type = eStopCat; newItem.pDocStore = _xDocStore.GetPointer(); _requestQueue.AddSCItem( &newItem , 0 ); xLocator->AddStoppedCat( dwOldState, request.GetCatName() ); CLock lock( g_mtxStartStop ); sc = StopFWCiSvcWork( eStopCat ); } break; case CICAT_READONLY: ciDebugOut(( DEB_ITRACE, "New state is CICAT_READONLY\n" )); if ( 0 == (CICAT_READONLY & dwOldState) ) { newItem.type = eCatRO; newItem.pDocStore = _xDocStore.GetPointer(); if ( 0 != (CICAT_STOPPED & dwOldState) ) { ciDebugOut(( DEB_ITRACE, "DoSetCatState, restarting a stopped cat\n" )); ciDebugOut(( DEB_ITRACE, "DoSetCatState.. newItem.StoppedCat is %ws\n", request.GetCatName() )); _requestQueue.AddSCItem( &newItem, request.GetCatName() ); } else _requestQueue.AddSCItem( &newItem , 0 ); CLock lock( g_mtxStartStop ); sc = StopFWCiSvcWork( eCatRO ); } ciDebugOut(( DEB_ITRACE, "Done setting CICAT_READONLY\n" )); break; case CICAT_WRITABLE: if ( 0 == (CICAT_WRITABLE & dwOldState) ) { sc = xLocator->IsMarkedReadOnly( request.GetCatName(), &fAbsUnWritable ); if ( FAILED(sc) ) THROW( CException(sc) ); ciDebugOut(( DEB_ITRACE, "DoSetCatState.. IsMarkedReadOnly is %d\n", fAbsUnWritable )); if ( CICAT_STOPPED == dwOldState ) { if ( !fAbsUnWritable ) { sc = xLocator->IsVolumeOrDirRO( request.GetCatName(), &fAbsUnWritable ); if ( FAILED(sc) ) THROW( CException(sc) ); } ciDebugOut(( DEB_ITRACE, "DoSetCatState.. fAbsUnWritable is %d\n", fAbsUnWritable )); } if ( fAbsUnWritable ) { //Catalog cannot be open for r/w, open as r/o instead //can't throw, must finish work newItem.type = eCatRO; } else newItem.type = eCatW; newItem.pDocStore = _xDocStore.GetPointer(); if ( CICAT_STOPPED == dwOldState ) { ciDebugOut(( DEB_ITRACE, "DoSetCatState, restarting a stopped cat\n" )); ciDebugOut(( DEB_ITRACE, "DoSetCatState.. newItem.StoppedCat is %ws\n", request.GetCatName() )); _requestQueue.AddSCItem( &newItem, request.GetCatName() ); } else _requestQueue.AddSCItem( &newItem, 0 ); CLock lock( g_mtxStartStop ); sc = StopFWCiSvcWork( newItem.type ); } break; case CICAT_NO_QUERY: if ( 0 == (CICAT_NO_QUERY & dwOldState) ) { newItem.type = eNoQuery; sc = xLocator->IsMarkedReadOnly( request.GetCatName(), &fAbsUnWritable ); if ( FAILED(sc) ) THROW( CException(sc) ); ciDebugOut(( DEB_ITRACE, "DoSetCatState.. IsMarkedReadOnly is %d\n", fAbsUnWritable )); if ( 0 != (CICAT_STOPPED & dwOldState) ) { if ( !fAbsUnWritable ) { sc = xLocator->IsVolumeOrDirRO( request.GetCatName(), &fAbsUnWritable ); if ( FAILED(sc) ) THROW( CException(sc) ); } ciDebugOut(( DEB_ITRACE, "DoSetCatState(NoQuery).. fAbsUnWritable is %d\n", fAbsUnWritable )); } if ( fAbsUnWritable ) { //The catalog is absolutely unwritable, opening for ReadOnly instead //can't throw here, since work needs to be done. newItem.fNoQueryRW = FALSE; } else newItem.fNoQueryRW = TRUE; newItem.pDocStore = _xDocStore.GetPointer(); if ( 0 != (CICAT_STOPPED & dwOldState) ) _requestQueue.AddSCItem( &newItem, request.GetCatName() ); else _requestQueue.AddSCItem( &newItem, 0 ); CLock lock( g_mtxStartStop ); sc = StopFWCiSvcWork( newItem.type ); } break; default: THROW( CException( STATUS_INVALID_PARAMETER ) ); } } ciDebugOut(( DEB_ITRACE, "DoSetCatState sc is %d or %#x\n", sc )); if ( !SUCCEEDED(sc) ) THROW( CException(sc) ); cbToWrite = sizeof reply; return stateContinue; } //DoSetCatState //+------------------------------------------------------------------------- // // Member: CRequestServer::DoFetchValue, private // // Synopsis: Retrieves a value from the property cache // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoFetchValue( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMFetchValueIn &request = * (CPMFetchValueIn *) _Buffer(); request.ValidateCheckSum( GetClientVersion(), cbRequest ); WORKID wid = request.GetWID(); DWORD cbSoFar = request.GetSoFar(); DWORD cbPropSpec = request.GetPSSize(); DWORD cbChunk = request.GetChunkSize(); // if this is the first request for the value, fetch the value if ( 0 == cbSoFar ) { _xFetchedValue.Free(); _cbFetchedValueSoFar = 0; Win4Assert( isQWordAligned( request.GetPS() ) ); CMemDeSerStream stmDeser( request.GetPS(), cbPropSpec ); CFullPropSpec ps( stmDeser ); if ( !ps.IsValid() ) THROW( CException( E_OUTOFMEMORY ) ); PROPVARIANT var; if ( !_pQuery->FetchDeferredValue( wid, ps, var ) || var.vt == VT_EMPTY ) { // // FetchDeferredValue does a security check to make sure the client has access // to this wid. A hacker could pass in a wid not returned in a query. // CPMFetchValueOut &reply = * (CPMFetchValueOut *) _Buffer(); reply.ValueExists() = FALSE; cbToWrite = sizeof reply; return stateContinue; } // marshall the property value and save it in _xFetchedValue SPropVariant xvar( &var ); ULONG cb = 0; StgConvertVariantToProperty( &var, CP_WINUNICODE, 0, &cb, pidInvalid, FALSE, 0 ); _xFetchedValue.Init( cb ); StgConvertVariantToProperty( &var, CP_WINUNICODE, (SERIALIZEDPROPERTYVALUE *)_xFetchedValue.Get(), &cb, pidInvalid, FALSE, 0 ); } // send a chunk (or all) of the marshalled property value Win4Assert( cbSoFar == _cbFetchedValueSoFar ); Win4Assert( 0 != _xFetchedValue.Get() ); DWORD cbToGo = _xFetchedValue.SizeOf() - _cbFetchedValueSoFar; Win4Assert( sizeof CPMFetchValueOut < cbChunk ); DWORD cbValToWrite = __min( cbChunk - sizeof CPMFetchValueOut, cbToGo ); cbToWrite = sizeof CPMFetchValueOut + cbValToWrite; CPMFetchValueOut *pReply; if ( cbToWrite <= _BufferSize() ) { pReply = (CPMFetchValueOut *) _Buffer(); } else { _xTempBuffer.Init( cbToWrite ); pReply = new( _xTempBuffer.Get() ) CPMFetchValueOut; } pReply->ValueExists() = TRUE; pReply->ValueSize() = cbValToWrite; pReply->MoreExists() = ( cbValToWrite != cbToGo ); RtlCopyMemory( pReply->Value(), _xFetchedValue.Get() + _cbFetchedValueSoFar, cbValToWrite ); _cbFetchedValueSoFar += cbValToWrite; if ( !pReply->MoreExists() ) { _xFetchedValue.Free(); _cbFetchedValueSoFar = 0; } return stateContinue; } //DoFetchValue //+------------------------------------------------------------------------- // // Member: CRequestServer::DoWorkIdToPath, private // // Synopsis: Converts a wid to a path. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoWorkIdToPath( DWORD cbRequest, DWORD & cbToWrite ) { if ( 0 == _pQuery ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMWorkIdToPathIn &request = * (CPMWorkIdToPathIn *) _Buffer(); CFunnyPath funnyPath; _pQuery->WorkIdToPath( request.GetWorkId(), funnyPath ); CPMWorkIdToPathOut &reply = * (CPMWorkIdToPathOut *) _Buffer(); cbToWrite = sizeof reply; if ( 0 != funnyPath.GetActualLength() ) { reply.Any() = TRUE; ULONG cbPath = (funnyPath.GetActualLength()+1) * sizeof(WCHAR); RtlCopyMemory( reply.Path(), funnyPath.GetActualPath(), cbPath ); cbToWrite += cbPath; } else { reply.Any() = FALSE; } return stateContinue; } //DoWorkIdToPath //+------------------------------------------------------------------------- // // Member: CRequestServer::DoUpdateDocuments, private // // Synopsis: Handles an update documents message. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::DoUpdateDocuments( DWORD cbRequest, DWORD & cbToWrite ) { VerifyThreadHasAdminPrivilege(); if ( _xDocStore.IsNull() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CPMUpdateDocumentsIn &request = * (CPMUpdateDocumentsIn *) _Buffer(); XInterface xFsCiAdmin; SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin, xFsCiAdmin.GetQIPointer() ); if ( wcslen( request.GetRootPath() ) >= MAX_PATH ) THROW( CException( STATUS_INVALID_PARAMETER ) ); if ( SUCCEEDED(sc) ) sc = xFsCiAdmin->UpdateDocuments( request.GetRootPath(), request.GetFlag() ); if ( !SUCCEEDED(sc) ) THROW( CException(sc) ); return stateContinue; } //DoUpdateDocuments //+------------------------------------------------------------------------- // // Member: CRequestServer::HandleRequestNoThrow, private // // Synopsis: Processes a request. This method can't throw. If there // is any problem with the request, set the status code // in the message to reflect the problem. // // Arguments: [cbRequest] - Size of the request in _Buffer() // [cbToWrite] - On output, set to the # of bytes to write if // other than sizeof CProxyMessage. // // Returns: FALSE if the connection with the client should be terminated, // TRUE otherwise. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- RequestState CRequestServer::HandleRequestNoThrow( DWORD cbRequest, DWORD & cbToWrite ) { // buffers must be 8-byte aligned on both sides of the proxy or the // marshalling/unmarshalling will be inconsistent. Win4Assert( isQWordAligned( _Buffer() ) ); CProxyMessage &msg = * (CProxyMessage *) _Buffer(); // Require that messages sent from clients have status set to S_OK, // as the reply message is in the same buffer and there is no need // to re-write the S_OK. Win4Assert( S_OK == msg.GetStatus() ); RequestState state = stateContinue; TRY { Win4Assert( _xTempBuffer.SizeOf() > _BufferSize() || _xTempBuffer.IsNull() ); int iMsg = msg.GetMessage() - pmConnect; if ( ( iMsg < 0 ) || ( iMsg >= cProxyMessages ) ) THROW( CException( STATUS_INVALID_PARAMETER ) ); // Don't impersonate when not necessary -- it's slow XPipeImpersonation impersonate; if ( _afImpersonate[ iMsg ] ) impersonate.Impersonate( GetPipe() ); SetLastTouchedTime( GetTickCount() ); state = ( this->*( _aMsgFunctions[ iMsg ] ) )( cbRequest, cbToWrite ); prxDebugOut(( DEB_ITRACE, "finish msg %d, cb %d, sc 0x%x, to pipe 0x%x\n", msg.GetMessage(), cbToWrite, msg.GetStatus(), GetPipe() )); } CATCH( CException, ex ) { prxDebugOut(( DEB_ITRACE, "HandleRequestNoThrow rs 0x%x msg %d caught 0x%x\n", this, msg.GetMessage(), ex.GetErrorCode() )); msg.SetStatus( ex.GetErrorCode() ); cbToWrite = sizeof CProxyMessage; _xTempBuffer.Free(); } END_CATCH; return state; } //HandleRequestNoThrow //+------------------------------------------------------------------------- // // Member: CRequestServer::DoAPC, private // // Synopsis: This method is called when an i/o operation is completed, // whether successfully or not. If there was an error, // this method terminates the connection. // // Arguments: [dwError] - Win32 error code for i/o operation // [cbTransferred] - # of bytes read or written // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::DoAPC( DWORD dwError, DWORD cbTransferred ) { prxDebugOut(( DEB_ITRACE, "apc %s t 0x%x p 0x%x msg %d cb %d err %d\n", pipeStateRead == _state ? "read" : pipeStateWrite == _state ? "write" : "none", GetCurrentThreadId(), GetPipe(), * (int *) _Buffer(), cbTransferred, dwError )); // Ownership of the item is with the apc: either issue another i/o // or recycle the request server. TRY { // // If we are recursing processing APCs, defer this APC. // Why would we be processing APCs? Someone in the query path // probably used RPC, and due to a bug in RPC we hit this. // if ( _pWorkThread->IsProcessingAPC() ) { _dwDeferredAPCError = dwError; _cbDeferredAPCTransferred = cbTransferred; _pWorkThread->DeferAPC( this ); return; } // // Mark the thread as busy processing an APC and addref it so the // destructor can unmark it. // XAPCWorkerThread apcWorker( _workQueue, _pWorkThread ); // if the apc operation completed with an error, abort the connection if ( 0 != dwError ) QUIETTHROW( CException( HRESULT_FROM_WIN32( dwError ) ) ); if ( IsBeingRemoved() ) THROW( CException( STATUS_TOO_LATE ) ); if ( pipeStateRead == _state ) { // if the entire message didn't fit in the buffer (eg, there was // a large restriction) read the rest of the message. if ( _BufferSize() == cbTransferred ) { prxDebugOut(( DEB_ITRACE, "more data available\n" )); BYTE *p = ReadRemainingSync( _Buffer(), cbTransferred ); // // ReadRemainingSync returns 0 if there was an exact fit // into _Buffer, so there was nothing left to read. // if ( 0 != p ) { _xTempBuffer.Set( cbTransferred, p ); prxDebugOut(( DEB_ITRACE, "total msg cb: %d\n", cbTransferred )); // Only certain msgs expect the input buffer to be in // _xTempBuffer. Fail if it's not one of these msgs. int pm = * (int *) _xTempBuffer.Get(); Win4Assert( ( pmCreateQuery == pm ) || ( pmConnect == pm ) || ( pmSetBindings == pm ) ); if ( pmCreateQuery != pm && pmConnect != pm && pmSetBindings != pm ) THROW( CException( STATUS_INVALID_PARAMETER ) ); } } #if CIDBG == 1 prxDebugOut(( DEB_PRX_MSGS, "proxy read %d bytes at %p\n", cbTransferred, _ActiveBuffer() )); #endif // CIDBG == 1 #if CI_PIPE_TESTING if ( 0 != _requestQueue._pTraceRead ) (*_requestQueue._pTraceRead)( GetPipe(), cbTransferred, _ActiveBuffer() ); #endif // CI_PIPE_TESTING // The read completed, process the request DWORD cbToWrite = sizeof CProxyMessage; RequestState state = HandleRequestNoThrow( cbTransferred, cbToWrite ); if ( stateContinue == state ) { Win4Assert( cbToWrite >= sizeof CProxyMessage ); Win4Assert( ( cbToWrite <= _BufferSize() ) || ( !_xTempBuffer.IsNull() ) ); Win4Assert( ( cbToWrite > _BufferSize() ) || ( _xTempBuffer.IsNull() ) ); Win4Assert( ( _xTempBuffer.IsNull() ) || ( cbToWrite == _xTempBuffer.SizeOf() ) ); _state = pipeStateWrite; #if CIDBG == 1 prxDebugOut(( DEB_PRX_MSGS, "proxy writing %d bytes at %p\n", cbToWrite, _ActiveBuffer() )); #endif // CIDBG == 1 void * pvToWrite = _ActiveBuffer(); #if CI_PIPE_TESTING void * pvToWriteOrg = pvToWrite; DWORD cbToWriteOrg = cbToWrite; if ( 0 != _requestQueue._pTraceBefore ) (*_requestQueue._pTraceBefore)( GetPipe(), cbToWriteOrg, pvToWriteOrg, cbToWrite, pvToWrite ); #endif // CI_PIPE_TESTING Write( pvToWrite, cbToWrite, APCRoutine ); #if CI_PIPE_TESTING if ( 0 != _requestQueue._pTraceAfter ) (*_requestQueue._pTraceAfter)( GetPipe(), cbToWriteOrg, pvToWriteOrg, cbToWrite, pvToWrite ); #endif // CI_PIPE_TESTING if ( IsBeingRemoved() ) CancelIO(); } else if ( stateDisconnect == state ) { Win4Assert( _xTempBuffer.IsNull() ); _state = pipeStateNone; _requestQueue.RecycleRequestServerNoThrow( this ); } else { Win4Assert( statePending == state ); } } else { Win4Assert( pipeStateWrite == _state ); // cleanup temp buffer if allocated for the write _xTempBuffer.Free(); // the write completed, so schedule another read _state = pipeStateRead; Read( _Buffer(), _BufferSize(), APCRoutine ); if ( IsBeingRemoved() ) CancelIO(); } } CATCH( CException, ex ) { prxDebugOut(( DEB_WARN, "exception in APC error %#x, this %#x, pipe %#x\n", ex.GetErrorCode(), this, GetPipe() )); // disconnect the pipe (and the query if it exists) _state = pipeStateNone; _requestQueue.RecycleRequestServerNoThrow( this ); } END_CATCH; } //DoAPC //+------------------------------------------------------------------------- // // Member: CRequestServer::APCRoutine, private, static // // Synopsis: This method is called when an i/o operation is completed. // // Arguments: [dwError] - Win32 error code for i/o operation // [cbTransferred] - # of bytes read or written // [pOverlapped] - Points to the overlapped for the i/o // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void WINAPI CRequestServer::APCRoutine( DWORD dwError, DWORD cbTransferred, LPOVERLAPPED pOverlapped ) { // It would be a programming error to take an exception here, and it // has never been hit. Leave the check in for checked builds. #if CIDBG == 1 TRY { #endif // The request server was saved in the hEvent field, which the // Win32 doc says is a good place for a user's APC data CRequestServer &serv = * (CRequestServer *) (CPipeServer *) ( pOverlapped->hEvent ); serv.DoAPC( dwError, cbTransferred ); #if CIDBG == 1 } CATCH( CException, e ) { prxDebugOut(( DEB_ERROR, "exception in APCRoutine: 0x%x\n", e.GetErrorCode() )); Win4Assert( !"caught an unexpected exception in an apc" ); } END_CATCH; #endif } //APCRoutine //+------------------------------------------------------------------------- // // Member: CRequestServer::CancelAPCRoutine, private/static // // Synopsis: Called in an APC when a connection should be cancelled // // Arguments: [dwParam] - The request server // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void WINAPI CRequestServer::CancelAPCRoutine( DWORD_PTR dwParam ) { TRY { CRequestServer * pServer = (CRequestServer *) dwParam; XInterface xServer( pServer ); prxDebugOut(( DEB_ITRACE, "Canceling server 0x%x io pipe 0x%x\n", pServer, pServer->GetPipe() )); // Freeing a pending query will result in the request server // being cleaned up, since _fShutdown/BeingRemoved is TRUE. // Cancelling pending IO will cause the IO APC routine to be // called with an error completion status, which will clean up // the request server. if ( pipeStatePending == pServer->_state ) pServer->FreeQuery(); else if ( pipeStateNone != pServer->_state ) pServer->CancelIO(); } CATCH( CException, e ) { prxDebugOut(( DEB_ERROR, "cancelapc caught 0x%x\n", e.GetErrorCode() )); Win4Assert( !"CancelAPC caught an exception" ); } END_CATCH } //CancelAPCRoutine //+------------------------------------------------------------------------- // // Member: CRequestServer::Quiesce, private // // Synopsis: Called by the APC when a query with notifications quiesces // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::Quiesce() { TRY { prxDebugOut(( DEB_ITRACE, "Quiescing server 0x%x io pipe 0x%x\n", this, GetPipe() )); Win4Assert( pipeStatePending == _state ); // If we're shutting down or the server is disconnecting the client // for being idle for too long, abort now. if ( _requestQueue.IsShutdown() || IsBeingRemoved() ) THROW( CException( STATUS_TOO_LATE ) ); // the long-running operation completed, so do the write _xTempBuffer.Free(); Win4Assert( 0 != _pQuery ); _state = pipeStateWrite; // // If the first call to Quiesce is to tell us that the asynch // query execution failed, free the query. // if ( FAILED ( _scPendingStatus ) ) FreeQuery(); CProxyMessage &msg = * (CProxyMessage *) _Buffer(); msg.SetStatus( _scPendingStatus ); Write( (BYTE *) _Buffer(), _cbPendingWrite, APCRoutine ); // Note that the state of these may have changed after the check above if ( IsBeingRemoved() ) CancelIO(); } CATCH( CException, ex ) { prxDebugOut(( DEB_ITRACE, "quiesce write error 0x%x, pipe 0x%x\n", ex.GetErrorCode(), GetPipe() )); _state = pipeStateNone; _requestQueue.RecycleRequestServerNoThrow( this ); } END_CATCH; } //Quiesce //+------------------------------------------------------------------------- // // Member: CRequestServer::QuiesceAPCRoutine, private/static // // Synopsis: Called in an APC when a query with notifications quiesces // // Arguments: [dwParam] - The request server // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void WINAPI CRequestServer::QuiesceAPCRoutine( ULONG_PTR dwParam ) { CRequestServer & Server = * (CRequestServer *) dwParam; Server.Quiesce(); } //QuiesceAPCRoutine //+------------------------------------------------------------------------- // // Member: CRequestServer::QueryQuiesced, public // // Synopsis: Called when a synchronous query is quiesced, so the pending // request can be completed by the thread that created the query. // // History: 16-Feb-97 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::QueryQuiesced( BOOL fSuccess, SCODE sc ) { // only complete the work if the query is truly quiesced if ( fSuccess || _requestQueue.IsShutdown() || IsBeingRemoved() ) { Win4Assert( IsBeingRemoved() || 0 != _pQuery || _requestQueue.IsShutdown() ); Win4Assert( pipeStatePending == _state ); _scPendingStatus = sc; _requestQueue.DecrementPendingItems(); QueueUserAPC( CRequestServer::QuiesceAPCRoutine, GetWorkerThreadHandle(), (ULONG_PTR) this ); } } //QueryQuiesced //+------------------------------------------------------------------------- // // Member: CRequestServer::CompleteNotification, public // // Synopsis: This is called by a query worker thread when a notification // should be delivered to the client. // // Arguments: [dwChangeType] - The notification // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::CompleteNotification( DWORD dwChangeType ) { // _pQuery can be 0 if the query is in the process of being deleted, // since _pQuery is set to 0 before it's actually deleted in FreeQuery(), // and queries that haven't quiesced yet always quiesce on destruction. if ( 0 != _pQuery ) { CPMSendNotifyOut notify( dwChangeType ); WriteSync( ¬ify, sizeof notify ); } } //CompleteNotification //+------------------------------------------------------------------------- // // Member: CRequestServer::Cleanup, public // // Synopsis: Frees data and refcounts associated with the request server. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestServer::Cleanup() { FreeQuery(); _xTempBuffer.Free(); _fClientIsRemote = FALSE; _state = pipeStateNone; _cbPendingWrite = 0; _scPendingStatus = S_OK; _xClientMachine.Free(); _xClientUser.Free(); // Remove the refcount on the thread since it no longer has to process // APCs on behalf of this request server. if ( 0 != _pWorkThread ) { _hWorkThread = INVALID_HANDLE_VALUE; _workQueue.Release( _pWorkThread ); _pWorkThread = 0; } _xDbProperties.Free(); if ( !_xDocStore.IsNull() ) { XInterface xAdviseStatus; SCODE sc = _xDocStore->QueryInterface( IID_ICiCAdviseStatus, xAdviseStatus.GetQIPointer() ); // It would be a bug if this QI actually failed. if ( S_OK == sc ) xAdviseStatus->DecrementPerfCounterValue( CI_PERF_RUNNING_QUERIES ); _xDocStore.Free(); } } //Cleanup //+------------------------------------------------------------------------- // // Member: CRequestQueue::CRequestQueue, public // // Synopsis: Constructs a request queue // // Arguments: [cMaxCachedServerItems] -- max # of cached pipe objects // [cMaxSimultaneousRequests] -- max # of simultaneous pipes // // History: 16-Sep-96 dlee Created. // 30-Mar-98 kitmanh Initialized _fNetPause // and _fNetContinue to FALSE // //-------------------------------------------------------------------------- CRequestQueue::CRequestQueue( unsigned cMaxCachedServerItems, unsigned cMaxSimultaneousRequests, unsigned cmsDefaultClientTimeout, BOOL fMinimizeWorkingSet, unsigned cMinClientIdleTime, unsigned cmsStartupDelay, const GUID & guidDocStoreClient ) : _guidDocStoreClient( guidDocStoreClient ), _cMaxSimultaneousRequests( cMaxSimultaneousRequests ), _cmsDefaultClientTimeout( cmsDefaultClientTimeout ), _fMinimizeWorkingSet( fMinimizeWorkingSet ), _cMinClientIdleTime( cMinClientIdleTime ), _cmsStartupDelay( cmsStartupDelay ), _fDocStoresOpen( FALSE ), _fShutdown( FALSE ), _cBusyItems( 0 ), _cPendingItems( 0 ), _workQueue( 1, CWorkQueue::workQueueRequest ), _tableActiveServers( cMaxSimultaneousRequests ), _queueCachedServers( cMaxCachedServerItems ), _fNetPause( FALSE ), _fNetContinue( FALSE ), _fNetStop( FALSE ) { #if CI_PIPE_TESTING _pTraceBefore = 0; _pTraceAfter = 0; _pTraceRead = 0; _hTraceDll = LoadLibraryEx( L"cipipetrace.dll", 0, LOAD_WITH_ALTERED_SEARCH_PATH ); if ( 0 != _hTraceDll ) { _pTraceBefore = (PipeTraceServerBeforeCall) GetProcAddress( _hTraceDll, "ServerBefore" ); _pTraceAfter = (PipeTraceServerAfterCall) GetProcAddress( _hTraceDll, "ServerAfter" ); _pTraceRead = (PipeTraceServerReadCall) GetProcAddress( _hTraceDll, "ServerRead" ); } #endif // CI_PIPE_TESTING _workQueue.Init(); // // Read the worker queue registry settings in the SYSTEM context // and initialize the parameters. // ULONG cMaxActiveThreads, cMinIdleThreads; _workQueue.GetWorkQueueRegParams( cMaxActiveThreads, cMinIdleThreads ); _workQueue.RefreshParams( cMaxActiveThreads, cMinIdleThreads ); // // The security checks for the pipe are done in this order: // The system account can create instances of this pipe. // No-one can write DAC or OWNER, or create pipe instances. // Everyone can read, write, and synchronize around this pipe. // // Actual query result and admin security checking is done when the // requests are made, and are based on the pipe impersonation. // // This data really is const, but the Win32 security APIs don't do const. // static SID sidLocalSystem = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID }; static SID sidWorld = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, SECURITY_WORLD_RID }; // NTRAID#DB-NTBUG9-83834-2000/07/31-dlee No way to protect Indexing Service named pipes. // FILE_CREATE_PIPE_INSTANCE no longer works, so we can't include // it in the AceData. There is no good way to secure the pipe. ACE_DATA AceData[] = { //{ ACCESS_ALLOWED_ACE_TYPE, 0, 0, // FILE_CREATE_PIPE_INSTANCE, // &sidLocalSystem }, { ACCESS_DENIED_ACE_TYPE, 0, 0, WRITE_DAC | WRITE_OWNER /*| FILE_CREATE_PIPE_INSTANCE */, &sidWorld }, { ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &sidWorld }, }; const ULONG cAces = sizeof AceData / sizeof ACE_DATA; CiCreateSecurityDescriptor( AceData, cAces, &sidLocalSystem, &sidLocalSystem, _xSecurityDescriptor ); } //CRequestQueue //+------------------------------------------------------------------------- // // Member: CRequestQueue::RecycleRequestServerNoThrow, public // // Synopsis: A request server has become available. Either delete it // or cache it for use later. There is a transfer of ownership. // // Arguments: [pServer] - The request server to be reused or deleted // // History: 16-Sep-96 dlee Created. // // Notes: This method must not throw. // //-------------------------------------------------------------------------- void CRequestQueue::RecycleRequestServerNoThrow( CRequestServer * pServer ) { Win4Assert( pServer->NoOutstandingAPCs() ); CServerItem item( pServer ); BOOL fDisconnectOk = pServer->Disconnect(); if ( !fDisconnectOk ) { prxDebugOut(( DEB_WARN, "Disconnect of server 0x%x failed %d\n", pServer, GetLastError() )); } pServer->Cleanup(); // Only try to reuse the server if the refcount is 1 (it's available). // The only reason the refcount might not be 1 is if we took an exception // in DoIt(). Wake up the main thread if a request server is available. if ( pServer->IsAvailable() ) { // The Add will fail if the cache is full or there is an admin // operation like shutdown going on. if ( _queueCachedServers.Add( item ) ) _event.Set(); } long cCurrent = InterlockedDecrement( &_cBusyItems ); // If no request servers are to be cached and we've fallen just // under the ceiling on the # of items, set the event so the main // thread can wake up and create another request server. if ( ( !_fShutdown ) && ( 0 == _queueCachedServers.MaxRequests() ) && ( cCurrent == (long) ( _cMaxSimultaneousRequests - 1 ) ) ) _event.Set(); } //RecycleRequestServerNoThrow //+------------------------------------------------------------------------- // // Member: CRequestQueue::OpenAllDocStores, private // // Synopsis: Tells the DocStore admin to open all docstores if they // aren't open yet. // // History: 19-Jun-98 dlee Created. // //-------------------------------------------------------------------------- void CRequestQueue::OpenAllDocStores() { if ( !_fDocStoresOpen ) { XInterface xLocator( DocStoreLocator() ); Win4Assert( !xLocator.IsNull() ); // Ignore failures to open docstores -- the docstore is responsible xLocator->OpenAllDocStores(); _fDocStoresOpen = TRUE; } } //OpenAllDocStores //+------------------------------------------------------------------------- // // Member: CRequestQueue::DocStoreLocator, public // // Synopsis: Retrieves the Doc Store Locator for the client // // History: 19-Jun-98 dlee Created. // //-------------------------------------------------------------------------- ICiCDocStoreLocator * CRequestQueue::DocStoreLocator() { return g_svcDocStoreLocator.Get( _guidDocStoreClient ); } //DocStoreLocator //+------------------------------------------------------------------------- // // Member: CRequestQueue::DoWork, public // // Synopsis: This is the main loop for the CI service. It makes // available pipes to which clients can connect. When the // _evtStateChange event is triggered, the method exits. // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- void CRequestQueue::DoWork() { // Throw away all that startup code SetProcessWorkingSetSize( GetCurrentProcess(), ~0, ~0 ); HANDLE ah[2]; ah[0] = _evtStateChange.GetHandle(); do { CServerItem item; TRY { // If a cached server item is available, use it. Otherwise, // create a non-cached item if we are under the limit. prxDebugOut(( DEB_ITRACE, "cached/busy items: %d / %d\n", _queueCachedServers.Count(), _cBusyItems )); if ( ( !_queueCachedServers.AcquireTop( item ) ) && ( _cBusyItems < (LONG) _cMaxSimultaneousRequests ) ) item.Create( CI_SERVER_PIPE_NAME, _cmsDefaultClientTimeout, *this, _workQueue ); if ( 0 != item.Get() ) { // make sure it doesn't get thrown away right away item.Get()->SetLastTouchedTime( GetTickCount() ); InterlockedIncrement( & _cBusyItems ); BOOL fConnected = item.Get()->Connect(); // if it's not connected yet, we have to wait on an event if ( !fConnected ) { prxDebugOut(( DEB_ITRACE, "waiting for connect of pipe 0x%x\n", item.Get()->GetPipe() )); ah[1] = item.Get()->GetEvent(); DWORD dwTimeout = _fMinimizeWorkingSet ? 30000 : INFINITE; DWORD dw; do { // // If catalogs haven't been opened yet due to the // startup delay and it's a good time, open them. // if ( !_fDocStoresOpen ) { if ( GetTickCount() >= _cmsStartupDelay ) { OpenAllDocStores(); } else { dwTimeout = 0; DWORD dwTC = GetTickCount(); if ( _cmsStartupDelay > dwTC ) dwTimeout += ( _cmsStartupDelay - dwTC ); } } // event 0 -- shutdown // event 1 -- a client connected dw = WaitForMultipleObjectsEx( 2, ah, FALSE, dwTimeout, FALSE ); // if an event triggered, handle it if ( WAIT_TIMEOUT != dw ) break; // If there isn't anything else going on, trim our // working set, and back off on the next time if ( 1 == _cBusyItems ) { dwTimeout = 120000; // 2 minutes SetProcessWorkingSetSize( GetCurrentProcess(), ~0, ~0 ); } } while ( TRUE ); Win4Assert( ( 0 == dw ) || ( 1 == dw ) ); if ( 0 == dw ) { item.Free(); InterlockedDecrement( & _cBusyItems ); break; // out of the do loop } else if ( 1 != dw ) THROW( CException() ); item.Get()->ResetEvent(); } // // Open the docstores now since the connection will not // force in a catalog if it's for administration. // OpenAllDocStores(); _workQueue.Add( item.Get() ); item.Acquire(); // // Fix for 123796. If we have no more request servers left // we should attempt to free one from an idle client. // if ( _cBusyItems == (LONG) _cMaxSimultaneousRequests ) WrestReqServerFromIdleClient(); } else { // // Wait for a request server to become available, either // in the cache or under the max ceiling. // event 0 -- shutdown // event 1 -- request server is available // timeout: look for an idle connection to destroy // prxDebugOut(( DEB_ITRACE, "wait for RequestServer\n" )); DWORD dwTimeout = ( _cBusyItems == (LONG) _cMaxSimultaneousRequests ) ? 10000 : INFINITE; ah[1] = _event.GetHandle(); DWORD dw = WaitForMultipleObjectsEx( 2, ah, FALSE, dwTimeout, FALSE ); prxDebugOut(( DEB_ITRACE, "wfsoex wakeup: %d\n", dw )); Win4Assert(( 0 == dw || 1 == dw || WAIT_TIMEOUT == dw)); if ( WAIT_TIMEOUT == dw ) WrestReqServerFromIdleClient(); else if ( 0 == dw ) break; // out of the do loop else if ( 1 != dw ) THROW( CException() ); } } CATCH( CException, ex ) { prxDebugOut(( DEB_WARN, "DoWork exception error 0x%x\n", ex.GetErrorCode() )); if ( 0 != item.Get() ) { item.Free(); InterlockedDecrement( & _cBusyItems ); } } END_CATCH; } while ( TRUE ); Shutdown(); } //DoWork //+------------------------------------------------------------------------- // // Member: CRequestQueue::WrestReqServerFromIdleClient, private // // Synopsis: Looks for connection that have been idle for more than // a predetermined amt of time. Flags the worst offender // and kicks it off to pave way for a new connection. // // History: 03-Feb-98 KrishnaN Created. // //-------------------------------------------------------------------------- void CRequestQueue::WrestReqServerFromIdleClient() { // look for the most idle connection that crossed the threshold // // We could be a little more efficient if we kick out the // first eligible idle client instead of looking for the most // idle one. But this is infrequent enough and not a big enough // problem that it's worth fixing. // XInterface xIdle; { CRequestServer * pMostIdleServer = 0; DWORD dwCurrentTick = GetTickCount(); DWORD dwMaxIdleTime = 0; CLock lock( _mutex ); for ( ULONG i = 0; i < _tableActiveServers.Size(); i++ ) { CRequestServer *pServer = _tableActiveServers.GetEntry( i ); if ( !_tableActiveServers.IsFree( pServer ) && INVALID_HANDLE_VALUE != pServer->GetWorkerThreadHandle() ) { DWORD dwLast = pServer->GetLastTouchedTime(); // The windows tick count wraps around every 50 days. // Unsigned arithmetic accounts for that, so we don't have to. DWORD dwIdleTime = dwCurrentTick - dwLast; if ( dwIdleTime > _cMinClientIdleTime && dwIdleTime > dwMaxIdleTime ) { dwMaxIdleTime = dwIdleTime; pMostIdleServer = pServer; } } } if ( 0 != pMostIdleServer ) { pMostIdleServer->AddRef(); xIdle.Set( pMostIdleServer ); } } if ( !xIdle.IsNull() ) { // Make sure this isn't in the cache or it won't get deleted _queueCachedServers.DisableAdditions(); FreeCachedServers(); // Disconnect this most-idle connection CEventSem evt; xIdle->BeingRemoved( &evt ); // Attempt to cancel it if it's still associated with a worker thread HANDLE hThrd = xIdle->GetWorkerThreadHandle(); if ( INVALID_HANDLE_VALUE != hThrd ) { DWORD dwRet = QueueUserAPC( CRequestServer::CancelAPCRoutine, hThrd, (ULONG_PTR) xIdle.GetPointer() ); Win4Assert( 0 != dwRet ); xIdle.Acquire(); } // Wait for the connection to completely go away xIdle.Free(); evt.Wait(); _queueCachedServers.EnableAdditions(); prxDebugOut(( DEB_ITRACE, "Successfully disconnected an idle client.\n" )); } } //WrestReqServerFromIdleClient //+------------------------------------------------------------------------- // // Member: CRequestQueue::FreeCachedServers, private // // Synopsis: Removes all cached request servers // // History: 01-July-99 dlee Created from existing code // //-------------------------------------------------------------------------- void CRequestQueue::FreeCachedServers() { CServerItem item; while ( _queueCachedServers.AcquireTop( item ) ) item.Free(); } //FreeCachedServers //+------------------------------------------------------------------------- // // Member: CRequestQueue::ShutdownActiveServers, private // // Synopsis: Cancels pending IO on each active request server. It may // be that a given request server has no IO pending, and // that will be taken care of when the IO is requested. // // Arguments: [pDocStore] - pointer to a docstore // if pDocStore == NULL, shutdown all active servers // // History: 19-May-97 dlee Created. // 24-Apr-98 kitmanh Shut down active servers associated // with the specified docstore // // Note: Always addref to everyone in the first loop, since the // in the 2nd loop, the smart pointer xServer will release // the pServer when it's out of scope, whether the docstore // matches or not. // //-------------------------------------------------------------------------- void CRequestQueue::ShutdownActiveServers( ICiCDocStore * pDocStore ) { ciDebugOut(( DEB_ITRACE, "ShutdownActiveServers is called\n" )); // AddRef all outstanding request servers so they won't go away { CLock lock( _mutex ); for ( ULONG i = 0; i < _tableActiveServers.Size(); i++ ) { CRequestServer *pServer = _tableActiveServers.GetEntry( i ); if ( !_tableActiveServers.IsFree( pServer ) ) pServer->AddRef(); } } // Make sure whatever request servers are freed aren't in the cache _queueCachedServers.DisableAdditions(); FreeCachedServers(); // // For each of the active request servers, file an APC so that the io // for that thread will be canceled by that thread, and so that // pending queries are cancelled. No items will be added to // _tableActiveServers during this loop, but items may be deleted. // for ( ULONG i = 0; i < _tableActiveServers.Size(); i++ ) { CRequestServer *pServer = _tableActiveServers.GetEntry( i ); if ( !_tableActiveServers.IsFree( pServer ) ) { XInterface xServer( pServer ); // The thread handle will be invalid if no worker thread is yet // associated or the association has been terminated. In either // case by definition there can be no outstanding APCs. // Note that the thread can't go away from under us since all // threads in the worker pool were addref'ed earlier // Disconnect it even if it has no docstore since it may be // processing a connect and be about to get a docstore. if ( ( 0 == pDocStore ) || ( pServer->GetDocStore() == 0 ) || ( pServer->GetDocStore() == pDocStore ) ) { CEventSem evt; pServer->BeingRemoved( &evt ); HANDLE hThread = pServer->GetWorkerThreadHandle(); if ( INVALID_HANDLE_VALUE != hThread && QueueUserAPC( CRequestServer::CancelAPCRoutine, hThread, (ULONG_PTR) pServer ) ) xServer.Acquire(); // Wait for the client to stop using the docstore xServer.Free(); evt.Wait(); } } } _queueCachedServers.EnableAdditions(); } //ShutdownActiveServers //+------------------------------------------------------------------------- // // Method: CRequestQueue::Shutdown // // Synopsis: Cleans up after the request queue. Can't throw. // handles both net stop and net pause // // History: 16-Sep-96 dlee Created. // 30-Mar-98 kitmanh Don't shutdown workqueue if we're // doing a net pause or a net continue // 06-25-98 kitmanh Update // //-------------------------------------------------------------------------- void CRequestQueue::Shutdown() { prxDebugOut(( DEB_ITRACE, "Shutdown\n" )); // Make sure the worker threads don't go away _workQueue.AddRefWorkThreads(); unsigned i = 0; SCWorkItem WorkItem; { CLock lock( _mutex ); if ( _fNetStop ) _fShutdown = TRUE; if ( _stateChangeArray.Count() > 0 ) WorkItem = _stateChangeArray.Get(i); else { ciDebugOut(( DEB_ITRACE, "Shutdown:: eNetStop\n" )); // No workitems exist, must be net stop, pause or continue // create a stop work item with NULL docstore WorkItem.type = eNetStop; // note: workitem.type is not really // important here ciDebugOut(( DEB_ITRACE, "WorkItem.type is %d\n", WorkItem.type )); WorkItem.pDocStore = 0; WorkItem.StoppedCat = 0; } } ciDebugOut(( DEB_ITRACE, "WorkItem.Count is %d\n", _stateChangeArray.Count() )); do { ciDebugOut(( DEB_ITRACE, "i is %d\n", i )); // now loop thru the workitem list and do the work i++; if ( 0 == WorkItem.StoppedCat ) ShutdownActiveServers( WorkItem.pDocStore ); { CLock lock( _mutex ); if ( i < _stateChangeArray.Count() ) WorkItem = _stateChangeArray.Get(i); } } while ( i < _stateChangeArray.Count() ); if ( _fNetStop || _fNetPause || _fNetContinue ) { if ( _stateChangeArray.Count() > 0 ) { // No workitem has been created at the top of the function, // thus need to run ShutdownActiveServers here ShutdownActiveServers( 0 ); } Win4Assert( !_queueCachedServers.Any() ); Win4Assert( !_tableActiveServers.Any() ); Win4Assert( 0 == _cBusyItems ); Win4Assert( 0 == _cPendingItems ); } _workQueue.ReleaseWorkThreads(); if ( _fNetStop ) _workQueue.Shutdown(); } //Shutdown //+--------------------------------------------------------------------------- // // Function: StartFWCiSvcWork // // Synopsis: Is the main server loop. Is called by the cisvc to receive // requests and demultiplex on them. // // Arguments: [lock] -- to be released when it's ok to call StopFWCiSvcWork // // History: 1-30-97 srikants Created // 4-13-98 kitmanh Moved the creation of CRequestQueue to // StartCisvcWork and passed the pointer // in from StartCisvcWork to initialize // g_pRequestQueue // // Notes: Must be called in the SYSTEM context without any impersonation. // //---------------------------------------------------------------------------- SCODE StartFWCiSvcWork( CReleasableLock & lock, CRequestQueue * pRequestQueue, CEventSem & evt ) { ciDebugOut(( DEB_ITRACE, "StartFWCiSvcWork is called\n" )); SCODE sc = S_OK; TRY { ciDebugOut(( DEB_ITRACE, "NetPaused == %d and NetContinued == %d\n", pRequestQueue->IsNetPause(), pRequestQueue->IsNetContinue() )); g_pRequestQueue = pRequestQueue; evt.Set(); lock.Release(); ciDebugOut(( DEB_ITRACE, "evtPauseContinue is set\n" )); ciDebugOut(( DEB_ITRACE, "StartFWCisvcWork.. About to DoWork()\n" )); g_pRequestQueue->DoWork(); ciDebugOut(( DEB_ITRACE, "StartFWCisvcWork.. Just fell out from DoWork\n" )); ciDebugOut(( DEB_ITRACE, "g_pRequestQueue->IsShutdown() is %d\n", g_pRequestQueue->IsShutdown() )); // Check if a netpause or netcontinue is requested during DoWork() if ( g_pRequestQueue->IsShutdown() && ! ( g_pRequestQueue->IsNetPause() || g_pRequestQueue->IsNetContinue() ) ) { g_svcDocStoreLocator.Shutdown(); // // Shutdown the Content Index work queue. // TheWorkQueue.Shutdown(); } } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Terminating - DoCiSvcServerWork(). Error 0x%X\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH ciDebugOut(( DEB_ITRACE, "Request lock for resetting g_pRequestQueue to 0\n" )); lock.Request(); ciDebugOut(( DEB_ITRACE, "Got lock\n" )); g_pRequestQueue = 0; ciDebugOut(( DEB_ITRACE, "About to release lock\n" )); lock.Release(); return sc; } //StartFWCiSvcWork //+--------------------------------------------------------------------------- // // Function: StopFWCiSvcWork // // Synopsis: Stops the server work. It is the Shutdown method called by // cisvc to stop the work. // // Arguments: [type] -- type of work to do // [wcVol] -- volume letter (for eLockVol only) // // History: 1-30-97 srikants Created // 3-30-98 kitmanh If we're doing a net pause, set // _fNetPause member in the CRequestQueue // //---------------------------------------------------------------------------- SCODE StopFWCiSvcWork( ECiSvcActionType type, CReleasableLock * pLock, CEventSem * pEvt, WCHAR wcVol ) { ciDebugOut(( DEB_ITRACE, "StopFWCiSvcWork.. type == %d\n", type )); SCODE sc = S_OK; // for the case where a shutdown occurs while the service is restarting // for pausing, continuing, stopping a catalog if ( eNetStop == type && 0 == g_pRequestQueue ) { //block until g_pRequestQueue is non-zero ciDebugOut(( DEB_ITRACE, "0 == g_pRequestQueue\n" )); ciDebugOut(( DEB_ITRACE, "StopFWCiSvcWork: Release the lock\n" )); ciDebugOut(( DEB_ITRACE, "Block on g_pevtPauseContinue for eNetStop\n" )); //this lock need to be released, so the lock can be grabbed by the //thread just done shutting down to finish restarting (calling //StartFWCiSvcWork) pLock->Release(); pEvt->Wait(); ciDebugOut(( DEB_ITRACE, "Done waiting on g_pevtPauseContinue\n" )); pLock->Request(); ciDebugOut(( DEB_ITRACE, "StopFWCiSvcWork requested the lock\n" )); } if ( 0 != g_pRequestQueue ) { ciDebugOut(( DEB_ITRACE, "0 != g_pRequestQueue\n" )); ciDebugOut(( DEB_ITRACE, "g_pRequestQueue->IsShutdown() is %d\n", g_pRequestQueue->IsShutdown() )); // Ignore all events if shutdown is initialized if ( g_pRequestQueue->IsShutdown() ) return STATUS_TOO_LATE; XInterface xLocator( g_pRequestQueue->DocStoreLocator() ); Win4Assert( !xLocator.IsNull() ); switch ( type ) { case eNetPause: g_pRequestQueue->SetNetPause(); break; case eNetContinue: g_pRequestQueue->SetNetContinue(); break; case eNetStop: g_pRequestQueue->SetNetStop(); break; case eCatRO: case eCatW: case eStopCat: case eNoQuery: break; case eLockVol: sc = xLocator->StopCatalogsOnVol( wcVol, g_pRequestQueue ); ciDebugOut(( DEB_ITRACE, "After StopCatalogsOnVol\n" )); break; case eUnLockVol: sc = xLocator->StartCatalogsOnVol( wcVol, g_pRequestQueue ); ciDebugOut(( DEB_ITRACE, "After StartCatalogsOnVol\n" )); break; default: Win4Assert( !"StopFWCiSvcWork is called with invalid ECisvcActionType" ); } ciDebugOut(( DEB_ITRACE, "Time to WakeForStateChange; type = %d\n", type )); g_pRequestQueue->WakeForStateChange(); } else { ciDebugOut(( DEB_ITRACE, "g_pRequestQueue->IsShutdown() is 0\n" )); } return sc; } //StopFWCiSvcWork