Esempio n. 1
0
        /// <summary>
        /// unlock the shell / command specified so that the shell / command
        /// starts sending data to the client.
        /// </summary>
        /// <param name="pluginContext"></param>
        /// <param name="requestDetails"></param>
        /// <param name="flags"></param>
        /// <param name="shellContext"></param>
        /// <param name="commandContext"></param>
        /// <param name="streamSet"></param>
        internal void EnableShellOrCommandToSendDataToClient(
            IntPtr pluginContext,
            WSManNativeApi.WSManPluginRequest requestDetails,
            int flags,
            IntPtr shellContext,
            IntPtr commandContext,
            WSManNativeApi.WSManStreamIDSet_UnToMan streamSet)
        {
            if (!validateIncomingContexts(requestDetails, shellContext, "EnableShellOrCommandToSendDataToClient"))
            {
                return;
            }

            SetThreadProperties(requestDetails);

            PSEtwLog.LogAnalyticInformational(PSEventId.ServerClientReceiveRequest,
                    PSOpcode.Open, PSTask.None,
                    PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
                    ((IntPtr)shellContext).ToString(),
                    ((IntPtr)commandContext).ToString(),
                    requestDetails.ToString());

            WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext);
            if (null == mgdShellSession)
            {
                ReportOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.InvalidShellContext,
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginInvalidShellContext)
                    );
                return;
            }

            WSManPluginOperationShutdownContext ctxtToReport = new WSManPluginOperationShutdownContext(pluginContext, shellContext, IntPtr.Zero, true);
            if (null == ctxtToReport)
            {
                ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory);
                return;
            }

            if (IntPtr.Zero == commandContext)
            {
                // the instruction is destined for shell (runspace) session. so let shell handle it
                if (mgdShellSession.EnableSessionToSendDataToClient(requestDetails, flags, streamSet, ctxtToReport))
                {
                    return;
                }
            }
            else
            {
                // the instruction is destined for command
                ctxtToReport.commandContext = commandContext;
                WSManPluginCommandSession mgdCmdSession = mgdShellSession.GetCommandSession(commandContext);

                if (null == mgdCmdSession)
                {
                    ReportOperationComplete(
                        requestDetails,
                        WSManPluginErrorCodes.InvalidCommandContext,
                        StringUtil.Format(
                            RemotingErrorIdStrings.WSManPluginInvalidCommandContext));
                    return;
                }

                if (mgdCmdSession.EnableSessionToSendDataToClient(requestDetails, flags, streamSet, ctxtToReport))
                {
                    return;
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// This gets called on a thread pool thread once Shutdown wait handle is notified.
        /// </summary>
        /// <param name="context"></param>
        internal void CloseShellOperation(
            WSManPluginOperationShutdownContext context)
        {
            PSEtwLog.LogAnalyticInformational(PSEventId.ServerCloseOperation,
                    PSOpcode.Disconnect, PSTask.None,
                    PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
                ((IntPtr)context.shellContext).ToString(),
                ((IntPtr)context.commandContext).ToString(),
                context.isReceiveOperation.ToString());

            WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(context.shellContext);
            if (null == mgdShellSession)
            {
                // this should never be the case. this will protect the service.
                //Dbg.Assert(false, "context.shellContext not matched");
                return;
            }

            SetThreadProperties(mgdShellSession.creationRequestDetails);
            // update the internal data store only if this is not receive operation.
            if (!context.isReceiveOperation)
            {
                DeleteFromActiveShellSessions(context.shellContext);
            }

            string errorMsg = StringUtil.Format(RemotingErrorIdStrings.WSManPluginOperationClose);
            System.Exception reasonForClose = new System.Exception(errorMsg);
            mgdShellSession.CloseOperation(context, reasonForClose);
        }
Esempio n. 3
0
        internal void CloseCommandOperation(
            WSManPluginOperationShutdownContext context)
        {
            PSEtwLog.LogAnalyticInformational(PSEventId.ServerCloseOperation,
                PSOpcode.Disconnect, PSTask.None,
                PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
                context.shellContext.ToString(),
                context.commandContext.ToString(),
                context.isReceiveOperation.ToString());

            WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(context.shellContext);
            if (null == mgdShellSession)
            {
                // this should never be the case. this will protect the service.
                //Dbg.Assert(false, "context.shellContext not matched");
                return;
            }
            SetThreadProperties(mgdShellSession.creationRequestDetails);
            mgdShellSession.CloseCommandOperation(context);
        }
Esempio n. 4
0
        /// <summary>
        /// Close the operation specified by the supplied context.
        /// </summary>
        /// <param name="context"></param>
        internal static void PerformCloseOperation(
            WSManPluginOperationShutdownContext context)
        {
            WSManPluginInstance pluginToUse = GetFromActivePlugins(context.pluginContext);

            if (null == pluginToUse)
            {
                return;
            }

            if (IntPtr.Zero == context.commandContext)
            {
                // this is targeted at shell
                pluginToUse.CloseShellOperation(context);
            }
            else
            {
                // shutdown is targeted at command
                pluginToUse.CloseCommandOperation(context);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Create a new shell in the plugin context.
        /// </summary>
        /// <param name="pluginContext"></param>
        /// <param name="requestDetails"></param>
        /// <param name="flags"></param>
        /// <param name="extraInfo"></param>
        /// <param name="startupInfo"></param>
        /// <param name="inboundShellInformation"></param>
        internal void CreateShell(
            IntPtr pluginContext,
            WSManNativeApi.WSManPluginRequest requestDetails,
            int flags,
            string extraInfo,
            WSManNativeApi.WSManShellStartupInfo_UnToMan startupInfo,
            WSManNativeApi.WSManData_UnToMan inboundShellInformation)
        {
            if (null == requestDetails)
            {
                // Nothing can be done because requestDetails are required to report operation complete
                PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete,
                    PSOpcode.Close, PSTask.None,
                    PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
                    "null",
                    Convert.ToString(WSManPluginErrorCodes.NullInvalidInput, CultureInfo.InvariantCulture),
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginNullInvalidInput,
                        "requestDetails",
                        "WSManPluginShell"),
                    String.Empty);
                return;
            }

            if ((null == requestDetails.senderDetails) ||
                (null == requestDetails.operationInfo))
            {
                ReportOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.NullInvalidInput,
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginNullInvalidInput,
                        "requestDetails",
                        "WSManPluginShell"));
                return;
            }

            if (null == startupInfo)
            {
                ReportOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.NullInvalidInput,
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginNullInvalidInput,
                        "startupInfo",
                        "WSManPluginShell"));
                return;
            }

            if ((0 == startupInfo.inputStreamSet.streamIDsCount) || (0 == startupInfo.outputStreamSet.streamIDsCount))
            {
                ReportOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.NullInvalidStreamSets,
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginNullInvalidStreamSet,
                        WSManPluginConstants.SupportedInputStream,
                        WSManPluginConstants.SupportedOutputStream));
                return;
            }

            if (String.IsNullOrEmpty(extraInfo))
            {
                ReportOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.NullInvalidInput,
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginNullInvalidInput,
                        "extraInfo",
                        "WSManPluginShell"));
                return;
            }

            WSManPluginInstance.SetThreadProperties(requestDetails);

            // check if protocolversion option is honored
            if (!EnsureOptionsComply(requestDetails))
            {
                return;
            }

            int result = WSManPluginConstants.ExitCodeSuccess;
            WSManPluginShellSession mgdShellSession;
            WSManPluginOperationShutdownContext context;
            System.Byte[] convertedBase64 = null;

            try
            {
                PSSenderInfo senderInfo = GetPSSenderInfo(requestDetails.senderDetails);

                // inbound shell information is already verified by pwrshplugin.dll.. so no need
                // to verify here.
                WSManPluginServerTransportManager serverTransportMgr;

                if (Platform.IsWindows)
                {
                    serverTransportMgr = new WSManPluginServerTransportManager(BaseTransportManager.DefaultFragmentSize, new PSRemotingCryptoHelperServer());
                }

                else
                {
                    serverTransportMgr = new WSManPluginServerTransportManager(BaseTransportManager.DefaultFragmentSize, null);
                }

                PSEtwLog.LogAnalyticInformational(PSEventId.ServerCreateRemoteSession,
                    PSOpcode.Connect, PSTask.None,
                    PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
                    requestDetails.ToString(), senderInfo.UserInfo.Identity.Name, requestDetails.resourceUri);
                ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession(senderInfo,
                    requestDetails.resourceUri,
                    extraInfo,
                    serverTransportMgr);

                if (null == remoteShellSession)
                {
                    WSManPluginInstance.ReportWSManOperationComplete(
                        requestDetails,
                        WSManPluginErrorCodes.SessionCreationFailed);
                    return;
                }

                context = new WSManPluginOperationShutdownContext(pluginContext, requestDetails.unmanagedHandle, IntPtr.Zero, false);
                if (null == context)
                {
                    ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory);
                    return;
                }

                // Create a shell session wrapper to track and service future interactions.
                mgdShellSession = new WSManPluginShellSession(requestDetails, serverTransportMgr, remoteShellSession, context);
                AddToActiveShellSessions(mgdShellSession);
                mgdShellSession.SessionClosed += new EventHandler<EventArgs>(HandleShellSessionClosed);

                if (null != inboundShellInformation)
                {
                    if ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT != inboundShellInformation.Type)
                    {
                        // only text data is supported
                        ReportOperationComplete(
                            requestDetails,
                            WSManPluginErrorCodes.InvalidInputDatatype,
                            StringUtil.Format(
                                RemotingErrorIdStrings.WSManPluginInvalidInputDataType,
                                "WSMAN_DATA_TYPE_TEXT"));
                        DeleteFromActiveShellSessions(requestDetails.unmanagedHandle);
                        return;
                    }
                    else
                    {
                        convertedBase64 = ServerOperationHelpers.ExtractEncodedXmlElement(
                            inboundShellInformation.Text,
                            WSManNativeApi.PS_CREATION_XML_TAG);
                    }
                }

                // now report the shell context to WSMan.
                PSEtwLog.LogAnalyticInformational(PSEventId.ReportContext,
                    PSOpcode.Connect, PSTask.None,
                    PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
                    requestDetails.ToString(), requestDetails.ToString());
                result = wsmanPinvokeStatic.WSManPluginReportContext(requestDetails.unmanagedHandle, 0, requestDetails.unmanagedHandle);

                if (WSManPluginConstants.ExitCodeSuccess != result)
                {
                    ReportOperationComplete(
                        requestDetails,
                        WSManPluginErrorCodes.ReportContextFailed,
                        StringUtil.Format(
                                RemotingErrorIdStrings.WSManPluginReportContextFailed));
                    DeleteFromActiveShellSessions(requestDetails.unmanagedHandle);
                    return;
                }
            }
            catch (System.Exception e)
            {
                CommandProcessorBase.CheckForSevereException(e);

                PSEtwLog.LogOperationalError(PSEventId.TransportError,
                    PSOpcode.Connect, PSTask.None, PSKeyword.UseAlwaysOperational, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000",
                    Convert.ToString(WSManPluginErrorCodes.ManagedException, CultureInfo.InvariantCulture), e.Message, e.StackTrace);

                DeleteFromActiveShellSessions(requestDetails.unmanagedHandle);
                ReportOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.ManagedException,
                    StringUtil.Format(
                        RemotingErrorIdStrings.WSManPluginManagedException,
                        e.Message));
                return;
            }

            bool isRegisterWaitForSingleObjectSucceeded = true;

            //always synchronize calls to OperationComplete once notification handle is registered.. else duplicate OperationComplete calls are bound to happen
            lock (mgdShellSession.shellSyncObject)
            {
                mgdShellSession.registeredShutdownNotification = 1;

                // Wrap the provided handle so it can be passed to the registration function
                EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

                if (Platform.IsWindows)
                {
                    SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM
                    ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle);
                }
                else
                {
                    //On non-windows platforms the shutdown notification is done through a callback instead of a windows event handle.
                    //Register the callback and this will then signal the event. Note, the gch object is deleted in the shell shutdown
                    //notification that will always come in to shut down the operation.

                    GCHandle gch = GCHandle.Alloc(eventWaitHandle);
                    IntPtr p = GCHandle.ToIntPtr(gch);

                    wsmanPinvokeStatic.WSManPluginRegisterShutdownCallback(
                                                           requestDetails.unmanagedHandle,
                                                           WSManPluginManagedEntryWrapper.workerPtrs.UnmanagedStruct.wsManPluginShutdownCallbackNative,
                                                           p);
                }


                mgdShellSession.registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject(
                                 eventWaitHandle,
                                 new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback),
                                 context,
                                 -1, // INFINITE
                                 true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION?
                if (null == mgdShellSession.registeredShutDownWaitHandle)
                {
                    isRegisterWaitForSingleObjectSucceeded = false;
                }
            }

            if (!isRegisterWaitForSingleObjectSucceeded)
            {
                mgdShellSession.registeredShutdownNotification = 0;
                WSManPluginInstance.ReportWSManOperationComplete(
                    requestDetails,
                    WSManPluginErrorCodes.ShutdownRegistrationFailed);
                DeleteFromActiveShellSessions(requestDetails.unmanagedHandle);
                return;
            }

            try
            {
                if (convertedBase64 != null)
                {
                    mgdShellSession.SendOneItemToSessionHelper(convertedBase64, WSManPluginConstants.SupportedInputStream);
                }
            }
            catch (System.Exception e)
            {
                CommandProcessorBase.CheckForSevereException(e);

                PSEtwLog.LogOperationalError(PSEventId.TransportError,
                    PSOpcode.Connect, PSTask.None, PSKeyword.UseAlwaysOperational, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000",
                    Convert.ToString(WSManPluginErrorCodes.ManagedException, CultureInfo.InvariantCulture), e.Message, e.StackTrace);

                if (Interlocked.Exchange(ref mgdShellSession.registeredShutdownNotification, 0) == 1)
                {
                    // unregister callback.. wait for any ongoing callbacks to complete.. nothing much we could do if this fails
                    bool ignore = mgdShellSession.registeredShutDownWaitHandle.Unregister(null);
                    mgdShellSession.registeredShutDownWaitHandle = null;

                    //this will called OperationComplete
                    PerformCloseOperation(context);
                }
                return;
            }

            return;
        }
