Example #1
0
        public void ListenAndDispatchConnections(TimeSpan?keepAlive, CancellationToken cancellationToken = default)
        {
            _state              = State.Running;
            _keepAlive          = keepAlive;
            _keepAliveIsDefault = true;

            try
            {
                _clientConnectionHost.BeginListening();
                ListenAndDispatchConnectionsCore(cancellationToken);
            }
            finally
            {
                _state       = State.Completed;
                _gcTask      = null;
                _timeoutTask = null;

                if (_clientConnectionHost.IsListening)
                {
                    _clientConnectionHost.EndListening();
                }

                // This type is responsible for cleaning up resources associated with _listenTask. Once EndListening
                // is complete this task is guaranteed to be completed. If it ran to completion we need to
                // dispose of the value.
                Console.WriteLine(_listenTask?.Status);
                Debug.Assert(_listenTask is null || _listenTask.IsCompleted);
                if (_listenTask?.Status == TaskStatus.RanToCompletion)
                {
                    try
                    {
                        _listenTask.Result.Dispose();
                    }
                    catch (Exception ex)
                    {
                        CompilerServerLogger.LogException(ex, $"Error disposing of {nameof(_listenTask)}");
                    }
                }
            }
            CompilerServerLogger.Log($"End ListenAndDispatchConnections");
        }
        public static int Main(string[] args)
        {
            using var logger = new CompilerServerLogger();

            NameValueCollection appSettings;

            try
            {
#if BOOTSTRAP
                ExitingTraceListener.Install();
#endif

#if NET472
                appSettings = System.Configuration.ConfigurationManager.AppSettings;
#else
                // Do not use AppSettings on non-desktop platforms
                appSettings = new NameValueCollection();
#endif
            }
            catch (Exception ex)
            {
                // It is possible for AppSettings to throw when the application or machine configuration
                // is corrupted.  This should not prevent the server from starting, but instead just revert
                // to the default configuration.
                appSettings = new NameValueCollection();
                logger.LogException(ex, "Error loading application settings");
            }

            try
            {
                var controller = new BuildServerController(appSettings, logger);
                return(controller.Run(args));
            }
            catch (Exception e)
            {
                // Assume the exception was the result of a missing compiler assembly.
                logger.LogException(e, "Cannot start server");
            }

            return(CommonCompiler.Failed);
        }
Example #3
0
        public static int Main(string[] args)
        {
            NameValueCollection appSettings;

            try
            {
                appSettings = ConfigurationManager.AppSettings;
            }
            catch (Exception ex)
            {
                // It is possible for AppSettings to throw when the application or machine configuration
                // is corrupted.  This should not prevent the server from starting, but instead just revert
                // to the default configuration.
                appSettings = new NameValueCollection();
                CompilerServerLogger.LogException(ex, "Error loading application settings");
            }

            var controller = new DesktopBuildServerController(appSettings);

            return(controller.Run(args));
        }
        /// <summary>
        /// Does the client of "pipeStream" have the same identity and elevation as we do?
        /// </summary>
        private static bool ClientAndOurIdentitiesMatch(NamedPipeServerStream pipeStream)
        {
            if (PlatformInformation.IsWindows)
            {
                var serverIdentity = GetIdentity(impersonating: false);

                (string name, bool admin)clientIdentity = default;
                pipeStream.RunAsClient(() => { clientIdentity = GetIdentity(impersonating: true); });

                CompilerServerLogger.Log($"Server identity = '{serverIdentity.name}', server elevation='{serverIdentity.admin}'.");
                CompilerServerLogger.Log($"Client identity = '{clientIdentity.name}', client elevation='{serverIdentity.admin}'.");

                return
                    (StringComparer.OrdinalIgnoreCase.Equals(serverIdentity.name, clientIdentity.name) &&
                     serverIdentity.admin == clientIdentity.admin);
            }
            else
            {
                return(BuildServerConnection.CheckIdentityUnix(pipeStream));
            }
        }
