2018-01-28 13:35:04 -08:00
|
|
|
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using Microsoft.DotNet.Cli;
|
|
|
|
|
using Microsoft.DotNet.Cli.Utils;
|
2018-04-02 12:37:25 -07:00
|
|
|
|
using Microsoft.DotNet.PlatformAbstractions;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
using Microsoft.DotNet.Tools;
|
|
|
|
|
using Microsoft.Extensions.EnvironmentAbstractions;
|
2018-04-02 12:37:25 -07:00
|
|
|
|
using Newtonsoft.Json;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
|
|
|
|
namespace Microsoft.DotNet.ShellShim
|
|
|
|
|
{
|
|
|
|
|
internal class ShellShimRepository : IShellShimRepository
|
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
private const string ApphostNameWithoutExtension = "apphost";
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
|
|
|
|
private readonly DirectoryPath _shimsDirectory;
|
2018-04-02 12:37:25 -07:00
|
|
|
|
private readonly string _appHostSourceDirectory;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
public ShellShimRepository(DirectoryPath shimsDirectory, string appHostSourcePath = null)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
|
|
|
|
_shimsDirectory = shimsDirectory;
|
2018-04-02 12:37:25 -07:00
|
|
|
|
_appHostSourceDirectory = appHostSourcePath ?? Path.Combine(ApplicationEnvironment.ApplicationBasePath,
|
|
|
|
|
"AppHostTemplate");
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CreateShim(FilePath targetExecutablePath, string commandName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(targetExecutablePath.Value))
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(CommonLocalizableStrings.CannotCreateShimForEmptyExecutablePath);
|
|
|
|
|
}
|
|
|
|
|
if (string.IsNullOrEmpty(commandName))
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(CommonLocalizableStrings.CannotCreateShimForEmptyCommand);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ShimExists(commandName))
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.ShellShimConflict,
|
|
|
|
|
commandName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransactionalAction.Run(
|
2018-04-02 12:37:25 -07:00
|
|
|
|
action: () =>
|
|
|
|
|
{
|
2018-01-28 13:35:04 -08:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(_shimsDirectory.Value))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(_shimsDirectory.Value);
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
CreateApphostShimAndConfigFile(
|
|
|
|
|
commandName,
|
|
|
|
|
entryPoint: targetExecutablePath);
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
|
{
|
|
|
|
|
SetUserExecutionPermission(GetShimPath(commandName));
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.FailedToCreateShellShim,
|
|
|
|
|
commandName,
|
|
|
|
|
ex.Message
|
|
|
|
|
),
|
|
|
|
|
ex);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
rollback: () => {
|
|
|
|
|
foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value)))
|
|
|
|
|
{
|
|
|
|
|
File.Delete(file.Value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveShim(string commandName)
|
|
|
|
|
{
|
|
|
|
|
var files = new Dictionary<string, string>();
|
|
|
|
|
TransactionalAction.Run(
|
|
|
|
|
action: () => {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value)))
|
|
|
|
|
{
|
|
|
|
|
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
|
|
|
|
File.Move(file.Value, tempPath);
|
|
|
|
|
files[file.Value] = tempPath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.FailedToRemoveShellShim,
|
|
|
|
|
commandName,
|
|
|
|
|
ex.Message
|
|
|
|
|
),
|
|
|
|
|
ex);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
commit: () => {
|
|
|
|
|
foreach (var value in files.Values)
|
|
|
|
|
{
|
|
|
|
|
File.Delete(value);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
rollback: () => {
|
|
|
|
|
foreach (var kvp in files)
|
|
|
|
|
{
|
|
|
|
|
File.Move(kvp.Value, kvp.Key);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
private void CreateApphostShimAndConfigFile(string commandName, FilePath entryPoint)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
string appHostSourcePath;
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
2018-04-02 12:37:25 -07:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
|
|
|
|
|
appHostSourceFilePath: appHostSourcePath,
|
|
|
|
|
appHostDestinationFilePath: GetShimPath(commandName).Value,
|
|
|
|
|
appBinaryName: Path.GetFileName(entryPoint.Value));
|
|
|
|
|
|
|
|
|
|
var config = JsonConvert.SerializeObject(
|
|
|
|
|
new RootObject
|
|
|
|
|
{
|
|
|
|
|
startupOptions = new StartupOptions
|
|
|
|
|
{
|
|
|
|
|
appRoot = entryPoint.GetDirectoryPath().Value
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
File.WriteAllText(GetConfigPath(commandName).Value, config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class StartupOptions
|
|
|
|
|
{
|
|
|
|
|
public string appRoot { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class RootObject
|
|
|
|
|
{
|
|
|
|
|
public StartupOptions startupOptions { get; set; }
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ShimExists(string commandName)
|
|
|
|
|
{
|
|
|
|
|
return GetShimFiles(commandName).Any(p => File.Exists(p.Value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerable<FilePath> GetShimFiles(string commandName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(commandName))
|
|
|
|
|
{
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
yield return GetShimPath(commandName);
|
|
|
|
|
yield return GetConfigPath(commandName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private FilePath GetShimPath(string commandName)
|
|
|
|
|
{
|
2018-01-28 13:35:04 -08:00
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
return new FilePath(_shimsDirectory.WithFile(commandName).Value +".exe");
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
return _shimsDirectory.WithFile(commandName);
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
private FilePath GetConfigPath(string commandName)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
return _shimsDirectory.WithFile(commandName + ".startupconfig.json");
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void SetUserExecutionPermission(FilePath path)
|
|
|
|
|
{
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CommandResult result = new CommandFactory()
|
|
|
|
|
.Create("chmod", new[] { "u+x", path.Value })
|
|
|
|
|
.CaptureStdOut()
|
|
|
|
|
.CaptureStdErr()
|
|
|
|
|
.Execute();
|
|
|
|
|
|
|
|
|
|
if (result.ExitCode != 0)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, result.StdErr));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|