/
Self.Remove.cs
142 lines (117 loc) · 5.56 KB
/
Self.Remove.cs
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
138
139
140
141
142
// Copyright Bastian Eicher et al.
// Licensed under the GNU Lesser Public License
using System.Diagnostics;
using NanoByte.Common.Native;
namespace ZeroInstall.Commands.Desktop;
partial class Self
{
public abstract class RemoveSubCommandBase(ICommandHandler handler) : SelfSubCommand(handler)
{
protected abstract string TargetDir { get; }
// Auto-detect portable targets by looking for flag file
protected bool Portable => File.Exists(Path.Combine(TargetDir, Locations.PortableFlagName));
// Auto-detect machine-wide targets by comparing path with registry entry
protected bool MachineWide => !Portable && TargetDir == ZeroInstallDeployment.GetPath(machineWide: true);
protected void PerformRemove()
{
using var manager = new SelfManager(TargetDir, Handler, MachineWide, Portable);
Log.Info($"Using Zero Install instance at '{Locations.InstallBase}' to remove '{TargetDir}'");
manager.Remove();
}
}
/// <summary>
/// Removes the current instance of Zero Install from the system.
/// </summary>
public class Remove(ICommandHandler handler) : RemoveSubCommandBase(handler)
{
public const string Name = "remove";
public override string Description => Resources.DescriptionMaintenanceRemove;
public override string Usage => "";
protected override int AdditionalArgsMax => 0;
protected override string TargetDir => Locations.InstallBase;
public override ExitCode Execute()
{
if (!ZeroInstallInstance.IsDeployed)
{
Log.Error(Resources.SelfRemoveNotDeployed);
return ExitCode.NoChanges;
}
if (MachineWide && WindowsUtils.IsWindows && !WindowsUtils.IsAdministrator)
throw new NotAdminException(Resources.MustBeAdminForMachineWide);
if (!ZeroInstallInstance.IsLibraryMode && !Handler.Ask(Resources.AskRemoveZeroInstall, defaultAnswer: true))
return ExitCode.UserCanceled;
if (IntegrationCommand.ExistingDesktopIntegration())
new RemoveAllApps(Handler).Execute();
if (MachineWide && IntegrationCommand.ExistingDesktopIntegration(machineWide: true))
new RemoveAllApps(Handler) {MachineWide = true}.Execute();
if (Handler.Ask(Resources.ConfirmPurge, defaultAnswer: ZeroInstallInstance.IsLibraryMode && ZeroInstallDeployment.FindOther() == null))
{
try
{
ImplementationStore.Purge();
}
catch (NotAdminException ex) when (ZeroInstallInstance.IsLibraryMode)
{
Log.Info("Unable to purge implementation store", ex);
}
}
if (WindowsUtils.IsWindows) DelegateToTempCopy();
else PerformRemove();
return ExitCode.OK;
}
/// <summary>
/// Deploys a portable copy of Zero Install to a temp directory and delegates the actual removal of the current instance to this copy.
/// </summary>
private void DelegateToTempCopy()
{
string tempDir = new TemporaryDirectory("0install-remove");
using (var manager = new SelfManager(tempDir, Handler, machineWide: false, portable: true))
manager.Deploy();
string path = Path.Combine(tempDir, "0install-win.exe");
if (!File.Exists(path)) path = Path.Combine(tempDir, "0install.exe");
var args = new List<string> {Self.Name, RemoveHelper.Name, Locations.InstallBase};
if (Handler.Verbosity == Verbosity.Batch) args.Add("--batch");
if (Handler.Background) args.Add("--background");
new ProcessStartInfo(path, args.JoinEscapeArguments())
{
UseShellExecute = false,
WorkingDirectory = tempDir
}.Start();
}
}
/// <summary>
/// Internal helper for <see cref="Remove"/> used to support self-removal on Windows.
/// </summary>
private class RemoveHelper(ICommandHandler handler) : RemoveSubCommandBase(handler)
{
public const string Name = "remove-helper";
public override string Description => "Internal helper for '0install maintenance remove' used to support self-removal on Windows.";
public override string Usage => "TARGET";
protected override int AdditionalArgsMin => 1;
protected override int AdditionalArgsMax => 1;
protected override string TargetDir => AdditionalArgs[0];
public override ExitCode Execute()
{
try
{
if (!Locations.IsPortable || !WindowsUtils.IsWindows)
throw new NotSupportedException("This command is used as an internal helper and should not be called manually.");
PerformRemove();
return ExitCode.OK;
}
finally
{
WindowsSelfDelete();
}
}
}
/// <summary>
/// Use cmd.exe to delete own installation directory after 8s delay
/// </summary>
private static void WindowsSelfDelete()
=> new ProcessStartInfo("cmd.exe", $"/c (ping 127.0.0.1 -n 8 || ping ::1 -n 8) & rd /s /q {Locations.InstallBase.EscapeArgument()}")
{
UseShellExecute = false,
CreateNoWindow = true
}.Start();
}