Example #5
0
        public static int Main(string[] args)
        {
            CompilerServerLogger.Initialize("SRV");
            CompilerServerLogger.Log("Process started");

            string pipeName;
            bool   shutdown;

            if (!ParseCommandLine(args, out pipeName, out shutdown))
            {
                return(CommonCompiler.Failed);
            }

            var cancellationTokenSource = new CancellationTokenSource();

            Console.CancelKeyPress += (sender, e) => { cancellationTokenSource.Cancel(); };

            return(shutdown
                ? RunShutdown(pipeName, cancellationToken: cancellationTokenSource.Token)
                : RunServer(pipeName, cancellationToken: cancellationTokenSource.Token));
        }
            public ulong Reserved; //always 0

            public static bool IsMemoryAvailable()
            {
                MemoryHelper status = new MemoryHelper();

                GlobalMemoryStatusEx(status);
                ulong max  = status.MaxVirtual;
                ulong free = status.AvailableVirtual;

                int    shift = 20;
                string unit  = "MB";

                if (free >> shift == 0)
                {
                    shift = 10;
                    unit  = "KB";
                }

                CompilerServerLogger.Log("Free memory: {1}{0} of {2}{0}.", unit, free >> shift, max >> shift);

                return(free >= 800 << 20); // Value (500MB) is arbitrary; feel free to improve.
            }
        /// <summary>
        /// Creates a Task representing the processing of the new connection.  This will return a task that
        /// will never fail.  It will always produce a <see cref="ConnectionData"/> value.  Connection errors
        /// will end up being represented as <see cref="CompletionReason.ClientDisconnect"/>
        /// </summary>
        internal static async Task <ConnectionData> CreateHandleConnectionTask(Task <NamedPipeServerStream> pipeStreamTask, IRequestHandler handler, CancellationToken cancellationToken)
        {
            Connection connection;

            try
            {
                var pipeStream = await pipeStreamTask.ConfigureAwait(false);

                var clientConnection = new NamedPipeClientConnection(pipeStream);
                connection = new Connection(clientConnection, handler);
            }
            catch (Exception ex)
            {
                // Unable to establish a connection with the client.  The client is responsible for
                // handling this case.  Nothing else for us to do here.
                CompilerServerLogger.LogException(ex, "Error creating client named pipe");
                return(new ConnectionData(CompletionReason.CompilationNotStarted));
            }

            return(await connection.ServeConnection(cancellationToken).ConfigureAwait(false));
        }
Example #8
0
        private static int Run(TimeSpan?keepAliveTimeout, string compilerExeDirectory, string pipeName)
        {
            try
            {
                int    keepAliveValue;
                string keepAliveStr = ConfigurationManager.AppSettings["keepalive"];
                if (int.TryParse(keepAliveStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out keepAliveValue) &&
                    keepAliveValue >= 0)
                {
                    if (keepAliveValue == 0)
                    {
                        // This is a one time server entry.
                        keepAliveTimeout = null;
                    }
                    else
                    {
                        keepAliveTimeout = TimeSpan.FromSeconds(keepAliveValue);
                    }
                }
                else
                {
                    keepAliveTimeout = s_defaultServerKeepAlive;
                }
            }
            catch (ConfigurationErrorsException e)
            {
                keepAliveTimeout = s_defaultServerKeepAlive;
                CompilerServerLogger.LogException(e, "Could not read AppSettings");
            }

            CompilerServerLogger.Log("Keep alive timeout is: {0} milliseconds.", keepAliveTimeout?.TotalMilliseconds ?? 0);
            FatalError.Handler = FailFast.OnFatalException;

            var dispatcher = new ServerDispatcher(new CompilerRequestHandler(compilerExeDirectory), new EmptyDiagnosticListener());

            dispatcher.ListenAndDispatchConnections(
                pipeName,
                keepAliveTimeout);
            return(CommonCompiler.Succeeded);
        }
Example #9
0
        /// <summary>
        /// Create an instance of the pipe. This might be the first instance, or a subsequent instance.
        /// There always needs to be an instance of the pipe created to listen for a new client connection.
        /// </summary>
        /// <returns>The pipe instance, or NULL if the pipe couldn't be created..</returns>
        private NamedPipeServerStream ConstructPipe()
        {
            // Add the process ID onto the pipe name so each process gets a unique pipe name.
            // The client must user this algorithm too to connect.
            string pipeName = basePipeName + Process.GetCurrentProcess().Id.ToString();

            try
            {
                CompilerServerLogger.Log("Constructing pipe '{0}'.", pipeName);

                SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner;
                PipeSecurity       security   = new PipeSecurity();

                // Restrict access to just this account.
                PipeAccessRule rule = new PipeAccessRule(identifier, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow);
                security.AddAccessRule(rule);
                security.SetOwner(identifier);

                NamedPipeServerStream pipeStream = new NamedPipeServerStream(
                    pipeName,
                    PipeDirection.InOut,
                    NamedPipeServerStream.MaxAllowedServerInstances, // Maximum connections.
                    PipeTransmissionMode.Byte,
                    PipeOptions.Asynchronous | PipeOptions.WriteThrough,
                    PipeBufferSize, // Default input buffer
                    PipeBufferSize, // Default output buffer
                    security,
                    HandleInheritability.None);

                CompilerServerLogger.Log("Successfully constructed pipe '{0}'.", pipeName);

                return(pipeStream);
            }
            catch (Exception e)
            {
                // Windows may not create the pipe for a number of reasons.
                CompilerServerLogger.LogException(e, string.Format("Construction of pipe '{0}' failed", pipeName));
                return(null);
            }
        }
