/// <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; } } }
/// <summary> /// Send data to the shell / command specified. /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandContext"></param> /// <param name="stream"></param> /// <param name="inboundData"></param> internal void SendOneItemToShellOrCommand( WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, string stream, WSManNativeApi.WSManData_UnToMan inboundData) { if (!validateIncomingContexts(requestDetails, shellContext, "SendOneItemToShellOrCommand")) { return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerReceivedData, 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; } if (IntPtr.Zero == commandContext) { // the data is destined for shell (runspace) session. so let shell handle it mgdShellSession.SendOneItemToSession(requestDetails, flags, stream, inboundData); return; } // the data is destined for command. WSManPluginCommandSession mgdCmdSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCmdSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } mgdCmdSession.SendOneItemToSession(requestDetails, flags, stream, inboundData); }
internal void StopCommand( WSManNativeApi.WSManPluginRequest requestDetails, IntPtr shellContext, IntPtr commandContext) { 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", "StopCommand"), String.Empty); return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerStopCommand, PSOpcode.Disconnect, 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; } WSManPluginCommandSession mgdCommandSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCommandSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } mgdCommandSession.Stop(requestDetails); }
/// <summary> /// Create a new command in the shell context. /// </summary> /// <param name="pluginContext"></param> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandLine"></param> /// <param name="arguments"></param> internal void CreateCommand( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, string commandLine, WSManNativeApi.WSManCommandArgSet arguments) { if (!validateIncomingContexts(requestDetails, shellContext, "WSManRunShellCommandEx")) { return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerCreateCommandSession, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)shellContext).ToString(), requestDetails.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext)); return; } mgdShellSession.CreateCommand(pluginContext, requestDetails, flags, commandLine, arguments); }
/// <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; }
/// <summary> /// extract message from exception (if any) and report operation complete with it to WSMan /// </summary> /// <param name="requestDetails"></param> /// <param name="reasonForClose"></param> internal static void ReportWSManOperationComplete( WSManNativeApi.WSManPluginRequest requestDetails, Exception reasonForClose) { Dbg.Assert(null != requestDetails, "requestDetails cannot be null in operation complete."); WSManPluginErrorCodes error = WSManPluginErrorCodes.NoError; String errorMessage = String.Empty; String stackTrace = String.Empty; if (null != reasonForClose) { error = WSManPluginErrorCodes.ManagedException; errorMessage = reasonForClose.Message; stackTrace = reasonForClose.StackTrace; } PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), Convert.ToString(error, CultureInfo.InvariantCulture), errorMessage, stackTrace); if (null != reasonForClose) { // report operation complete to wsman with the error message (if any). ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ManagedException, StringUtil.Format( RemotingErrorIdStrings.WSManPluginManagedException, reasonForClose.Message)); } else { ReportOperationComplete( requestDetails.unmanagedHandle, WSManPluginErrorCodes.NoError); } }