MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Globalization;5using System.Management.Automation.Host;6using System.Management.Automation.Remoting.Server;7using System.Management.Automation.Runspaces;8 9using Dbg = System.Management.Automation.Diagnostics;10 11namespace System.Management.Automation.Remoting12{13 /// <summary>14 /// The ServerRemoteHost class.15 /// </summary>16 internal class ServerRemoteHost : PSHost, IHostSupportsInteractiveSession17 {18 #region Private Members19 20 /// <summary>21 /// Remote host user interface.22 /// </summary>23 private readonly ServerRemoteHostUserInterface _remoteHostUserInterface;24 25 /// <summary>26 /// Server method executor.27 /// </summary>28 private readonly ServerMethodExecutor _serverMethodExecutor;29 30 /// <summary>31 /// Client runspace pool id.32 /// </summary>33 private readonly Guid _clientRunspacePoolId;34 35 /// <summary>36 /// Client power shell id.37 /// </summary>38 private readonly Guid _clientPowerShellId;39 40 /// <summary>41 /// Transport manager.42 /// </summary>43 protected AbstractServerTransportManager _transportManager;44 45 /// <summary>46 /// ServerDriverRemoteHost.47 /// </summary>48 private readonly ServerDriverRemoteHost _serverDriverRemoteHost;49 50 #endregion51 52 #region Constructor53 54 /// <summary>55 /// Constructor for ServerRemoteHost.56 /// </summary>57 internal ServerRemoteHost(58 Guid clientRunspacePoolId,59 Guid clientPowerShellId,60 HostInfo hostInfo,61 AbstractServerTransportManager transportManager,62 Runspace runspace,63 ServerDriverRemoteHost serverDriverRemoteHost)64 {65 _clientRunspacePoolId = clientRunspacePoolId;66 _clientPowerShellId = clientPowerShellId;67 Dbg.Assert(hostInfo != null, "Expected hostInfo != null");68 Dbg.Assert(transportManager != null, "Expected transportManager != null");69 70 // Set host-info and the transport-manager.71 HostInfo = hostInfo;72 _transportManager = transportManager;73 _serverDriverRemoteHost = serverDriverRemoteHost;74 75 // Create the executor for the host methods.76 _serverMethodExecutor = new ServerMethodExecutor(77 clientRunspacePoolId, clientPowerShellId, _transportManager);78 79 // Use HostInfo to create host-UI as null or non-null based on the client's host-UI.80 _remoteHostUserInterface = hostInfo.IsHostUINull ? null : new ServerRemoteHostUserInterface(this);81 82 Runspace = runspace;83 }84 85 #endregion86 87 #region Properties88 89 /// <summary>90 /// Server method executor.91 /// </summary>92 internal ServerMethodExecutor ServerMethodExecutor93 {94 get { return _serverMethodExecutor; }95 }96 97 /// <summary>98 /// The user interface.99 /// </summary>100 public override PSHostUserInterface UI101 {102 get { return _remoteHostUserInterface; }103 }104 105 /// <summary>106 /// Name.107 /// </summary>108 public override string Name109 {110 get { return "ServerRemoteHost"; }111 }112 113 /// <summary>114 /// Version.115 /// </summary>116 public override Version Version117 {118 get { return RemotingConstants.HostVersion; }119 }120 121 /// <summary>122 /// Instance id.123 /// </summary>124 public override Guid InstanceId { get; } = Guid.NewGuid();125 126 /// <summary>127 /// Is runspace pushed.128 /// </summary>129 public virtual bool IsRunspacePushed130 {131 get132 {133 if (_serverDriverRemoteHost != null)134 {135 return _serverDriverRemoteHost.IsRunspacePushed;136 }137 else138 {139 throw RemoteHostExceptions.NewNotImplementedException(RemoteHostMethodId.GetIsRunspacePushed);140 }141 }142 }143 144 /// <summary>145 /// Runspace.146 /// </summary>147 public Runspace Runspace { get; internal set; }148 149 /// <summary>150 /// Host info.151 /// </summary>152 internal HostInfo HostInfo { get; }153 154 #endregion155 156 #region Method Overrides157 158 /// <summary>159 /// Set should exit.160 /// </summary>161 public override void SetShouldExit(int exitCode)162 {163 _serverMethodExecutor.ExecuteVoidMethod(RemoteHostMethodId.SetShouldExit, new object[] { exitCode });164 }165 166 /// <summary>167 /// Enter nested prompt.168 /// </summary>169 public override void EnterNestedPrompt()170 {171 throw RemoteHostExceptions.NewNotImplementedException(RemoteHostMethodId.EnterNestedPrompt);172 }173 174 /// <summary>175 /// Exit nested prompt.176 /// </summary>177 public override void ExitNestedPrompt()178 {179 throw RemoteHostExceptions.NewNotImplementedException(RemoteHostMethodId.ExitNestedPrompt);180 }181 182 /// <summary>183 /// Notify begin application.184 /// </summary>185 public override void NotifyBeginApplication()186 {187 // This is called when a native application is executed on the server. It gives the188 // host an opportunity to save state that might be altered by the native application.189 // This call should not be sent to the client because the native application running190 // on the server cannot affect the state of the machine on the client.191 }192 193 /// <summary>194 /// Notify end application.195 /// </summary>196 public override void NotifyEndApplication()197 {198 // See note in NotifyBeginApplication.199 }200 201 /// <summary>202 /// Current culture.203 /// </summary>204 public override CultureInfo CurrentCulture205 {206 get207 {208 // Return the thread's current culture and rely on WinRM to set this209 // correctly based on the client's culture.210 return CultureInfo.CurrentCulture;211 }212 }213 214 /// <summary>215 /// Current ui culture.216 /// </summary>217 public override CultureInfo CurrentUICulture218 {219 get220 {221 // Return the thread's current UI culture and rely on WinRM to set222 // this correctly based on the client's UI culture.223 return CultureInfo.CurrentUICulture;224 }225 }226 227 #endregion228 229 #region Methods230 231 /// <summary>232 /// Push runspace.233 /// </summary>234 public virtual void PushRunspace(Runspace runspace)235 {236 if (_serverDriverRemoteHost != null)237 {238 _serverDriverRemoteHost.PushRunspace(runspace);239 }240 else241 {242 throw RemoteHostExceptions.NewNotImplementedException(RemoteHostMethodId.PushRunspace);243 }244 }245 246 /// <summary>247 /// Pop runspace.248 /// </summary>249 public virtual void PopRunspace()250 {251 if ((_serverDriverRemoteHost != null) && (_serverDriverRemoteHost.IsRunspacePushed))252 {253 if (_serverDriverRemoteHost.PropagatePop)254 {255 // Forward the PopRunspace command to client and keep *this* pushed runspace as256 // the configured JEA restricted session.257 _serverMethodExecutor.ExecuteVoidMethod(RemoteHostMethodId.PopRunspace);258 }259 else260 {261 _serverDriverRemoteHost.PopRunspace();262 }263 }264 else265 {266 _serverMethodExecutor.ExecuteVoidMethod(RemoteHostMethodId.PopRunspace);267 }268 }269 270 #endregion271 }272 273 /// <summary>274 /// The remote host class for the ServerRunspacePoolDriver.275 /// </summary>276 internal class ServerDriverRemoteHost : ServerRemoteHost277 {278 #region Private Members279 280 private RemoteRunspace _pushedRunspace;281 private ServerRemoteDebugger _debugger;282 private bool _hostSupportsPSEdit;283 284 #endregion285 286 #region Constructor287 288 internal ServerDriverRemoteHost(289 Guid clientRunspacePoolId,290 Guid clientPowerShellId,291 HostInfo hostInfo,292 AbstractServerSessionTransportManager transportManager,293 ServerRemoteDebugger debugger)294 : base(clientRunspacePoolId, clientPowerShellId, hostInfo, transportManager, null, null)295 {296 _debugger = debugger;297 }298 299 #endregion300 301 #region Overrides302 303 /// <summary>304 /// True if runspace is pushed.305 /// </summary>306 public override bool IsRunspacePushed307 {308 get309 {310 return (_pushedRunspace != null);311 }312 }313 314 /// <summary>315 /// Push runspace to use for remote command execution.316 /// </summary>317 /// <param name="runspace">RemoteRunspace.</param>318 public override void PushRunspace(Runspace runspace)319 {320 if (_debugger == null)321 {322 throw new PSInvalidOperationException(RemotingErrorIdStrings.ServerDriverRemoteHostNoDebuggerToPush);323 }324 325 if (_pushedRunspace != null)326 {327 throw new PSInvalidOperationException(RemotingErrorIdStrings.ServerDriverRemoteHostAlreadyPushed);328 }329 330 if (runspace is not RemoteRunspace remoteRunspace)331 {332 throw new PSInvalidOperationException(RemotingErrorIdStrings.ServerDriverRemoteHostNotRemoteRunspace);333 }334 335 // PSEdit support. Existence of RemoteSessionOpenFileEvent event indicates host supports PSEdit336 _hostSupportsPSEdit = false;337 PSEventManager localEventManager = Runspace?.Events;338 _hostSupportsPSEdit = localEventManager != null && localEventManager.GetEventSubscribers(HostUtilities.RemoteSessionOpenFileEvent).GetEnumerator().MoveNext();339 if (_hostSupportsPSEdit)340 {341 AddPSEditForRunspace(remoteRunspace);342 }343 344 _debugger.PushDebugger(runspace.Debugger);345 _pushedRunspace = remoteRunspace;346 }347 348 /// <summary>349 /// Pop runspace.350 /// </summary>351 public override void PopRunspace()352 {353 if (_pushedRunspace != null)354 {355 _debugger?.PopDebugger();356 357 if (_hostSupportsPSEdit)358 {359 RemovePSEditFromRunspace(_pushedRunspace);360 }361 362 if (_pushedRunspace.ShouldCloseOnPop)363 {364 _pushedRunspace.Close();365 }366 367 _pushedRunspace = null;368 }369 }370 371 #endregion372 373 #region Properties374 375 /// <summary>376 /// Server Debugger.377 /// </summary>378 internal Debugger ServerDebugger379 {380 get { return _debugger; }381 382 set { _debugger = value as ServerRemoteDebugger; }383 }384 385 /// <summary>386 /// Pushed runspace or null.387 /// </summary>388 internal Runspace PushedRunspace389 {390 get { return _pushedRunspace; }391 }392 393 /// <summary>394 /// When true will propagate pop call to client after popping runspace from this395 /// host. Used for OutOfProc remote sessions in a restricted (pushed) remote runspace,396 /// where a pop (exit session) should occur.397 /// </summary>398 internal bool PropagatePop399 {400 get;401 set;402 }403 404 #endregion405 406 #region PSEdit Support for ISE Host407 408 private void AddPSEditForRunspace(RemoteRunspace remoteRunspace)409 {410 if (remoteRunspace.Events == null)411 {412 return;413 }414 415 // Add event handler.416 remoteRunspace.Events.ReceivedEvents.PSEventReceived += HandleRemoteSessionForwardedEvent;417 418 // Add script function.419 using (PowerShell powershell = PowerShell.Create())420 {421 powershell.Runspace = remoteRunspace;422 powershell.AddScript(HostUtilities.CreatePSEditFunction).AddParameter("PSEditFunction", HostUtilities.PSEditFunction);423 try424 {425 powershell.Invoke();426 }427 catch (RemoteException) { }428 }429 }430 431 private void RemovePSEditFromRunspace(RemoteRunspace remoteRunspace)432 {433 if (remoteRunspace.Events == null)434 {435 return;436 }437 438 // It is possible for the popped runspace to be in a bad state after an error.439 if ((remoteRunspace.RunspaceStateInfo.State != RunspaceState.Opened) || (remoteRunspace.RunspaceAvailability != RunspaceAvailability.Available))440 {441 return;442 }443 444 // Remove event handler.445 remoteRunspace.Events.ReceivedEvents.PSEventReceived -= HandleRemoteSessionForwardedEvent;446 447 // Remove script function.448 using (PowerShell powershell = PowerShell.Create())449 {450 powershell.Runspace = remoteRunspace;451 powershell.AddScript(HostUtilities.RemovePSEditFunction);452 try453 {454 powershell.Invoke();455 }456 catch (RemoteException) { }457 }458 }459 460 private void HandleRemoteSessionForwardedEvent(object sender, PSEventArgs args)461 {462 if ((Runspace == null) || (Runspace.Events == null))463 {464 return;465 }466 467 // Forward events from nested pushed session to parent session.468 try469 {470 Runspace.Events.GenerateEvent(471 sourceIdentifier: args.SourceIdentifier,472 sender: null,473 args: args.SourceArgs,474 extraData: null);475 }476 catch (Exception)477 {478 }479 }480 481 #endregion482 }483}484 