Esempio n. 6
0
        internal override void CloseOperation(
            WSManPluginOperationShutdownContext context,
            Exception reasonForClose)
        {
            // let command sessions to close.
            lock (cmdSyncObject)
            {
                if (true == isClosed)
                {
                    return;
                }

                if (!context.isReceiveOperation)
                {
                    isClosed = true;
                }
            }

            bool isRcvOp = context.isReceiveOperation;
            // only one thread will be here.
            bool isRcvOpShuttingDown = (context.isShuttingDown) &&
                (context.isReceiveOperation) &&
                (context.commandContext == creationRequestDetails.unmanagedHandle);

            bool isCmdShuttingDown = (context.isShuttingDown) &&
                (!context.isReceiveOperation) &&
                (context.commandContext == creationRequestDetails.unmanagedHandle);

            // close the pending send operation if any
            ReportSendOperationComplete();
            // close the shell's transport manager first..so we wont send data.
            transportMgr.DoClose(isRcvOpShuttingDown, reasonForClose);

            if (!isRcvOp)
            {
                // raise session closed event and let dependent code to release resources.
                // null check is not performed here because Managed C++ will take care of this.
                base.SafeInvokeSessionClosed(creationRequestDetails.unmanagedHandle, EventArgs.Empty);
                // Send Operation Complete to WSMan service
                WSManPluginInstance.ReportWSManOperationComplete(creationRequestDetails, reasonForClose);
                // let base class release its resources
                this.Close(isCmdShuttingDown);
            }
        }