Example #10
0
        public static bool Check(string baseDirectory, IEnumerable <CommandLineAnalyzerReference> analyzerReferences, IAnalyzerAssemblyLoader loader, IEnumerable <string> ignorableReferenceNames = null)
        {
            if (ignorableReferenceNames == null)
            {
                ignorableReferenceNames = s_defaultIgnorableReferenceNames;
            }

            try
            {
                CompilerServerLogger.Log("Begin Analyzer Consistency Check");
                return(CheckCore(baseDirectory, analyzerReferences, loader, ignorableReferenceNames));
            }
            catch (Exception e)
            {
                CompilerServerLogger.LogException(e, "Analyzer Consistency Check");
                return(false);
            }
            finally
            {
                CompilerServerLogger.Log("End Analyzer Consistency Check");
            }
        }
Example #11
0
        public static int Main(string[] args)
        {
            NameValueCollection appSettings;

            try
            {
#if NET46
                appSettings = System.Configuration.ConfigurationManager.AppSettings;
#else
                // Do not use AppSettings on non-desktop platforms
                appSettings = new NameValueCollection();
#endif
            }
            catch (Exception ex)
            {
                // It is possible for AppSettings to throw when the application or machine configuration
                // is corrupted.  This should not prevent the server from starting, but instead just revert
                // to the default configuration.
                appSettings = new NameValueCollection();
                CompilerServerLogger.LogException(ex, "Error loading application settings");
            }

            try
            {
                var controller = new DesktopBuildServerController(appSettings);
                return(controller.Run(args));
            }
            catch (FileNotFoundException e)
            {
                // Assume the exception was the result of a missing compiler assembly.
                LogException(e);
            }
            catch (TypeInitializationException e) when(e.InnerException is FileNotFoundException)
            {
                // Assume the exception was the result of a missing compiler assembly.
                LogException((FileNotFoundException)e.InnerException);
            }
            return(CommonCompiler.Failed);
        }
Example #12
0
 /// <summary>
 /// The timeout was fired -- check if we need to cancel the pipe.
 /// </summary>
 private void ServerDieTimeoutFired(Task timeoutTask)
 {
     // If the timeout wasn't cancelled and we have no connections
     // we should shut down
     if (!timeoutTask.IsCanceled && this.activeConnectionCount == 0)
     {
         // N.B. There is no way to cancel waiting for a connection other than closing the
         // pipe, so there is a race between closing the pipe and getting another
         // connection. We should close the pipe as soon as possible and do any necessary
         // cleanup afterwards
         this.waitingPipeStream.Close();
         CompilerServerLogger.Log("Waiting for pipe connection timed out after {0} ms.",
                                  this.serverDieTimeout);
     }
     else
     {
         lock (this.timeoutLockObject)
         {
             this.timeoutCTS = null;
         }
     }
 }
Example #13
0
        public async Task WriteAsync(Stream outStream,
                                     CancellationToken cancellationToken)
        {
            using (var writer = new BinaryWriter(new MemoryStream(), Encoding.Unicode))
            {
                // Format the response
                CompilerServerLogger.Log("Formatting Response");
                writer.Write((int)this.Type);

                this.AddResponseBody(writer);
                writer.Flush();

                cancellationToken.ThrowIfCancellationRequested();

                // Send the response to the client

                // Grab the MemoryStream and its internal buffer to prevent
                // making another copy.
                var stream = (MemoryStream)writer.BaseStream;
                // Write the length of the response
                uint length = (uint)stream.Length;
                CompilerServerLogger.Log("Writing response length");
                // There is no way to know the number of bytes written to
                // the pipe stream. We just have to assume all of them are written.
                await outStream.WriteAsync(BitConverter.GetBytes(length),
                                           0,
                                           4,
                                           cancellationToken).ConfigureAwait(false);

                // Write the response
                CompilerServerLogger.Log("Writing response of size {0}", length);
                // There is no way to know the number of bytes written to
                // the pipe stream. We just have to assume all of them are written.
                await outStream.WriteAsync(stream.GetBuffer(),
                                           0,
                                           (int)length,
                                           cancellationToken).ConfigureAwait(false);
            }
        }
