chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//            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
{
    /// <summary>
    /// 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. 
    /// </summary>
    public abstract class IsolatedPackageAction : LockingPackageAction
    {
        /// <summary>
        /// Try to force execution in spite of errors. When true the action will execute even when isolation cannot be achieved.
        /// </summary>
        [CommandLineArgument("force", Description = "Try to run in spite of errors.", ShortName = "f")]
        public bool Force { get; set; }
        
        /// <summary>
        /// Avoid starting an isolated process. This can cause installations to fail if the DLLs that must be overwritten are loaded.
        /// </summary>
        [Browsable(false)]
        [CommandLineArgument("no-isolation", Description = "Avoid starting an isolated process.")]
        [Obsolete("Package actions are no longer starting isolated.")]
        public bool NoIsolation { get; set; }
        
 
        /// <summary>
        /// Executes this the action. Derived types should override LockedExecute instead of this.
        /// </summary>
        /// <returns>Return 0 to indicate success. Otherwise, return a custom error code that will be set as the exitcode from the CLI.</returns>
        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));
        
        /// <summary> This notifies the rest of the system that the package configuration has changed. </summary>
        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();
        }
    }
}