Esempio n. 7
0
        internal static void PerformWSManPluginSignal(
            IntPtr pluginContext, // PVOID
            IntPtr requestDetails, // WSMAN_PLUGIN_REQUEST*
            int flags,
            IntPtr shellContext, // PVOID
            IntPtr commandContext, // PVOID
            string code)
        {
            WSManNativeApi.WSManPluginRequest request = WSManNativeApi.WSManPluginRequest.UnMarshal(requestDetails);

            // Close Command
            if (IntPtr.Zero != commandContext)
            {
                if (!String.Equals(code, WSManPluginConstants.CtrlCSignal, StringComparison.Ordinal))
                {
                    // Close operations associated with this command..
                    WSManPluginOperationShutdownContext cmdCtxt = new WSManPluginOperationShutdownContext(pluginContext, shellContext, commandContext, false);
                    if (null != cmdCtxt)
                    {
                        PerformCloseOperation(cmdCtxt);
                    }
                    else
                    {
                        ReportOperationComplete(request, WSManPluginErrorCodes.OutOfMemory);
                        return;
                    }
                }
                else
                {
                    // we got crtl_c (stop) message from client. so stop powershell
                    WSManPluginInstance pluginToUse = GetFromActivePlugins(pluginContext);

                    if (null == pluginToUse)
                    {
                        ReportOperationComplete(
                            request,
                            WSManPluginErrorCodes.PluginContextNotFound,
                            StringUtil.Format(
                                RemotingErrorIdStrings.WSManPluginContextNotFound));
                        return;
                    }

                    // this will ReportOperationComplete by itself..
                    // so we just here.
                    pluginToUse.StopCommand(request, shellContext, commandContext);
                    return;
                }
            }

            ReportOperationComplete(request, WSManPluginErrorCodes.NoError);
        }
