diff --git a/ref/Microsoft.Build/net/Microsoft.Build.cs b/ref/Microsoft.Build/net/Microsoft.Build.cs index 6a73e861935..ef0efa9c422 100644 --- a/ref/Microsoft.Build/net/Microsoft.Build.cs +++ b/ref/Microsoft.Build/net/Microsoft.Build.cs @@ -1096,7 +1096,6 @@ public enum NodeAffinity Any = 2, InProc = 0, OutOfProc = 1, - RarNode = 3, } public enum NodeEngineShutdownReason { diff --git a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs index 44a5cf67099..4240899acad 100644 --- a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs +++ b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs @@ -1090,7 +1090,6 @@ public enum NodeAffinity Any = 2, InProc = 0, OutOfProc = 1, - RarNode = 3, } public enum NodeEngineShutdownReason { diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 7b0b1998900..eb9a12cdb3c 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -2021,6 +2021,13 @@ private void PerformSchedulingActions(IEnumerable responses) } } + internal NodeInfo CreateRarNode() + { + NodeConfiguration configuration = GetNodeConfiguration(); + configuration.RarNode = true; + return _nodeManager.CreateNode(configuration, NodeAffinity.OutOfProc); + } + /// /// Completes a submission using the specified overall results. /// diff --git a/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs b/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs index 702c0a0a87e..05ea23f5425 100644 --- a/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs +++ b/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs @@ -61,7 +61,6 @@ public void RegisterDefaultFactories() _componentEntriesByType[BuildComponentType.InProcNodeProvider] = new BuildComponentEntry(BuildComponentType.InProcNodeProvider, NodeProviderInProc.CreateComponent, CreationPattern.Singleton); _componentEntriesByType[BuildComponentType.OutOfProcNodeProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcNodeProvider, NodeProviderOutOfProc.CreateComponent, CreationPattern.Singleton); - _componentEntriesByType[BuildComponentType.OutOfProcNodeRarProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcNodeRarProvider, NodeProviderOutOfProcRar.CreateComponent, CreationPattern.Singleton); _componentEntriesByType[BuildComponentType.OutOfProcTaskHostNodeProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcTaskHostNodeProvider, NodeProviderOutOfProcTaskHost.CreateComponent, CreationPattern.Singleton); // PropertyCache, diff --git a/src/Build/BackEnd/Components/Communications/INodeProvider.cs b/src/Build/BackEnd/Components/Communications/INodeProvider.cs index d6f8116f414..bd2cacc7266 100644 --- a/src/Build/BackEnd/Components/Communications/INodeProvider.cs +++ b/src/Build/BackEnd/Components/Communications/INodeProvider.cs @@ -24,12 +24,7 @@ internal enum NodeProviderType /// /// The provider provides remote nodes. /// - Remote, - - /// - /// The provider provides RAR nodes. - /// - ResolveAssemblyReference + Remote } /// diff --git a/src/Build/BackEnd/Components/Communications/NodeManager.cs b/src/Build/BackEnd/Components/Communications/NodeManager.cs index dc61bffae81..e0c45681ff0 100644 --- a/src/Build/BackEnd/Components/Communications/NodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/NodeManager.cs @@ -35,12 +35,6 @@ internal class NodeManager : INodeManager /// private INodeProvider _outOfProcNodeProvider; - - /// - /// The node provider for out-of-proc RAR nodes. - /// - private INodeProvider _outOfProcRarNodeProvider; - /// /// The build component host. /// @@ -119,11 +113,6 @@ public NodeInfo CreateNode(NodeConfiguration configuration, NodeAffinity nodeAff nodeId = AttemptCreateNode(_outOfProcNodeProvider, configuration); } - if (nodeId == InvalidNodeId && nodeAffinity == NodeAffinity.RarNode) - { - nodeId = AttemptCreateNode(_outOfProcRarNodeProvider, configuration); - } - if (nodeId == InvalidNodeId) { return null; @@ -177,8 +166,6 @@ public void ShutdownConnectedNodes(bool enableReuse) { _outOfProcNodeProvider.ShutdownConnectedNodes(enableReuse); } - - _outOfProcRarNodeProvider?.ShutdownConnectedNodes(enableReuse); } /// @@ -191,8 +178,6 @@ public void ShutdownAllNodes() { _outOfProcNodeProvider.ShutdownAllNodes(); } - - _outOfProcRarNodeProvider?.ShutdownAllNodes(); } #endregion @@ -211,7 +196,6 @@ public void InitializeComponent(IBuildComponentHost host) _inProcNodeProvider = _componentHost.GetComponent(BuildComponentType.InProcNodeProvider) as INodeProvider; _outOfProcNodeProvider = _componentHost.GetComponent(BuildComponentType.OutOfProcNodeProvider) as INodeProvider; - _outOfProcRarNodeProvider = _componentHost.GetComponent(BuildComponentType.OutOfProcNodeRarProvider) as INodeProvider; _componentShutdown = false; @@ -233,14 +217,8 @@ public void ShutdownComponent() ((IDisposable)_outOfProcNodeProvider).Dispose(); } - if (_outOfProcRarNodeProvider is IDisposable rarNodeProvider) - { - rarNodeProvider.Dispose(); - } - _inProcNodeProvider = null; _outOfProcNodeProvider = null; - _outOfProcRarNodeProvider = null; _componentHost = null; _componentShutdown = true; diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProc.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProc.cs index a4cdb9e0e31..e59e31d7613 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProc.cs @@ -18,10 +18,15 @@ namespace Microsoft.Build.BackEnd internal class NodeProviderOutOfProc : NodeProviderOutOfProcBase, INodeProvider { /// - /// A mapping of all the nodes managed by this provider. + /// A mapping of all normal nodes managed by this provider. /// private Dictionary _nodeContexts; + /// + /// A mapping of all RAR nodes managed by this provider. + /// + private Dictionary _rarNodeContexts; + /// /// Constructor. /// @@ -87,7 +92,7 @@ public bool CreateNode(int nodeId, INodePacketFactory factory, NodeConfiguration // want to start up just a standard MSBuild out-of-proc node. // Note: We need to always pass /nodeReuse to ensure the value for /nodeReuse from msbuild.rsp // (next to msbuild.exe) is ignored. - string commandLineArgs = $"/nologo /nodemode:1 /nodeReuse:{ComponentHost.BuildParameters.EnableNodeReuse.ToString().ToLower()} /low:{ComponentHost.BuildParameters.LowPriority.ToString().ToLower()}"; + string commandLineArgs = $"/nologo /nodemode:{(configuration.RarNode ? 3 : 1)} /nodeReuse:{ComponentHost.BuildParameters.EnableNodeReuse.ToString().ToLower()} /low:{ComponentHost.BuildParameters.LowPriority.ToString().ToLower()}"; // Make it here. CommunicationsUtilities.Trace("Starting to acquire a new or existing node to establish node ID {0}...", nodeId); @@ -97,7 +102,14 @@ public bool CreateNode(int nodeId, INodePacketFactory factory, NodeConfiguration if (null != context) { - _nodeContexts[nodeId] = context; + if (configuration.RarNode) + { + _rarNodeContexts[nodeId] = context; + } + else + { + _nodeContexts[nodeId] = context; + } // Start the asynchronous read. context.BeginAsyncPacketRead(); @@ -118,9 +130,18 @@ public bool CreateNode(int nodeId, INodePacketFactory factory, NodeConfiguration /// The packet to send. public void SendData(int nodeId, INodePacket packet) { - ErrorUtilities.VerifyThrow(_nodeContexts.ContainsKey(nodeId), "Invalid node id specified: {0}.", nodeId); - - SendData(_nodeContexts[nodeId], packet); + if (_nodeContexts.ContainsKey(nodeId)) + { + SendData(_nodeContexts[nodeId], packet); + } + else if (_rarNodeContexts.ContainsKey(nodeId)) + { + SendData(_rarNodeContexts[nodeId], packet); + } + else + { + ErrorUtilities.ThrowInternalError("Invalid node id specified: {0}.", nodeId); + } } /// @@ -135,8 +156,9 @@ public void ShutdownConnectedNodes(bool enableReuse) lock (_nodeContexts) { contextsToShutDown = new List(_nodeContexts.Values); + contextsToShutDown.AddRange(_rarNodeContexts.Values); } - + ShutdownConnectedNodes(contextsToShutDown, enableReuse); } @@ -170,6 +192,7 @@ public void InitializeComponent(IBuildComponentHost host) { this.ComponentHost = host; _nodeContexts = new Dictionary(); + _rarNodeContexts = new Dictionary(); } /// @@ -197,7 +220,10 @@ private void NodeContextTerminated(int nodeId) { lock (_nodeContexts) { - _nodeContexts.Remove(nodeId); + if (!_nodeContexts.Remove(nodeId)) + { + _rarNodeContexts.Remove(nodeId); + } } } } diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 263bc3b1904..a2089118206 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -125,18 +125,18 @@ protected void ShutdownAllNodes(bool nodeReuse, NodeContextTerminateDelegate ter int timeout = 30; // Attempt to connect to the process with the handshake without low priority. - Stream nodeStream = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false, true)); + Stream nodeStream = NamedPipeUtil.TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false, true)); // If we couldn't connect attempt to connect to the process with the handshake including low priority. - nodeStream ??= TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true, true)); + nodeStream ??= NamedPipeUtil.TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true, true)); // Attempt to connect to the non-worker process // Attempt to connect to the process with the handshake without low priority. - nodeStream ??= TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false, false)); + nodeStream ??= NamedPipeUtil.TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false, false)); // If we couldn't connect attempt to connect to the process with the handshake including low priority. - nodeStream ??= TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true, false)); + nodeStream ??= NamedPipeUtil.TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true, false)); - if (null != nodeStream) + if (nodeStream != null) { // If we're able to connect to such a process, send a packet requesting its termination CommunicationsUtilities.Trace("Shutting down node with pid = {0}", nodeProcess.Id); @@ -201,7 +201,7 @@ protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, in _processesToIgnore.Add(nodeLookupKey); // Attempt to connect to each process in turn. - Stream nodeStream = TryConnectToProcess(nodeProcess.Id, 0 /* poll, don't wait for connections */, hostHandshake); + Stream nodeStream = NamedPipeUtil.TryConnectToProcess(nodeProcess.Id, 0 /* poll, don't wait for connections */, hostHandshake); if (nodeStream != null) { // Connection successful, use this node. @@ -252,7 +252,7 @@ protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, in // to the debugger process. Instead, use MSBUILDDEBUGONSTART=1 // Now try to connect to it. - Stream nodeStream = TryConnectToProcess(msbuildProcessId, TimeoutForNewNodeCreation, hostHandshake); + Stream nodeStream = NamedPipeUtil.TryConnectToProcess(msbuildProcessId, TimeoutForNewNodeCreation, hostHandshake); if (nodeStream != null) { // Connection successful, use this node. @@ -300,99 +300,6 @@ private string GetProcessesToIgnoreKey(Handshake hostHandshake, int nodeProcessI return hostHandshake.ToString() + "|" + nodeProcessId.ToString(CultureInfo.InvariantCulture); } -#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY - // This code needs to be in a separate method so that we don't try (and fail) to load the Windows-only APIs when JIT-ing the code - // on non-Windows operating systems - private void ValidateRemotePipeSecurityOnWindows(NamedPipeClientStream nodeStream) - { - SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; -#if FEATURE_PIPE_SECURITY - PipeSecurity remoteSecurity = nodeStream.GetAccessControl(); -#else - var remoteSecurity = new PipeSecurity(nodeStream.SafePipeHandle, System.Security.AccessControl.AccessControlSections.Access | - System.Security.AccessControl.AccessControlSections.Owner | System.Security.AccessControl.AccessControlSections.Group); -#endif - IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); - if (remoteOwner != identifier) - { - CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); - throw new UnauthorizedAccessException(); - } - } -#endif - - /// - /// Attempts to connect to the specified process. - /// - private Stream TryConnectToProcess(int nodeProcessId, int timeout, Handshake handshake) - { - // Try and connect to the process. - string pipeName = NamedPipeUtil.GetPipeNameOrPath("MSBuild" + nodeProcessId); - - NamedPipeClientStream nodeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous -#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY - | PipeOptions.CurrentUserOnly -#endif - ); - CommunicationsUtilities.Trace("Attempting connect to PID {0} with pipe {1} with timeout {2} ms", nodeProcessId, pipeName, timeout); - - try - { - nodeStream.Connect(timeout); - -#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY - if (NativeMethodsShared.IsWindows && !NativeMethodsShared.IsMono) - { - // Verify that the owner of the pipe is us. This prevents a security hole where a remote node has - // been faked up with ACLs that would let us attach to it. It could then issue fake build requests back to - // us, potentially causing us to execute builds that do harmful or unexpected things. The pipe owner can - // only be set to the user's own SID by a normal, unprivileged process. The conditions where a faked up - // remote node could set the owner to something else would also let it change owners on other objects, so - // this would be a security flaw upstream of us. - ValidateRemotePipeSecurityOnWindows(nodeStream); - } -#endif - - int[] handshakeComponents = handshake.RetrieveHandshakeComponents(); - for (int i = 0; i < handshakeComponents.Length; i++) - { - CommunicationsUtilities.Trace("Writing handshake part {0} to pipe {1}", i, pipeName); - nodeStream.WriteIntForHandshake(handshakeComponents[i]); - } - - // This indicates that we have finished all the parts of our handshake; hopefully the endpoint has as well. - nodeStream.WriteEndOfHandshakeSignal(); - - CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName); - -#if NETCOREAPP2_1 || MONO - nodeStream.ReadEndOfHandshakeSignal(true, timeout); -#else - nodeStream.ReadEndOfHandshakeSignal(true); -#endif - // We got a connection. - CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", pipeName); - return nodeStream; - } - catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) - { - // Can be: - // UnauthorizedAccessException -- Couldn't connect, might not be a node. - // IOException -- Couldn't connect, already in use. - // TimeoutException -- Couldn't connect, might not be a node. - // InvalidOperationException – Couldn’t connect, probably a different build - CommunicationsUtilities.Trace("Failed to connect to pipe {0}. {1}", pipeName, e.Message.TrimEnd()); - - // If we don't close any stream, we might hang up the child - if (nodeStream != null) - { - nodeStream.Dispose(); - } - } - - return null; - } - /// /// Creates a new MSBuild process /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcRar.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcRar.cs deleted file mode 100644 index 0e912831c9b..00000000000 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcRar.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Microsoft.Build.Exceptions; -using Microsoft.Build.Internal; -using Microsoft.Build.Shared; -using System.Collections.Generic; - -namespace Microsoft.Build.BackEnd -{ - internal sealed class NodeProviderOutOfProcRar : NodeProviderOutOfProcBase, INodeProvider - { - private int? _rarNodeId = null; - private NodeContext _rarNodeContext = null; - - public NodeProviderType ProviderType => NodeProviderType.ResolveAssemblyReference; - - public int AvailableNodes => 1; - - public bool CreateNode(int nodeId, INodePacketFactory factory, NodeConfiguration configuration) - { - ErrorUtilities.VerifyThrowArgumentNull(factory, "factory"); - ErrorUtilities.VerifyThrow(_rarNodeId.HasValue, "RAR node already created."); - - // Start the new process. We pass in a node mode with a node number of 3, to indicate that we - // want to start up RAR out-of-proc node. - // Note: We need to always pass /nodeReuse to ensure the value for /nodeReuse from msbuild.rsp - // (next to msbuild.exe) is ignored. - string commandLineArgs = $"/nologo /nodemode:3 /nodeReuse:{ComponentHost.BuildParameters.EnableNodeReuse.ToString().ToLower()} /low:{ComponentHost.BuildParameters.LowPriority.ToString().ToLower()}"; - - // Make it here. - CommunicationsUtilities.Trace("Starting to acquire a new or existing node to establish node ID {0}...", nodeId); - - Handshake hostHandshake = new Handshake(CommunicationsUtilities.GetHandshakeOptions(taskHost: false, - nodeReuse: ComponentHost.BuildParameters.EnableNodeReuse, - lowPriority: ComponentHost.BuildParameters.LowPriority, - is64Bit: EnvironmentUtilities.Is64BitProcess, - workerNode: false)); - NodeContext context = GetNode(null, commandLineArgs, nodeId, factory, hostHandshake, NodeContextTerminated); - - if (context != null) - { - _rarNodeId = nodeId; - _rarNodeContext = context; - - // Is this necesarry? - // Start the asynchronous read. - context.BeginAsyncPacketRead(); - - // Configure the node. - context.SendData(configuration); - - return true; - } - - throw new BuildAbortedException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("CouldNotConnectToMSBuildExe", ComponentHost.BuildParameters.NodeExeLocation)); - } - - private void NodeContextTerminated(int nodeId) - { - if (_rarNodeId == nodeId) - { - _rarNodeId = null; - _rarNodeContext = null; - } - } - - #region IBuildComponent Members - - /// - /// Initializes the component. - /// - /// The component host. - public void InitializeComponent(IBuildComponentHost host) - { - this.ComponentHost = host; - } - - /// - /// Shuts down the component - /// - public void ShutdownComponent() - { - } - - #endregion - - /// - /// Static factory for component creation. - /// - static internal IBuildComponent CreateComponent(BuildComponentType componentType) - { - ErrorUtilities.VerifyThrow(componentType == BuildComponentType.OutOfProcNodeRarProvider, "Factory cannot create components of type {0}", componentType); - return new NodeProviderOutOfProcRar(); - } - - public void SendData(int node, INodePacket packet) - { - ErrorUtilities.VerifyThrowInternalNull(packet, "Packet is null"); - - if (_rarNodeId == node) - { - _rarNodeContext.SendData(packet); - } - } - - public void ShutdownAllNodes() - { - // If no BuildParameters were specified for this build, - // we must be trying to shut down idle nodes from some - // other, completed build. If they're still around, - // they must have been started with node reuse. - bool nodeReuse = ComponentHost.BuildParameters?.EnableNodeReuse ?? true; - - // To avoid issues with mismatched priorities not shutting - // down all the nodes on exit, we will attempt to shutdown - // all matching nodes with and without the priority bit set. - // This means we need both versions of the handshake. - - // RAR node is special... - if (!nodeReuse) - ShutdownAllNodes(nodeReuse, NodeContextTerminated); - } - - public void ShutdownConnectedNodes(bool enableReuse) - { - // RAR node is special - if (_rarNodeId != null && !enableReuse) - { - List contextList = new List() - { - _rarNodeContext - }; - ShutdownConnectedNodes(contextList, enableReuse); - } - } - } -} diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs index b5bb2110a26..8ada8167e06 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs @@ -997,21 +997,7 @@ private void VerifyActiveProxy() /// bool IRarBuildEngine.CreateRarNode() { - var nodeManager = _host.GetComponent(BuildComponentType.NodeManager) as INodeManager; - - var configuration = new NodeConfiguration - ( - -1, /* must be assigned by the NodeManager */ - _host.BuildParameters, - null - #if FEATURE_APPDOMAIN - , AppDomain.CurrentDomain.SetupInformation - #endif - , new LoggingNodeConfiguration(_host.LoggingService.IncludeEvaluationMetaprojects, _host.LoggingService.IncludeEvaluationProfile, _host.LoggingService.IncludeTaskInputs) - ); - - var nodeInfo = nodeManager.CreateNode(configuration, NodeAffinity.RarNode); - + NodeInfo nodeInfo = BuildManager.DefaultBuildManager.CreateRarNode(); return nodeInfo != null; } @@ -1020,7 +1006,7 @@ bool IRarBuildEngine.CreateRarNode() /// string IRarBuildEngine.GetRarPipeName() { - var parameters = _host.BuildParameters; + BuildParameters parameters = _host.BuildParameters; return CommunicationsUtilities.GetRarPipeName(parameters.EnableNodeReuse, parameters.LowPriority); } @@ -1029,7 +1015,7 @@ string IRarBuildEngine.GetRarPipeName() /// NamedPipeClientStream IRarBuildEngine.GetRarClientStream(string pipeName, int timeout) { - return NamedPipeUtil.GetClientStream(pipeName, timeout); + return NamedPipeUtil.TryConnectToProcess(pipeName, timeout, null); } } } diff --git a/src/Build/BackEnd/Node/NodeConfiguration.cs b/src/Build/BackEnd/Node/NodeConfiguration.cs index 3708b9be7f2..373a2b869a2 100644 --- a/src/Build/BackEnd/Node/NodeConfiguration.cs +++ b/src/Build/BackEnd/Node/NodeConfiguration.cs @@ -39,6 +39,7 @@ internal class NodeConfiguration : INodePacket /// The logging configuration for the node. /// private LoggingNodeConfiguration _loggingNodeConfiguration; + private bool _rarNode; #if FEATURE_APPDOMAIN /// @@ -153,7 +154,7 @@ public LoggingNodeConfiguration LoggingNodeConfiguration { return _loggingNodeConfiguration; } } -#region INodePacket Members + #region INodePacket Members /// /// Retrieves the packet type. @@ -168,7 +169,7 @@ public NodePacketType Type /// /// Indicates if the node is RAR node or not (executes only one task) /// - public bool RarNode { get; } + public bool RarNode { get => _rarNode; set => _rarNode = value; } #endregion @@ -187,6 +188,7 @@ public void Translate(ITranslator translator) translator.TranslateDotNet(ref _appDomainSetup); #endif translator.Translate(ref _loggingNodeConfiguration); + translator.Translate(ref _rarNode); } /// @@ -198,7 +200,7 @@ internal static INodePacket FactoryForDeserialization(ITranslator translator) configuration.Translate(translator); return configuration; } -#endregion + #endregion /// /// We need to clone this object since it gets modified for each node which is launched. diff --git a/src/Build/BackEnd/Node/RarNode.cs b/src/Build/BackEnd/Node/RarNode.cs index 39194357e04..d04cafff383 100644 --- a/src/Build/BackEnd/Node/RarNode.cs +++ b/src/Build/BackEnd/Node/RarNode.cs @@ -35,8 +35,8 @@ public NodeEngineShutdownReason Run(bool nodeReuse, bool lowPriority, out Except { var cancellationTokenSource = new CancellationTokenSource(); - var pipeName = CommunicationsUtilities.GetRarPipeName(nodeReuse, lowPriority); - var controller = new RarController(pipeName); + string pipeName = CommunicationsUtilities.GetRarPipeName(nodeReuse, lowPriority); + RarController controller = new RarController(pipeName); Console.CancelKeyPress += (e, sender) => cancellationTokenSource.Cancel(); diff --git a/src/Build/Instance/HostServices.cs b/src/Build/Instance/HostServices.cs index 8a4e9eaa2f1..4035072f365 100644 --- a/src/Build/Instance/HostServices.cs +++ b/src/Build/Instance/HostServices.cs @@ -35,12 +35,7 @@ public enum NodeAffinity /// /// The project may be scheduled anywhere. /// - Any, - - /// - /// The node accepts only RAR tasks. - /// - RarNode + Any } /// diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 9d474f889cf..966336543eb 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -157,7 +157,6 @@ - diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index a26d0b3359d..57c4bacd7c0 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -2651,7 +2651,8 @@ private static void StartLocalNode(CommandLineSwitches commandLineSwitches) var node = new RarNode(); // If FEATURE_NODE_REUSE is OFF, just validates that the switch is OK, and always returns False bool nodeReuse = ProcessNodeReuseSwitch(commandLineSwitches[CommandLineSwitches.ParameterizedSwitch.NodeReuse]); - bool lowPriority = commandLineSwitches[CommandLineSwitches.ParameterizedSwitch.LowPriority][0].Equals("true"); + string[] lowPriorityInput = commandLineSwitches[CommandLineSwitches.ParameterizedSwitch.LowPriority]; + bool lowPriority = lowPriorityInput.Length > 0 ? lowPriorityInput[0].Equals("true") : false; shutdownReason = node.Run(nodeReuse, lowPriority, out nodeException); } diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index 32d6ca9b5a9..250e5a2efcf 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -57,7 +57,12 @@ internal enum HandshakeOptions /// /// Building with administrator privileges /// - Administrator = 32 + Administrator = 32, + + /// + /// Is worker node. Worker node can accept normal build requests. + /// + Worker = 64 } internal readonly struct Handshake @@ -527,6 +532,10 @@ internal static HandshakeOptions GetHandshakeOptions(bool taskHost, bool is64Bit context |= HandshakeOptions.Administrator; } #endif + if (workerNode) + { + context |= HandshakeOptions.Worker; + } return context; } @@ -648,28 +657,8 @@ internal static int AvoidEndOfHandshakeSignal(int x) internal static string GetRarPipeName(bool nodeReuse, bool lowPriority) { - var context = HandshakeOptions.None; + var context = GetHandshakeOptions(true); var userName = $"{Environment.UserDomainName}.{Environment.UserName}"; - var clrVersion = ClrVersion; - var is64Bit = XMakeAttributes.GetCurrentMSBuildArchitecture().Equals(XMakeAttributes.MSBuildArchitectureValues.x64); - - if (is64Bit) - { - context |= HandshakeOptions.X64; - } - if (clrVersion == 2) - { - context |= HandshakeOptions.CLR2; - } - if (nodeReuse) - { - context |= HandshakeOptions.NodeReuse; - } - if (lowPriority) - { - context |= HandshakeOptions.LowPriority; - } - return $"MSBuild.RAR.{userName}.{(int)context}"; } } diff --git a/src/Shared/NamedPipeUtil.cs b/src/Shared/NamedPipeUtil.cs index a34217b02ea..427e4eb7410 100644 --- a/src/Shared/NamedPipeUtil.cs +++ b/src/Shared/NamedPipeUtil.cs @@ -29,14 +29,61 @@ internal static string GetPipeNameOrPath(string pipeName) return pipeName; } } - internal static NamedPipeClientStream GetClientStream(string pipeName, int timeout) + +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + // This code needs to be in a separate method so that we don't try (and fail) to load the Windows-only APIs when JIT-ing the code + // on non-Windows operating systems + private static void ValidateRemotePipeSecurityOnWindows(NamedPipeClientStream nodeStream) + { + SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; +#if FEATURE_PIPE_SECURITY + PipeSecurity remoteSecurity = nodeStream.GetAccessControl(); +#else + var remoteSecurity = new PipeSecurity(nodeStream.SafePipeHandle, System.Security.AccessControl.AccessControlSections.Access | + System.Security.AccessControl.AccessControlSections.Owner | System.Security.AccessControl.AccessControlSections.Group); +#endif + IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); + if (remoteOwner != identifier) + { + CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); + throw new UnauthorizedAccessException(); + } + } +#endif + /// + /// Attempts to connect to the specified process. + /// + internal static NamedPipeClientStream TryConnectToProcess(int nodeProcessId, int timeout, Handshake? handshake) + { + string pipeName = GetPipeNameOrPath("MSBuild" + nodeProcessId); + return TryConnectToProcess(pipeName, timeout, timeout, handshake); + } + + /// + /// Attempts to connect to the specified process. + /// + internal static NamedPipeClientStream TryConnectToProcess(string pipeName, int timeout, Handshake? handshake) + { + return TryConnectToProcess(pipeName, null, timeout, handshake); + } + + private static NamedPipeClientStream TryConnectToProcess(string pipeName, int? nodeProcessId, int timeout, Handshake? handshake) { // Try and connect to the process. - var nodeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous + NamedPipeClientStream nodeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous #if FEATURE_PIPEOPTIONS_CURRENTUSERONLY | PipeOptions.CurrentUserOnly #endif ); + if (nodeProcessId.HasValue) + { + CommunicationsUtilities.Trace("Attempting connect to PID {0} with pipe {1} with timeout {2} ms", nodeProcessId.Value, pipeName, timeout); + } + else + { + CommunicationsUtilities.Trace("Attempting connect to process {0} with pipe {1} with timeout {2} ms", pipeName, pipeName, timeout); + } + try { nodeStream.Connect(timeout); @@ -51,11 +98,32 @@ internal static NamedPipeClientStream GetClientStream(string pipeName, int timeo // remote node could set the owner to something else would also let it change owners on other objects, so // this would be a security flaw upstream of us. ValidateRemotePipeSecurityOnWindows(nodeStream); + } +#endif + if (handshake.HasValue) + { + int[] handshakeComponents = handshake.Value.RetrieveHandshakeComponents(); + for (int i = 0; i < handshakeComponents.Length; i++) + { + CommunicationsUtilities.Trace("Writing handshake part {0} to pipe {1}", i, pipeName); + nodeStream.WriteIntForHandshake(handshakeComponents[i]); + } + + // This indicates that we have finished all the parts of our handshake; hopefully the endpoint has as well. + nodeStream.WriteEndOfHandshakeSignal(); + CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName); + +#if NETCOREAPP2_1 || MONO + nodeStream.ReadEndOfHandshakeSignal(true, timeout); +#else + nodeStream.ReadEndOfHandshakeSignal(true); +#endif } + // We got a connection. + CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", pipeName); return nodeStream; -#endif } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { @@ -64,6 +132,7 @@ internal static NamedPipeClientStream GetClientStream(string pipeName, int timeo // IOException -- Couldn't connect, already in use. // TimeoutException -- Couldn't connect, might not be a node. // InvalidOperationException – Couldn’t connect, probably a different build + CommunicationsUtilities.Trace("Failed to connect to pipe {0}. {1}", pipeName, e.Message.TrimEnd()); // If we don't close any stream, we might hang up the child if (nodeStream != null) @@ -74,26 +143,5 @@ internal static NamedPipeClientStream GetClientStream(string pipeName, int timeo return null; } - -#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY - // This code needs to be in a separate method so that we don't try (and fail) to load the Windows-only APIs when JIT-ing the code - // on non-Windows operating systems - private static void ValidateRemotePipeSecurityOnWindows(NamedPipeClientStream nodeStream) - { - SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; -#if FEATURE_PIPE_SECURITY - PipeSecurity remoteSecurity = nodeStream.GetAccessControl(); -#else - var remoteSecurity = new PipeSecurity(nodeStream.SafePipeHandle, System.Security.AccessControl.AccessControlSections.Access | - System.Security.AccessControl.AccessControlSections.Owner | System.Security.AccessControl.AccessControlSections.Group); -#endif - IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); - if (remoteOwner != identifier) - { - CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); - throw new UnauthorizedAccessException(); - } - } -#endif } } diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index a1f0a6ceb75..22e76612f40 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -2996,7 +2996,7 @@ public override bool Execute() { // Client is connected to the RAR node, we can execute RAR task remotely // return client.Execute(); // TODO: Let it do something. - //var number = client.GetNumber(42); + // var number = client.GetNumber(42); } } diff --git a/src/Tasks/ResolveAssemblyReferences/Client/RarClient.cs b/src/Tasks/ResolveAssemblyReferences/Client/RarClient.cs index 29965290b84..5c25cf823fd 100644 --- a/src/Tasks/ResolveAssemblyReferences/Client/RarClient.cs +++ b/src/Tasks/ResolveAssemblyReferences/Client/RarClient.cs @@ -26,8 +26,8 @@ public RarClient(IRarBuildEngine rarBuildEngine) internal bool Connect() { - var pipeName = _rarBuildEngine.GetRarPipeName(); - var stream = _rarBuildEngine.GetRarClientStream(pipeName, ConnectionTimeout); + string pipeName = _rarBuildEngine.GetRarPipeName(); + NamedPipeClientStream stream = _rarBuildEngine.GetRarClientStream(pipeName, ConnectionTimeout); if (stream == null) return false; // We couldn't connect @@ -43,7 +43,7 @@ internal bool CreateNode() internal int GetNumber(int parameter) { - using var client = GetRpcClient(); + using IResolveAssemblyReferenceTaskHandler client = GetRpcClient(); // TODO: Find out if there is any possibility of awaiting it. return client.GetNumber(parameter).GetAwaiter().GetResult(); } @@ -52,7 +52,7 @@ private IResolveAssemblyReferenceTaskHandler GetRpcClient() { ErrorUtilities.VerifyThrowArgumentNull(_clientStream, nameof(_clientStream)); - var handler = RpcUtils.GetRarMessageHandler(_clientStream); + IJsonRpcMessageHandler handler = RpcUtils.GetRarMessageHandler(_clientStream); return JsonRpc.Attach(handler); }