Example #14
0
        private const int DefaultServerDieTimeout = 3 * 60 * 60 * 1000; // 3 hours

        /// <summary>
        /// Main entry point for the process. Initialize the server dispatcher
        /// and wait for connections.
        /// </summary>
        public static int Main(string[] args)
        {
            int dieTimeout;

            // Try to get the die timeout from the app.config file.
            // Set to default if any failures
            try
            {
                string dieTimeoutStr = ConfigurationManager.AppSettings["dieTimeout"];
                if (!int.TryParse(dieTimeoutStr, out dieTimeout))
                {
                    dieTimeout = DefaultServerDieTimeout;
                }
                else if (dieTimeout > 0)
                {
                    // The die timeout in the app.config file is stored in
                    // seconds, not milliseconds
                    dieTimeout *= 1000;
                }
            }
            catch (ConfigurationErrorsException)
            {
                dieTimeout = DefaultServerDieTimeout;
            }

            CompilerFatalError.Handler = FailFast.OnFatalException;

            CompilerServerLogger.Initialize("SRV");
            CompilerServerLogger.Log("Process started");
            var dispatcher = new ServerDispatcher(BuildProtocolConstants.PipeName,
                                                  new CompilerRequestHandler(),
                                                  dieTimeout);

            //Debugger.Launch();

            dispatcher.ListenAndDispatchConnections();
            return(0);
        }
        /// <summary>
        /// Invoke the VB compiler with the given arguments and current directory, and send output and error
        /// to the given TextWriters.
        /// </summary>
        private BuildResponse BasicCompile(
            string responseFileDirectory,
            string currentDirectory,
            string libDirectory,
            string[] commandLineArguments,
            CancellationToken cancellationToken)
        {
            CompilerServerLogger.Log("CurrentDirectory = '{0}'", currentDirectory);
            CompilerServerLogger.Log("LIB = '{0}'", libDirectory);
            for (int i = 0; i < commandLineArguments.Length; ++i)
            {
                CompilerServerLogger.Log("Argument[{0}] = '{1}'", i, commandLineArguments[i]);
            }

            return(VisualBasicCompilerServer.RunCompiler(
                       responseFileDirectory,
                       commandLineArguments,
                       currentDirectory,
                       RuntimeEnvironment.GetRuntimeDirectory(),
                       libDirectory,
                       AnalyzerLoader,
                       cancellationToken));
        }
Example #16
0
        /// <summary>
        /// Invoke the VB compiler with the given arguments and current directory, and send output and error
        /// to the given TextWriters.
        /// </summary>
        private int BasicCompile(
            string currentDirectory,
            string libDirectory,
            string[] commandLineArguments,
            TextWriter output,
            CancellationToken cancellationToken,
            out bool utf8output)
        {
            CompilerServerLogger.Log("CurrentDirectory = '{0}'", currentDirectory);
            CompilerServerLogger.Log("LIB = '{0}'", libDirectory);
            for (int i = 0; i < commandLineArguments.Length; ++i)
            {
                CompilerServerLogger.Log("Argument[{0}] = '{1}'", i, commandLineArguments[i]);
            }

            return(VisualBasicCompilerServer.RunCompiler(
                       commandLineArguments,
                       currentDirectory,
                       libDirectory,
                       output,
                       cancellationToken,
                       out utf8output));
        }
Example #17
0
        /// <summary>
        /// Does the client of "pipeStream" have the same identity and elevation as we do?
        /// </summary>
        private bool ClientAndOurIdentitiesMatch()
        {
            var serverIdentity = GetIdentity(impersonating: false);

            Tuple <string, bool> clientIdentity = null;

            _pipeStream.RunAsClient(() => { clientIdentity = GetIdentity(impersonating: true); });

            CompilerServerLogger.Log(
                "Pipe {0}: Server identity = '{1}', server elevation='{2}'.",
                _loggingIdentifier,
                serverIdentity.Item1,
                serverIdentity.Item2.ToString());
            CompilerServerLogger.Log(
                "Pipe {0}: Client identity = '{1}', client elevation='{2}'.",
                _loggingIdentifier,
                clientIdentity.Item1,
                clientIdentity.Item2.ToString());

            return
                (StringComparer.OrdinalIgnoreCase.Equals(serverIdentity.Item1, clientIdentity.Item1) &&
                 serverIdentity.Item2 == clientIdentity.Item2);
        }
        /// <summary>
        /// Creates a Task that waits for a client connection to occur and returns the connected
        /// <see cref="NamedPipeServerStream"/> object.  Throws on any connection error.
        /// </summary>
        /// <param name="cancellationToken">Used to cancel the connection sequence.</param>
        private async Task <NamedPipeServerStream> CreateListenTaskCore(CancellationToken cancellationToken)
        {
            // Create the pipe and begin waiting for a connection. This
            // doesn't block, but could fail in certain circumstances, such
            // as Windows refusing to create the pipe for some reason
            // (out of handles?), or the pipe was disconnected before we
            // starting listening.
            NamedPipeServerStream pipeStream = ConstructPipe(_pipeName);

            CompilerServerLogger.Log("Waiting for new connection");
            await pipeStream.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false);

            CompilerServerLogger.Log("Pipe connection detected.");

            if (Environment.Is64BitProcess || MemoryHelper.IsMemoryAvailable())
            {
                CompilerServerLogger.Log("Memory available - accepting connection");
                return(pipeStream);
            }

            pipeStream.Close();
            throw new Exception("Insufficient resources to process new connection.");
        }
