mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-17 20:56:02 +00:00
450 lines
16 KiB
C#
450 lines
16 KiB
C#
//
|
|
// Copyright (C) Microsoft. All rights reserved.
|
|
//
|
|
using System;
|
|
using System.Activities;
|
|
using System.Activities.Validation;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Runtime;
|
|
using System.Activities.Statements;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Runspaces;
|
|
using System.ComponentModel;
|
|
using Microsoft.PowerShell.Activities;
|
|
using System.Management.Automation.Tracing;
|
|
using System.Globalization;
|
|
|
|
namespace Microsoft.PowerShell.Management.Activities
|
|
{
|
|
/// <summary>
|
|
/// RestartActivityContext
|
|
/// </summary>
|
|
[Serializable]
|
|
public class RestartActivityContext {
|
|
/// <summary>
|
|
/// Indicates whether a self restart is needed.
|
|
/// </summary>
|
|
[NonSerialized]
|
|
internal bool NeedsRestart = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// RestartComputerActivity
|
|
/// </summary>
|
|
public sealed class RestartComputer : PSActivity
|
|
{
|
|
/// <summary>
|
|
/// Gets the display name of the command invoked by this activity.
|
|
/// </summary>
|
|
public RestartComputer()
|
|
{
|
|
this.DisplayName = "Restart-Computer";
|
|
}
|
|
|
|
private static Tracer _structuredTracer = new Tracer();
|
|
|
|
/// <summary>
|
|
/// If true, them the workflow will not checkpoint and resume after
|
|
/// the computer is restarted.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public static bool DisableSelfRestart
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the fully qualified name of the command invoked by this activity.
|
|
/// </summary>
|
|
public override string PSCommandName
|
|
{
|
|
get
|
|
{
|
|
return "Microsoft.PowerShell.Management\\Restart-Computer";
|
|
}
|
|
}
|
|
|
|
// Arguments
|
|
|
|
/// <summary>
|
|
/// Provides access to the Authentication parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public InArgument<System.Management.AuthenticationLevel> DcomAuthentication
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the Impersonation parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.Management.ImpersonationLevel> Impersonation
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the WsmanAuthentication parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public InArgument<System.String> WsmanAuthentication { get; set; }
|
|
|
|
/// <summary>
|
|
/// Provides access to the Protocol parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.String> Protocol { get; set; }
|
|
|
|
/// <summary>
|
|
/// The computer name to invoke this activity on.
|
|
/// </summary>
|
|
[ConnectivityCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<string[]> PSComputerName
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines the credential to use in the remote connection.
|
|
/// </summary>
|
|
[ConnectivityCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<PSCredential> PSCredential
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the Force parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.Management.Automation.SwitchParameter> Force
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the ThrottleLimit parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.Int32> ThrottleLimit
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the Wait parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.Management.Automation.SwitchParameter> Wait
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the Timeout parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.Int32> Timeout
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the For parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<Microsoft.PowerShell.Commands.WaitForServiceTypes> For
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides access to the Delay parameter.
|
|
/// </summary>
|
|
[ParameterSpecificCategory]
|
|
[DefaultValue(null)]
|
|
public InArgument<System.Int16> Delay
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
private Variable<RestartActivityContext> restartActivityContext = new Variable<RestartActivityContext>("RestartActivityContext");
|
|
|
|
private readonly PSPersist persistActivity = new PSPersist();
|
|
|
|
/// <summary>
|
|
/// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run.
|
|
/// </summary>
|
|
/// <param name="context">The NativeActivityContext for the currently running activity.</param>
|
|
/// <returns>A populated instance of Sytem.Management.Automation.PowerShell</returns>
|
|
/// <remarks>The infrastructure takes responsibility for closing and disposing the PowerShell instance returned.</remarks>
|
|
protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context)
|
|
{
|
|
System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create();
|
|
System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName);
|
|
|
|
// Get the host default values we should use if no explicit parameter was provided.
|
|
var hostExtension = context.GetExtension<HostParameterDefaults>();
|
|
Dictionary<string, object> parameterDefaults = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
if (hostExtension != null)
|
|
{
|
|
Dictionary<string, object> incomingArguments = hostExtension.Parameters;
|
|
foreach (KeyValuePair<string, object> parameterDefault in incomingArguments)
|
|
{
|
|
parameterDefaults[parameterDefault.Key] = parameterDefault.Value;
|
|
}
|
|
}
|
|
|
|
|
|
if (DcomAuthentication.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("DcomAuthentication", DcomAuthentication.Get(context));
|
|
|
|
}
|
|
|
|
if (Impersonation.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("Impersonation", Impersonation.Get(context));
|
|
}
|
|
|
|
// See if the DCOM protocol is to be used
|
|
bool usingDcom = false;
|
|
if (Protocol.Expression != null)
|
|
{
|
|
string protocol = Protocol.Get(context);
|
|
targetCommand.AddParameter("Protocol", protocol);
|
|
if (string.Equals(protocol, "DCOM", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
usingDcom = true;
|
|
}
|
|
}
|
|
|
|
// Get the WSMan authentication mechanism to use. If no expression was specified,
|
|
// and DCOM is not being used, get the default from the host.
|
|
if (WsmanAuthentication.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("WsmanAuthentication", WsmanAuthentication.Get(context));
|
|
}
|
|
else
|
|
{
|
|
if (!usingDcom)
|
|
{
|
|
if (parameterDefaults.ContainsKey("PSAuthentication"))
|
|
{
|
|
string authString = parameterDefaults["PSAuthentication"].ToString();
|
|
// Note: the underlying cmdlet does support NegotiateWithImplicitCredential so it is expected
|
|
// that passing this in as-is will result in an (appropriate) error being emitted in that case.
|
|
targetCommand.AddParameter("WsmanAuthentication", authString);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Map PSComputerName to the underlying cmdlet name only if the computername is not empty
|
|
string[] computerName = GetPSComputerName(context);
|
|
if ((computerName != null) && (computerName.Length != 0))
|
|
{
|
|
targetCommand.AddParameter("ComputerName", computerName);
|
|
}
|
|
|
|
// Map PSCredential to credential. If no expression was provided, then use the default.
|
|
if (PSCredential.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("Credential", PSCredential.Get(context));
|
|
}
|
|
else
|
|
{
|
|
if (parameterDefaults.ContainsKey("PSCredential"))
|
|
{
|
|
targetCommand.AddParameter("Credential", parameterDefaults["PSCredential"]);
|
|
}
|
|
}
|
|
|
|
if (Force.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("Force", Force.Get(context));
|
|
}
|
|
|
|
if (ThrottleLimit.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("ThrottleLimit", ThrottleLimit.Get(context));
|
|
}
|
|
|
|
// Ignore the -Wait parameter in the self-restart case.
|
|
if (!IsSelfRestart(context))
|
|
{
|
|
if (Wait.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("Wait", Wait.Get(context));
|
|
}
|
|
|
|
if (For.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("For", For.Get(context));
|
|
}
|
|
|
|
if (Delay.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("Delay", Delay.Get(context));
|
|
}
|
|
}
|
|
|
|
if (Timeout.Expression != null)
|
|
{
|
|
targetCommand.AddParameter("Timeout", Timeout.Get(context));
|
|
}
|
|
|
|
return new ActivityImplementationContext()
|
|
{
|
|
PowerShellInstance = invoker
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute this command for this activity.
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
protected override void Execute(NativeActivityContext context)
|
|
{
|
|
if (!IsSelfRestart(context))
|
|
{
|
|
if (_structuredTracer.IsEnabled)
|
|
{
|
|
string[] computerNames = new string[] { };
|
|
computerNames = PSComputerName.Get(context);
|
|
_structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', restarting managed nodes: [" + string.Join(", ", computerNames, ", ") + "]");
|
|
}
|
|
base.Execute(context);
|
|
}
|
|
else
|
|
{
|
|
_structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "',doing self-restart" );
|
|
PersistAndRestart(context);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test to see if we're restarting the machine we're running on.
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <returns>true if we are restarting the local machine.</returns>
|
|
private bool IsSelfRestart(NativeActivityContext context)
|
|
{
|
|
string[] computerName = GetPSComputerName(context);
|
|
|
|
if (computerName == null || computerName.Length == 0)
|
|
return true;
|
|
|
|
// NOTE: this is known to be problematic as the number of ways of saying "me" in the various
|
|
// protocols is potentially unbounded. This should be reviewed periodically.
|
|
foreach (string s in computerName)
|
|
{
|
|
string cn = s.Trim();
|
|
if (string.Equals(cn, "localhost", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(cn, "127.0.0.1", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(cn, "0:0:0:0:0:0:0:1", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(cn, "::1", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(cn, ".", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(cn, Environment.MachineName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the PSComputerName argument from the context
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <returns></returns>
|
|
private string[] GetPSComputerName(NativeActivityContext context)
|
|
{
|
|
if (PSComputerName.Expression != null)
|
|
{
|
|
return PSComputerName.Get(context);
|
|
}
|
|
|
|
// Retrieve our host overrides
|
|
var hostValues = context.GetExtension<HostParameterDefaults>();
|
|
|
|
if (hostValues != null)
|
|
{
|
|
Dictionary<string, object> incomingArguments = hostValues.Parameters;
|
|
if (incomingArguments.ContainsKey("PSComputerName"))
|
|
{
|
|
return incomingArguments["PSComputerName"] as string[];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// CacheMetadata
|
|
/// </summary>
|
|
/// <param name="metadata"></param>
|
|
protected override void CacheMetadata(NativeActivityMetadata metadata)
|
|
{
|
|
base.CacheMetadata(metadata);
|
|
metadata.AddImplementationChild(this.persistActivity);
|
|
metadata.AddImplementationVariable(restartActivityContext);
|
|
}
|
|
|
|
private void PersistAndRestart(NativeActivityContext executionContext)
|
|
{
|
|
if (DisableSelfRestart)
|
|
{
|
|
_structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', resume of workflow after restart was disabled.");
|
|
return;
|
|
}
|
|
|
|
_structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "' scheduling persistence for workflow resumption after restart.");
|
|
RestartActivityContext c = new RestartActivityContext();
|
|
c.NeedsRestart = true;
|
|
restartActivityContext.Set(executionContext, c);
|
|
|
|
executionContext.ScheduleActivity(persistActivity, SelfRestart);
|
|
}
|
|
|
|
private void SelfRestart(NativeActivityContext context, ActivityInstance target)
|
|
{
|
|
RestartActivityContext restartContext = restartActivityContext.Get(context);
|
|
if (!restartContext.NeedsRestart)
|
|
{
|
|
_structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', local machine (" + Environment.MachineName + ") has been restarted.");
|
|
return;
|
|
}
|
|
|
|
_structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', local machine (" + Environment.MachineName + ") is self-restaring");
|
|
base.Execute(context);
|
|
}
|
|
}
|
|
}
|