/// <summary> /// adds shell session to activeShellSessions store and returns the id /// at which the session is added. /// </summary> /// <param name="newShellSession"></param> private void AddToActiveShellSessions( WSManPluginShellSession newShellSession) { int count = -1; lock (_syncObject) { IntPtr key = newShellSession.creationRequestDetails.unmanagedHandle; Dbg.Assert(IntPtr.Zero != key, "NULL handles should not be provided"); if (!_activeShellSessions.ContainsKey(key)) { _activeShellSessions.Add(key, newShellSession); // trigger an event outside the lock count = _activeShellSessions.Count; } } if (-1 != count) { // Raise session count changed event WSManServerChannelEvents.RaiseActiveSessionsChangedEvent(new ActiveSessionsChangedEventArgs(count)); } }
/// <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; }