// 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(); } } }