Esempio n. 8
0
        // Closes the command and clears associated resources
        internal void CloseCommandOperation(
            WSManPluginOperationShutdownContext context)
        {
            WSManPluginCommandSession mgdCmdSession = GetCommandSession(context.commandContext);
            if (null == mgdCmdSession)
            {
                // this should never be the case. this will protect the service.
                return;
            }

            // update the internal data store only if this is not receive operation.
            if (!context.isReceiveOperation)
            {
                DeleteFromActiveCmdSessions(mgdCmdSession.creationRequestDetails.unmanagedHandle);
            }

            mgdCmdSession.CloseOperation(context, null);
        }
Esempio n. 9
0
        internal override void CloseOperation(
            WSManPluginOperationShutdownContext context,
            Exception reasonForClose)
        {
            // let command sessions to close.
            lock (shellSyncObject)
            {
                if (true == isClosed)
                {
                    return;
                }

                if (!context.isReceiveOperation)
                {
                    isClosed = true;
                }
            }

            bool isRcvOpShuttingDown = (context.isShuttingDown) && (context.isReceiveOperation);
            bool isRcvOp = context.isReceiveOperation;
            bool isShuttingDown = context.isShuttingDown;

            // close the pending send operation if any
            ReportSendOperationComplete();
            // close the shell's transport manager after commands handled the operation
            transportMgr.DoClose(isRcvOpShuttingDown, reasonForClose);

            if (!isRcvOp)
            {
                // Initiate close on the active command sessions and then clear the internal
                // Command Session dictionary
                CloseAndClearCommandSessions(reasonForClose);
                // raise session closed event and let dependent code to release resources.
                // null check is not performed here because the handler will take care of this.
                base.SafeInvokeSessionClosed(creationRequestDetails.unmanagedHandle, EventArgs.Empty);
                // Send Operation Complete to WSMan service
                WSManPluginInstance.ReportWSManOperationComplete(creationRequestDetails, reasonForClose);
                // let base class release its resources
                base.Close(isShuttingDown);
            }
            // TODO: Do this.Dispose(); here?
        }
