// Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using OpenTap.Cli;
namespace OpenTap.Package
{
///
/// Base class for ICliActions related to installing or removing packages. Useful for making changes to the installation. Previously this made a copy of the installation to a temp dir before executing.
///
public abstract class IsolatedPackageAction : LockingPackageAction
{
///
/// Try to force execution in spite of errors. When true the action will execute even when isolation cannot be achieved.
///
[CommandLineArgument("force", Description = "Try to run in spite of errors.", ShortName = "f")]
public bool Force { get; set; }
///
/// Avoid starting an isolated process. This can cause installations to fail if the DLLs that must be overwritten are loaded.
///
[Browsable(false)]
[CommandLineArgument("no-isolation", Description = "Avoid starting an isolated process.")]
[Obsolete("Package actions are no longer starting isolated.")]
public bool NoIsolation { get; set; }
///
/// Executes this the action. Derived types should override LockedExecute instead of this.
///
/// Return 0 to indicate success. Otherwise, return a custom error code that will be set as the exitcode from the CLI.
public override int Execute(CancellationToken cancellationToken)
{
if (String.IsNullOrEmpty(Target))
Target = GetLocalInstallationDir();
else
Target = Path.GetFullPath(Target.Trim());
if (!Directory.Exists(Target))
{
if (File.Exists(Target))
{
log.Error("Destination directory \"{0}\" is a file.", Target);
return (int)ExitCodes.ArgumentError;
}
FileSystemHelper.EnsureDirectoryOf(Target);
}
return base.Execute(cancellationToken);
}
internal static bool TryFindParentInstallation(string targetDirectory, out string parent)
{
var dir = new DirectoryInfo(targetDirectory).Parent;
while (dir != null)
{
if (dir.EnumerateFiles("OpenTap.dll").Any())
{
parent = dir.FullName;
return true;
}
dir = dir.Parent;
}
parent = null;
return false;
}
private static string GetChangeFile(string target) => Path.Combine(target, "Packages", ".changeId");
internal static long GetChangeId(string target)
{
var filePath = GetChangeFile(target);
if (File.Exists(filePath))
if (long.TryParse(File.ReadAllText(filePath), out var changeId))
return changeId;
return 0;
}
private static void EnsureDirectory(string filePath) => Directory.CreateDirectory(Path.GetDirectoryName(filePath));
/// This notifies the rest of the system that the package configuration has changed.
internal static void IncrementChangeId(string target)
{
var filePath = GetChangeFile(target);
long changeId = GetChangeId(target);
changeId += 1;
try
{
EnsureDirectory(filePath);
File.WriteAllText(filePath, changeId.ToString());
}
catch (Exception ex)
{
log.Warning($"Failed writing Change ID to {filePath}");
log.Debug(ex);
}
}
// This method is only called by the static function LockedPackageAction.RunIsolated(), which has been obsolete since 9.0
// It is unlikely to ever be called.
internal static void RunIsolated(string application = null, string target = null, IsolatedPackageAction action = null)
{
// public static bool IsExecutorMode => Environment.GetEnvironmentVariable(ExecutorSubProcess.EnvVarNames.TpmInteropPipeName) != null;
var cmd = application ?? Assembly.GetEntryAssembly().Location;
if (cmd.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
{ //.netcore wierdness.
cmd = Path.ChangeExtension(cmd, "exe");
if (File.Exists(cmd) == false)
cmd = cmd.Substring(0, cmd.Length - ".exe".Length);
}
// now that we start from a different dir, we need to supply a --target argument
string args = "";
if (target != null)
args = $"--target \"{target}\"";
var startInfo = new ProcessStartInfo()
{
FileName = cmd,
Arguments = args,
UseShellExecute = false,
};
var proc = Process.Start(startInfo);
proc?.WaitForExit();
}
}
}