Example #19
0
        /// <summary>
        /// This function never returns. It loops and dispatches requests
        /// until the process it terminated. Each incoming request is
        /// dispatched to a new thread which runs.
        /// </summary>
        public void ListenAndDispatchConnections()
        {
            Debug.Assert(SynchronizationContext.Current == null);
            // We loop here continuously, dispatching client connections as
            // they come in, until TimeoutFired causes an exception to be
            // thrown. Each time through the loop we either have accepted a
            // client connection, or timed out. After each connection,
            // we need to create a new instance of the pipe to listen on.

            bool firstConnection = true;

            while (true)
            {
                // Create the pipe and begin waiting for a connection. This
                // doesn't block, but could fail in certain circumstances, such
                // as Windows refusing to create the pipe for some reason
                // (out of handles?), or the pipe was disconnected before we
                // starting listening.
                NamedPipeServerStream pipeStream = ConstructPipe();
                if (pipeStream == null)
                {
                    return;
                }

                this.waitingPipeStream = pipeStream;

                // If this is the first connection then we want to start a timeout
                // Otherwise, we should start the timeout when the last connection
                // finishes processing.
                if (firstConnection)
                {
                    StartTimeoutTimer();
                    firstConnection = false;
                }

                CompilerServerLogger.Log("Waiting for new connection");

                // Wait for a connection or the timeout
                // If a timeout occurs then the pipe will be closed and we will throw an exception
                // to the calling function.
                try
                {
                    pipeStream.WaitForConnection();
                }
                catch (ObjectDisposedException)
                {
                    CompilerServerLogger.Log("Listening pipe closed; exiting.");
                    break;
                }
                catch (IOException)
                {
                    CompilerServerLogger.Log("The pipe was closed or the client has been disconnected");
                    break;
                }

                // We have a connection
                CompilerServerLogger.Log("Pipe connection detected.");

                // Cancel the timeouts
                this.keepAliveTimer.CancelIfActive();

                // Dispatch the new connection on the thread pool
                var newConnection = DispatchConnection(pipeStream);
                // Connection object now owns the connected pipe.

                // Cleanup any connections that have completed, and then add
                // the new one.
                activeConnections.RemoveAll(t => t.IsCompleted);
                activeConnections.Add(newConnection);

                if (this.keepAliveTimer.StopAfterFirstConnection)
                {
                    break;
                }

                // Next time around the loop, create a new instance of the pipe
                // to listen for another connection.
            }

            Task.WhenAll(activeConnections).Wait();
        }
Example #20
0
        /// <summary>
        /// Main entry point for the process. Initialize the server dispatcher
        /// and wait for connections.
        /// </summary>
        public static int Main(string[] args)
        {
            CompilerServerLogger.Initialize("SRV");
            CompilerServerLogger.Log("Process started");

            TimeSpan?keepAliveTimeout = null;

            // VBCSCompiler is installed in the same directory as csc.exe and vbc.exe which is also the
            // location of the response files.
            var compilerExeDirectory = AppDomain.CurrentDomain.BaseDirectory;

            // Pipename should be passed as the first and only argument to the server process
            // and it must have the form "-pipename:name". Otherwise, exit with a non-zero
            // exit code
            const string pipeArgPrefix = "-pipename:";

            if (args.Length != 1 ||
                args[0].Length <= pipeArgPrefix.Length ||
                !args[0].StartsWith(pipeArgPrefix))
            {
                return(CommonCompiler.Failed);
            }

            var pipeName = args[0].Substring(pipeArgPrefix.Length);

            try
            {
                int    keepAliveValue;
                string keepAliveStr = ConfigurationManager.AppSettings["keepalive"];
                if (int.TryParse(keepAliveStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out keepAliveValue) &&
                    keepAliveValue >= 0)
                {
                    if (keepAliveValue == 0)
                    {
                        // This is a one time server entry.
                        keepAliveTimeout = null;
                    }
                    else
                    {
                        keepAliveTimeout = TimeSpan.FromSeconds(keepAliveValue);
                    }
                }
                else
                {
                    keepAliveTimeout = s_defaultServerKeepAlive;
                }
            }
            catch (ConfigurationErrorsException e)
            {
                keepAliveTimeout = s_defaultServerKeepAlive;
                CompilerServerLogger.LogException(e, "Could not read AppSettings");
            }

            CompilerServerLogger.Log("Keep alive timeout is: {0} milliseconds.", keepAliveTimeout?.TotalMilliseconds ?? 0);
            FatalError.Handler = FailFast.OnFatalException;

            var dispatcher = new ServerDispatcher(new CompilerRequestHandler(compilerExeDirectory), new EmptyDiagnosticListener());

            dispatcher.ListenAndDispatchConnections(
                pipeName,
                keepAliveTimeout,
                watchAnalyzerFiles: true);
            return(CommonCompiler.Succeeded);
        }
