protected WSManPluginServerSession( WSManNativeApi.WSManPluginRequest creationRequestDetails, WSManPluginServerTransportManager transportMgr) { _syncObject = new Object(); this.creationRequestDetails = creationRequestDetails; this.transportMgr = transportMgr; transportMgr.PrepareCalled += new EventHandler<EventArgs>(this.HandlePrepareFromTransportManager); transportMgr.WSManTransportErrorOccured += new EventHandler<TransportErrorOccuredEventArgs>(this.HandleTransportError); }
internal bool EnableSessionToSendDataToClient( WSManNativeApi.WSManPluginRequest requestDetails, int flags, WSManNativeApi.WSManStreamIDSet_UnToMan streamSet, WSManPluginOperationShutdownContext ctxtToReport) { if (isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, lastErrorReported); return(false); } if ((streamSet == null) || (1 != streamSet.streamIDsCount)) { // only "stdout" is the supported output stream. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidOutputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidOutputStream, WSManPluginConstants.SupportedOutputStream)); return(false); } if (!string.Equals(streamSet.streamIDs[0], WSManPluginConstants.SupportedOutputStream, StringComparison.Ordinal)) { // only "stdout" is the supported output stream. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidOutputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidOutputStream, WSManPluginConstants.SupportedOutputStream)); return(false); } return(transportMgr.EnableTransportManagerSendDataToClient(requestDetails, ctxtToReport)); }
/// <summary> /// Main Routine for Connect on a Shell. /// Calls in server remotesessions ExecuteConnect to run the Connect algorithm /// This call is synchronous. i.e WSManOperationComplete will be called before the routine completes /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="inboundConnectInformation"></param> internal override void ExecuteConnect( WSManNativeApi.WSManPluginRequest requestDetails, // in int flags, // in WSManNativeApi.WSManData_UnToMan inboundConnectInformation) // in optional { if (null == inboundConnectInformation) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "inboundConnectInformation", "WSManPluginShellConnect")); return; } //not registering shutdown event as this is a synchronous operation. IntPtr responseXml = IntPtr.Zero; try { System.Byte[] inputData; System.Byte[] outputData; // Retrieve the string (Base64 encoded) inputData = ServerOperationHelpers.ExtractEncodedXmlElement( inboundConnectInformation.Text, WSManNativeApi.PS_CONNECT_XML_TAG); //this will raise exceptions on failure try { _remoteSession.ExecuteConnect(inputData, out outputData); //construct Xml to send back string responseData = String.Format(System.Globalization.CultureInfo.InvariantCulture, "<{0} xmlns=\"{1}\">{2}</{0}>", WSManNativeApi.PS_CONNECTRESPONSE_XML_TAG, WSManNativeApi.PS_XML_NAMESPACE, Convert.ToBase64String(outputData)); //TODO: currently using OperationComplete to report back the responseXml. This will need to change to use WSManReportObject //that is currently internal. WSManPluginInstance.ReportOperationComplete(requestDetails, WSManPluginErrorCodes.NoError, responseData); } catch (PSRemotingDataStructureException ex) { WSManPluginInstance.ReportOperationComplete(requestDetails, WSManPluginErrorCodes.PluginConnectOperationFailed, ex.Message); } } catch (OutOfMemoryException) { WSManPluginInstance.ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory); } finally { if (responseXml != IntPtr.Zero) { Marshal.FreeHGlobal(responseXml); } } return; }
internal abstract void ExecuteConnect( WSManNativeApi.WSManPluginRequest requestDetails, // in int flags, // in WSManNativeApi.WSManData_UnToMan inboundConnectInformation); // in optional
internal void DoClose( bool isShuttingDown, Exception reasonForClose) { if (_isClosed) { return; } lock (_syncObject) { if (_isClosed) { return; } _isClosed = true; _lastErrorReported = reasonForClose; if (!_isRequestPending) { // release threads blocked on the sending data to client if any _waitHandle.Set(); } } // only one thread will reach here // let everyone know that we are about to close try { RaiseClosingEvent(); foreach (var cmdTransportKvp in _activeCmdTransportManagers) { cmdTransportKvp.Value.Close(reasonForClose); } _activeCmdTransportManagers.Clear(); if (_registeredShutDownWaitHandle != null) { // This will not wait for the callback to complete. _registeredShutDownWaitHandle.Unregister(null); _registeredShutDownWaitHandle = null; } // Delete the context only if isShuttingDown != true. isShuttingDown will // be true only when the method is called from RegisterWaitForSingleObject // handler..in which case the context will be freed from the callback. if (_shutDownContext != null) { _shutDownContext = null; } // This might happen when client did not send a receive request // but the server is closing if (_requestDetails != null) { // Notify that no more data is being sent on this transport. WSManNativeApi.WSManPluginReceiveResult( _requestDetails.unmanagedHandle, (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_NO_MORE_DATA, WSManPluginConstants.SupportedOutputStream, IntPtr.Zero, WSManNativeApi.WSMAN_COMMAND_STATE_DONE, 0); WSManPluginInstance.ReportWSManOperationComplete(_requestDetails, reasonForClose); // We should not use request details again after reporting operation complete // so releasing the resource. Remember not to free this memory as this memory // is allocated and owned by WSMan. _requestDetails = null; } } finally { // dispose resources _waitHandle.Dispose(); } }
internal bool EnableTransportManagerSendDataToClient( WSManNativeApi.WSManPluginRequest requestDetails, WSManPluginOperationShutdownContext ctxtToReport) { _shutDownContext = ctxtToReport; bool isRegisterWaitForSingleObjectSucceeded = true; lock (_syncObject) { if (_isRequestPending) { // if a request is already pending..ignore this. WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return(false); } if (_isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, _lastErrorReported); return(false); } _isRequestPending = true; _requestDetails = requestDetails; if (Platform.IsWindows) { // Wrap the provided handle so it can be passed to the registration function SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); eventWaitHandle.SafeWaitHandle = safeWaitHandle; _registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback), _shutDownContext, -1, // INFINITE true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION? if (_registeredShutDownWaitHandle == null) { isRegisterWaitForSingleObjectSucceeded = false; } } // release thread waiting to send data to the client. _waitHandle.Set(); } if (!isRegisterWaitForSingleObjectSucceeded) { WSManPluginInstance.PerformCloseOperation(ctxtToReport); WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ShutdownRegistrationFailed, StringUtil.Format( RemotingErrorIdStrings.WSManPluginShutdownRegistrationFailed)); return(false); } return(true); }
// Report Operation Complete using the send request details. internal void ReportSendOperationComplete() { lock (_syncObject) { if (null != sendRequestDetails) { // report and clear the send request details WSManPluginInstance.ReportWSManOperationComplete(sendRequestDetails, lastErrorReported); sendRequestDetails = null; } } }
internal void Close(bool isShuttingDown) { if (Interlocked.Exchange(ref registeredShutdownNotification, 0) == 1) { // release the shutdown notification handle. if (null != registeredShutDownWaitHandle) { registeredShutDownWaitHandle.Unregister(null); registeredShutDownWaitHandle = null; } } // Delete the context only if isShuttingDown != true. isShuttingDown will // be true only when the method is called from RegisterWaitForSingleObject // handler..in which case the context will be freed from the callback. if (null != shutDownContext) { shutDownContext = null; } transportMgr.WSManTransportErrorOccured -= new EventHandler<TransportErrorOccuredEventArgs>(this.HandleTransportError); // We should not use request details again after so releasing the resource. // Remember not to free this memory as this memory is allocated and owned by WSMan. creationRequestDetails = null; // if already disposing..no need to let finalizer thread // put resources to clean this object. //System.GC.SuppressFinalize(this); // TODO: This is already called in Dispose(). }
internal void SendOneItemToSession( WSManNativeApi.WSManPluginRequest requestDetails, int flags, string stream, WSManNativeApi.WSManData_UnToMan inboundData) { if ((!String.Equals(stream, WSManPluginConstants.SupportedInputStream, StringComparison.Ordinal)) && (!String.Equals(stream, WSManPluginConstants.SupportedPromptResponseStream, StringComparison.Ordinal))) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidInputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidInputStream, WSManPluginConstants.SupportedInputStream)); return; } if (null == inboundData) { // no data is supplied..just ignore. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return; } if ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_BINARY != inboundData.Type) { // only binary data is supported WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidInputDatatype, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidInputStream, "WSMAN_DATA_TYPE_BINARY")); return; } lock (_syncObject) { if (true == isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, lastErrorReported); return; } // store the send request details..because the operation complete // may happen from a different thread. sendRequestDetails = requestDetails; } SendOneItemToSessionHelper(inboundData.Data, stream); // report operation complete. ReportSendOperationComplete(); }
internal void DoClose( bool isShuttingDown, Exception reasonForClose) { if (_isClosed) { return; } lock (_syncObject) { if (_isClosed) { return; } _isClosed = true; _lastErrorReported = reasonForClose; if (!_isRequestPending) { // release threads blocked on the sending data to client if any _waitHandle.Set(); } } // only one thread will reach here // let everyone know that we are about to close try { RaiseClosingEvent(); foreach (var cmdTransportKvp in _activeCmdTransportManagers) { cmdTransportKvp.Value.Close(reasonForClose); } _activeCmdTransportManagers.Clear(); if (null != _registeredShutDownWaitHandle) { // This will not wait for the callback to complete. _registeredShutDownWaitHandle.Unregister(null); _registeredShutDownWaitHandle = null; } // Delete the context only if isShuttingDown != true. isShuttingDown will // be true only when the method is called from RegisterWaitForSingleObject // handler..in which case the context will be freed from the callback. if (null != _shutDownContext) { _shutDownContext = null; } // This might happen when client did not send a receive request // but the server is closing if (null != _requestDetails) { // Notify that no more data is being sent on this transport. WSManNativeApi.WSManPluginReceiveResult( _requestDetails.unmanagedHandle, (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_NO_MORE_DATA, WSManPluginConstants.SupportedOutputStream, IntPtr.Zero, WSManNativeApi.WSMAN_COMMAND_STATE_DONE, 0); WSManPluginInstance.ReportWSManOperationComplete(_requestDetails, reasonForClose); // We should not use request details again after reporting operation complete // so releasing the resource. Remember not to free this memory as this memory // is allocated and owned by WSMan. _requestDetails = null; } } finally { // dispose resources _waitHandle.Dispose(); } }
internal bool EnableTransportManagerSendDataToClient( WSManNativeApi.WSManPluginRequest requestDetails, WSManPluginOperationShutdownContext ctxtToReport) { _shutDownContext = ctxtToReport; bool isRegisterWaitForSingleObjectSucceeded = true; lock (_syncObject) { if (_isRequestPending) { // if a request is already pending..ignore this. WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return false; } if (_isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, _lastErrorReported); return false; } _isRequestPending = true; _requestDetails = requestDetails; if (Platform.IsWindows) { // Wrap the provided handle so it can be passed to the registration function SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); _registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback), _shutDownContext, -1, // INFINITE true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION? if (null == _registeredShutDownWaitHandle) { isRegisterWaitForSingleObjectSucceeded = false; } } // release thread waiting to send data to the client. _waitHandle.Set(); } if (!isRegisterWaitForSingleObjectSucceeded) { WSManPluginInstance.PerformCloseOperation(ctxtToReport); WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ShutdownRegistrationFailed, StringUtil.Format( RemotingErrorIdStrings.WSManPluginShutdownRegistrationFailed)); return false; } return true; }