Esempio n. 10
0
 internal abstract void CloseOperation(WSManPluginOperationShutdownContext context, Exception reasonForClose);
Esempio n. 11
0
        internal WSManPluginShellSession(
            WSManNativeApi.WSManPluginRequest creationRequestDetails,
            WSManPluginServerTransportManager transportMgr,
            ServerRemoteSession remoteSession,
            WSManPluginOperationShutdownContext shutDownContext)
            : base(creationRequestDetails, transportMgr)
        {
            _remoteSession = remoteSession;
            _remoteSession.Closed +=
                new EventHandler<RemoteSessionStateMachineEventArgs>(this.HandleServerRemoteSessionClosed);

            _activeCommandSessions = new Dictionary<IntPtr, WSManPluginCommandSession>();
            this.shellSyncObject = new System.Object();
            this.shutDownContext = shutDownContext;
        }
Esempio n. 12
0
 // close current session and transport manager because of an exception
 internal void Close(Exception reasonForClose)
 {
     lastErrorReported = reasonForClose;
     WSManPluginOperationShutdownContext context = new WSManPluginOperationShutdownContext(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, false);
     CloseOperation(context, reasonForClose);
 }
Esempio n. 13
0
        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().
        }
Esempio n. 14
0
        internal bool EnableSessionToSendDataToClient(
            WSManNativeApi.WSManPluginRequest requestDetails,
            int flags,
            WSManNativeApi.WSManStreamIDSet_UnToMan streamSet,
            WSManPluginOperationShutdownContext ctxtToReport)
        {
            if (true == isClosed)
            {
                WSManPluginInstance.ReportWSManOperationComplete(requestDetails, lastErrorReported);
                return false;
            }

            if ((null == streamSet) ||
                (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);
        }
        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;
        }