Example #21
0
        /// <summary>
        /// Creates a Task that waits for a client connection to occur and returns the connected
        /// <see cref="NamedPipeServerStream"/> object.  Throws on any connection error.
        /// </summary>
        /// <param name="pipeName">Name of the pipe on which the instance will listen for requests.</param>
        /// <param name="cancellationToken">Used to cancel the connection sequence.</param>
        private async Task <NamedPipeServerStream> CreateListenTask(string pipeName, CancellationToken cancellationToken)
        {
            // Create the pipe and begin waiting for a connection. This
            // doesn't block, but could fail in certain circumstances, such
            // as Windows refusing to create the pipe for some reason
            // (out of handles?), or the pipe was disconnected before we
            // starting listening.
            NamedPipeServerStream pipeStream = ConstructPipe(pipeName);

            // Unfortunately the version of .Net we are using doesn't support the WaitForConnectionAsync
            // method.  When it is available it should absolutely be used here.  In the meantime we
            // have to deal with the idea that this WaitForConnection call will block a thread
            // for a significant period of time.  It is unadvisable to do this to a thread pool thread
            // hence we will use an explicit thread here.
            var listenSource = new TaskCompletionSource <NamedPipeServerStream>();
            var listenTask   = listenSource.Task;
            var listenThread = new Thread(() =>
            {
                try
                {
                    CompilerServerLogger.Log("Waiting for new connection");
                    pipeStream.WaitForConnection();
                    CompilerServerLogger.Log("Pipe connection detected.");

                    if (Environment.Is64BitProcess || MemoryHelper.IsMemoryAvailable())
                    {
                        CompilerServerLogger.Log("Memory available - accepting connection");
                        listenSource.SetResult(pipeStream);
                        return;
                    }

                    try
                    {
                        pipeStream.Close();
                    }
                    catch
                    {
                        // Okay for Close failure here.
                    }

                    listenSource.SetException(new Exception("Insufficient resources to process new connection."));
                }
                catch (Exception ex)
                {
                    listenSource.SetException(ex);
                }
            });

            listenThread.Start();

            // Create a tasks that waits indefinitely (-1) and completes only when cancelled.
            var waitCancellationTokenSource = new CancellationTokenSource();
            var waitTask = Task.Delay(
                Timeout.Infinite,
                CancellationTokenSource.CreateLinkedTokenSource(waitCancellationTokenSource.Token, cancellationToken).Token);
            await Task.WhenAny(listenTask, waitTask).ConfigureAwait(false);

            if (listenTask.IsCompleted)
            {
                waitCancellationTokenSource.Cancel();
                return(await listenTask.ConfigureAwait(false));
            }

            // The listen operation was cancelled.  Close the pipe stream throw a cancellation exception to
            // simulate the cancel operation.
            waitCancellationTokenSource.Cancel();
            try
            {
                pipeStream.Close();
            }
            catch
            {
                // Okay for Close failure here.
            }

            throw new OperationCanceledException();
        }
Example #22
0
 private void LogException(Exception e, string description)
 {
     CompilerServerLogger.LogException(e, string.Format(LogFormat, LoggingIdentifier, description));
 }
Example #23
0
 private void Log(string message)
 {
     CompilerServerLogger.Log(LogFormat, LoggingIdentifier, message);
 }
Example #24
0
 private static void LogException(FileNotFoundException e)
 {
     CompilerServerLogger.LogException(e, "File not found");
 }
