/// <summary> /// /// </summary> /// <param name="pluginContext">PVOID</param> /// <param name="requestDetails">WSMAN_PLUGIN_REQUEST*</param> /// <param name="flags">DWORD</param> /// <param name="extraInfo">PCWSTR</param> /// <param name="startupInfo">WSMAN_SHELL_STARTUP_INFO*</param> /// <param name="inboundShellInformation">WSMAN_DATA*</param> public static void WSManPluginShell( IntPtr pluginContext, IntPtr requestDetails, int flags, [MarshalAs(UnmanagedType.LPWStr)] string extraInfo, IntPtr startupInfo, IntPtr inboundShellInformation) { if (IntPtr.Zero == pluginContext) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullPluginContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullPluginContext, "pluginContext", "WSManPluginShell") ); return; } #if (DEBUG) // In debug builds, allow remote runspaces to wait for debugger attach if (Environment.GetEnvironmentVariable("__PSRemoteRunspaceWaitForDebugger", EnvironmentVariableTarget.Machine) != null) { bool debuggerAttached = false; while (!debuggerAttached) { System.Threading.Thread.Sleep(100); } } #endif WSManPluginInstance.PerformWSManPluginShell(pluginContext, requestDetails, flags, extraInfo, startupInfo, inboundShellInformation); }
/// <summary> /// /// </summary> /// <param name="requestDetails"></param> internal void Stop( WSManNativeApi.WSManPluginRequest requestDetails) { // stop the command..command will be stoped if we raise ClosingEvent on // transport manager. transportMgr.PerformStop(); WSManPluginInstance.ReportWSManOperationComplete(requestDetails, null); }
// Create a new command in the shell context. internal void CreateCommand( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, string commandLine, WSManNativeApi.WSManCommandArgSet arguments) { try { // inbound cmd information is already verified.. so no need to verify here. WSManPluginCommandTransportManager serverCmdTransportMgr = new WSManPluginCommandTransportManager(transportMgr); serverCmdTransportMgr.Initialize(); // Apply quota limits on the command transport manager _remoteSession.ApplyQuotaOnCommandTransportManager(serverCmdTransportMgr); WSManPluginCommandSession mgdCmdSession = new WSManPluginCommandSession(requestDetails, serverCmdTransportMgr, _remoteSession); AddToActiveCmdSessions(mgdCmdSession); mgdCmdSession.SessionClosed += new EventHandler <EventArgs>(this.HandleCommandSessionClosed); mgdCmdSession.shutDownContext = new WSManPluginOperationShutdownContext( pluginContext, creationRequestDetails.unmanagedHandle, mgdCmdSession.creationRequestDetails.unmanagedHandle, false); do { if (!mgdCmdSession.ProcessArguments(arguments)) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidArgSet, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidArgSet, "WSManPluginCommand")); break; } // Report plugin context to WSMan mgdCmdSession.ReportContext(); } while (false); } catch (System.Exception e) { CommandProcessorBase.CheckForSevereException(e); // if there is an exception creating remote session send the message to client. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ManagedException, StringUtil.Format( RemotingErrorIdStrings.WSManPluginManagedException, e.Message)); } }
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(); }
/// <summary> /// Main routine for connect on a command/pipeline.. Currently NO-OP /// will be enhanced later to support intelligent connect... like ending input streams on pipelines /// that are still waiting for input data /// </summary> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="inboundConnectInformation"></param> internal override void ExecuteConnect( WSManNativeApi.WSManPluginRequest requestDetails, int flags, WSManNativeApi.WSManData_UnToMan inboundConnectInformation) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return; }
/// <summary> /// Called only once during shutdown. This is used to perform various deinitializations. /// </summary> /// <param name="pluginContext">PVOID</param> public static void ShutdownPlugin( IntPtr pluginContext) { WSManPluginInstance.PerformShutdown(pluginContext); if (null != workerPtrs) { workerPtrs.Dispose(); } }
// 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; } } }
/// <summary> /// Callback used to register with thread pool to notify when a plugin operation shuts down. /// Conforms to: /// public delegate void WaitOrTimerCallback( Object state, bool timedOut ) /// </summary> /// <param name="operationContext">PVOID</param> /// <param name="timedOut">BOOLEAN</param> /// <returns></returns> public static void PSPluginOperationShutdownCallback( object operationContext, bool timedOut) { if (null == operationContext) { return; } WSManPluginOperationShutdownContext context = (WSManPluginOperationShutdownContext)operationContext; context.isShuttingDown = true; WSManPluginInstance.PerformCloseOperation(context); }
internal override void CloseOperation( WSManPluginOperationShutdownContext context, Exception reasonForClose) { // let command sessions to close. lock (cmdSyncObject) { if (isClosed) { return; } if (!context.isReceiveOperation) { isClosed = true; } } WSManPluginInstance.SetThreadProperties(creationRequestDetails); 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); } }
internal override void CloseOperation( WSManPluginOperationShutdownContext context, Exception reasonForClose) { // let command sessions to close. lock (shellSyncObject) { if (isClosed) { return; } if (!context.isReceiveOperation) { isClosed = true; } } WSManPluginInstance.SetThreadProperties(creationRequestDetails); 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? }
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)); }
/// <summary> /// /// </summary> /// <param name="pluginContext">PVOID</param> /// <param name="requestDetails">WSMAN_PLUGIN_REQUEST*</param> /// <param name="flags">DWORD</param> /// <param name="shellContext">PVOID</param> /// <param name="commandContext">PVOID optional</param> /// <param name="code">PCWSTR</param> public static void WSManPluginSignal( IntPtr pluginContext, IntPtr requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, [MarshalAs(UnmanagedType.LPWStr)] string code) { if ((IntPtr.Zero == pluginContext) || (IntPtr.Zero == shellContext)) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullPluginContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullPluginContext, "Plugin Context", "WSManPluginSignal") ); return; } WSManPluginInstance.PerformWSManPluginSignal(pluginContext, requestDetails, flags, shellContext, commandContext, code); }
/// <summary> /// /// </summary> /// <param name="pluginContext">PVOID</param> /// <param name="requestDetails">WSMAN_PLUGIN_REQUEST*</param> /// <param name="flags">DWORD</param> /// <param name="shellContext">PVOID</param> /// <param name="commandContext">PVOID optional</param> /// <param name="streamSet">WSMAN_STREAM_ID_SET* optional</param> public static void WSManPluginReceive( IntPtr pluginContext, IntPtr requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, IntPtr streamSet) { if (IntPtr.Zero == pluginContext) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullPluginContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullPluginContext, "Plugin Context", "WSManPluginReceive") ); return; } WSManPluginInstance.PerformWSManPluginReceive(pluginContext, requestDetails, flags, shellContext, commandContext, streamSet); }
/// <summary> /// </summary> /// <param name="pluginContext">PVOID.</param> /// <param name="requestDetails">WSMAN_PLUGIN_REQUEST*.</param> /// <param name="flags">DWORD.</param> /// <param name="shellContext">PVOID.</param> /// <param name="commandContext">PVOID optional.</param> /// <param name="inboundConnectInformation">WSMAN_DATA* optional.</param> public static void WSManPluginConnect( IntPtr pluginContext, IntPtr requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, IntPtr inboundConnectInformation) { if (pluginContext == IntPtr.Zero) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullPluginContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullPluginContext, "pluginContext", "WSManPluginConnect") ); return; } WSManPluginInstance.PerformWSManPluginConnect(pluginContext, requestDetails, flags, shellContext, commandContext, inboundConnectInformation); }
/// <summary> /// </summary> /// <param name="pluginContext">PVOID.</param> /// <param name="requestDetails">WSMAN_PLUGIN_REQUEST*.</param> /// <param name="flags">DWORD.</param> /// <param name="shellContext">PVOID.</param> /// <param name="commandLine">PCWSTR.</param> /// <param name="arguments">WSMAN_COMMAND_ARG_SET* optional.</param> public static void WSManPluginCommand( IntPtr pluginContext, IntPtr requestDetails, int flags, IntPtr shellContext, [MarshalAs(UnmanagedType.LPWStr)] string commandLine, IntPtr arguments) { if (pluginContext == IntPtr.Zero) { WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullPluginContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullPluginContext, "Plugin Context", "WSManPluginCommand") ); return; } WSManPluginInstance.PerformWSManPluginCommand(pluginContext, requestDetails, flags, shellContext, commandLine, arguments); }
/// <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 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); }
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(); } }