Example #25
0
        /// <summary>
        /// This function never returns. It loops and dispatches requests
        /// until the process it terminated. Each incoming request is
        /// dispatched to a new thread which runs.
        /// </summary>
        public void ListenAndDispatchConnections()
        {
            Debug.Assert(SynchronizationContext.Current == null);
            // We loop here continuously, dispatching client connections as
            // they come in, until TimeoutFired causes an exception to be
            // thrown. Each time through the loop we either have accepted a
            // client connection, or timed out. After each connection,
            // we need to create a new instance of the pipe to listen on.

            bool firstConnection = true;

            while (true)
            {
                // Create the pipe and begin waiting for a connection. This
                // doesn't block, but could fail in certain circumstances, such
                // as Windows refusing to create the pipe for some reason
                // (out of handles?), or the pipe was disconnected before we
                // starting listening.
                NamedPipeServerStream pipeStream = ConstructPipe();
                if (pipeStream == null)
                {
                    return;
                }

                this.waitingPipeStream = pipeStream;

                // If this is the first connection then we want to start a timeout
                // Otherwise, we should start the timeout when the last connection
                // finishes processing.
                if (firstConnection)
                {
                    // Since no timeout could have been started before now,
                    // this will definitely start a timeout
                    StartTimeoutTimerIfNecessary();
                    firstConnection = false;
                }

                CompilerServerLogger.Log("Waiting for new connection");

                // Wait for a connection or the timeout
                // If a timeout occurs then the pipe will be closed and we will throw an exception
                // to the calling function.
                try
                {
                    pipeStream.WaitForConnection();
                }
                catch (ObjectDisposedException)
                {
                    CompilerServerLogger.Log("Listening pipe closed; exiting.");
                    break;
                }
                catch (IOException)
                {
                    CompilerServerLogger.Log("The pipe was closed or the client has been disconnected");
                    break;
                }

                // We have a connection
                CompilerServerLogger.Log("Pipe connection detected.");

                // Cancel the timeouts
                CancelTimeoutTimerIfNecessary();

                // Dispatch the new connection on the thread pool
                // Assign to blank variable -- we want to fire & forget
                var _ = DispatchConnection(pipeStream);
                // Connection object now owns the connected pipe.

                // If our timeout is 0, then we stop after the first
                // connection. Otherwise, continue.
                if (this.serverDieTimeout == 0)
                {
                    this.dieTimeoutBlock = new AutoResetEvent(false);
                    this.dieTimeoutBlock.WaitOne();
                    break;
                }
                // Next time around the loop, create a new instance of the pipe
                // to listen for another connection.
            }
        }
Example #26
0
        private static bool CheckCore(string baseDirectory, IEnumerable <CommandLineAnalyzerReference> analyzerReferences, IAnalyzerAssemblyLoader loader, IEnumerable <string> ignorableReferenceNames)
        {
            var resolvedPaths = new List <string>();

            foreach (var analyzerReference in analyzerReferences)
            {
                string resolvedPath = FileUtilities.ResolveRelativePath(analyzerReference.FilePath, basePath: null, baseDirectory: baseDirectory, searchPaths: SpecializedCollections.EmptyEnumerable <string>(), fileExists: File.Exists);
                if (resolvedPath != null)
                {
                    resolvedPath = FileUtilities.TryNormalizeAbsolutePath(resolvedPath);
                    if (resolvedPath != null)
                    {
                        resolvedPaths.Add(resolvedPath);
                    }
                }

                // Don't worry about paths we can't resolve. The compiler will report an error for that later.
            }

            // First, check that the set of references is complete, modulo items in the safe list.
            foreach (var resolvedPath in resolvedPaths)
            {
                var missingDependencies = AssemblyUtilities.IdentifyMissingDependencies(resolvedPath, resolvedPaths);

                foreach (var missingDependency in missingDependencies)
                {
                    if (!ignorableReferenceNames.Any(name => missingDependency.Name.StartsWith(name)))
                    {
                        CompilerServerLogger.Log($"Analyzer assembly {resolvedPath} depends on '{missingDependency}' but it was not found.");
                        return(false);
                    }
                }
            }

            // Register analyzers and their dependencies upfront,
            // so that assembly references can be resolved:
            foreach (var resolvedPath in resolvedPaths)
            {
                loader.AddDependencyLocation(resolvedPath);
            }

            // Load all analyzer assemblies:
            var loadedAssemblies = new List <Assembly>();

            foreach (var resolvedPath in resolvedPaths)
            {
                loadedAssemblies.Add(loader.LoadFromPath(resolvedPath));
            }

            // Third, check that the MVIDs of the files on disk match the MVIDs of the loaded assemblies.
            for (int i = 0; i < resolvedPaths.Count; i++)
            {
                var resolvedPath       = resolvedPaths[i];
                var loadedAssembly     = loadedAssemblies[i];
                var resolvedPathMvid   = AssemblyUtilities.ReadMvid(resolvedPath);
                var loadedAssemblyMvid = loadedAssembly.ManifestModule.ModuleVersionId;

                if (resolvedPathMvid != loadedAssemblyMvid)
                {
                    CompilerServerLogger.Log($"Analyzer assembly {resolvedPath} has MVID '{resolvedPathMvid}' but loaded assembly '{loadedAssembly.FullName}' has MVID '{loadedAssemblyMvid}'.");
                    return(false);
                }
            }

            return(true);
        }
        /// <summary>
        /// Create an instance of the pipe. This might be the first instance, or a subsequent instance.
        /// There always needs to be an instance of the pipe created to listen for a new client connection.
        /// </summary>
        /// <returns>The pipe instance or throws an exception.</returns>
        private NamedPipeServerStream ConstructPipe(string pipeName)
        {
            CompilerServerLogger.Log("Constructing pipe '{0}'.", pipeName);

#if NET472
            PipeSecurity security;
            PipeOptions  pipeOptions = PipeOptions.Asynchronous | PipeOptions.WriteThrough;

            if (!PlatformInformation.IsRunningOnMono)
            {
                security = new PipeSecurity();
                SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner;

                // Restrict access to just this account.
                PipeAccessRule rule = new PipeAccessRule(identifier, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow);
                security.AddAccessRule(rule);
                security.SetOwner(identifier);
            }
            else
            {
                // Pipe security and additional access rights constructor arguments
                //  are not supported by Mono
                // https://github.com/dotnet/roslyn/pull/30810
                // https://github.com/mono/mono/issues/11406
                security = null;
                // This enum value is implemented by Mono to restrict pipe access to
                //  the current user
                const int CurrentUserOnly = unchecked ((int)0x20000000);
                pipeOptions |= (PipeOptions)CurrentUserOnly;
            }

            NamedPipeServerStream pipeStream = new NamedPipeServerStream(
                pipeName,
                PipeDirection.InOut,
                NamedPipeServerStream.MaxAllowedServerInstances, // Maximum connections.
                PipeTransmissionMode.Byte,
                pipeOptions,
                PipeBufferSize, // Default input buffer
                PipeBufferSize, // Default output buffer
                security,
                HandleInheritability.None);
#else
            // The overload of NamedPipeServerStream with the PipeAccessRule
            // parameter was removed in netstandard. However, the default
            // constructor does not provide WRITE_DAC, so attempting to use
            // SetAccessControl will always fail. So, completely ignore ACLs on
            // netcore, and trust that our `ClientAndOurIdentitiesMatch`
            // verification will catch any invalid connections.
            // Issue to add WRITE_DAC support:
            // https://github.com/dotnet/corefx/issues/24040
            NamedPipeServerStream pipeStream = new NamedPipeServerStream(
                pipeName,
                PipeDirection.InOut,
                NamedPipeServerStream.MaxAllowedServerInstances, // Maximum connections.
                PipeTransmissionMode.Byte,
                PipeOptions.Asynchronous | PipeOptions.WriteThrough,
                PipeBufferSize,  // Default input buffer
                PipeBufferSize); // Default output buffer
#endif

            CompilerServerLogger.Log("Successfully constructed pipe '{0}'.", pipeName);

            return(pipeStream);
        }
 private void Log(string message)
 {
     CompilerServerLogger.Log("Client {0}: {1}", _loggingIdentifier, message);
 }
Example #29
0
        internal async Task <CompletionData> ProcessAsync(
            Task <IClientConnection> clientConnectionTask,
            bool allowCompilationRequests       = true,
            CancellationToken cancellationToken = default)
        {
            try
            {
                return(await ProcessCore().ConfigureAwait(false));
            }
            catch (Exception ex)
            {
                CompilerServerLogger.LogException(ex, $"Error processing request for client");
                return(CompletionData.RequestError);
            }

            async Task <CompletionData> ProcessCore()
            {
                using var clientConnection = await clientConnectionTask.ConfigureAwait(false);

                var request = await clientConnection.ReadBuildRequestAsync(cancellationToken).ConfigureAwait(false);

                if (request.ProtocolVersion != BuildProtocolConstants.ProtocolVersion)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new MismatchedVersionBuildResponse(),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                if (!string.Equals(request.CompilerHash, BuildProtocolConstants.GetCommitHash(), StringComparison.OrdinalIgnoreCase))
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new IncorrectHashBuildResponse(),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                if (request.Arguments.Count == 1 && request.Arguments[0].ArgumentId == BuildProtocolConstants.ArgumentId.Shutdown)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new ShutdownBuildResponse(Process.GetCurrentProcess().Id),
                               new CompletionData(CompletionReason.RequestCompleted, shutdownRequested : true),
                               cancellationToken).ConfigureAwait(false));
                }

                if (!allowCompilationRequests)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new RejectedBuildResponse("Compilation not allowed at this time"),
                               CompletionData.RequestCompleted,
                               cancellationToken).ConfigureAwait(false));
                }

                if (!Environment.Is64BitProcess && !MemoryHelper.IsMemoryAvailable())
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               new RejectedBuildResponse("Not enough resources to accept connection"),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                return(await ProcessCompilationRequestAsync(clientConnection, request, cancellationToken).ConfigureAwait(false));
            }
        }
 private void LogException(Exception e, string message)
 {
     CompilerServerLogger.LogException(e, string.Format("Client {0}: {1}", _loggingIdentifier, message));
 }