dotnet-installer/src/dotnet/commands/dotnet-projectmodel-server/Program.cs

161 lines
5.6 KiB
C#

// 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.Diagnostics;
using System.Net;
using System.Net.Sockets;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.ProjectModel.Server
{
public class ProjectModelServerCommand
{
private readonly Dictionary<int, ProjectManager> _projects;
private readonly DesignTimeWorkspace _workspaceContext;
private readonly ProtocolManager _protocolManager;
private readonly string _hostName;
private readonly int _port;
private Socket _listenSocket;
public ProjectModelServerCommand(int port, string hostName)
{
_port = port;
_hostName = hostName;
_protocolManager = new ProtocolManager(maxVersion: 4);
_workspaceContext = new DesignTimeWorkspace(ProjectReaderSettings.ReadFromEnvironment());
_projects = new Dictionary<int, ProjectManager>();
}
public static int Run(string[] args)
{
var app = new CommandLineApplication();
app.Name = "dotnet-projectmodel-server";
app.Description = ".NET Project Model Server";
app.FullName = ".NET Design Time Server";
app.Description = ".NET Design Time Server";
app.HelpOption("-?|-h|--help");
var verbose = app.Option("--verbose", "Verbose ouput", CommandOptionType.NoValue);
var hostpid = app.Option("--host-pid", "The process id of the host", CommandOptionType.SingleValue);
var hostname = app.Option("--host-name", "The name of the host", CommandOptionType.SingleValue);
var port = app.Option("--port", "The TCP port used for communication", CommandOptionType.SingleValue);
app.OnExecute(() =>
{
try
{
if (!MonitorHostProcess(hostpid))
{
return 1;
}
var intPort = CheckPort(port);
if (intPort == -1)
{
return 1;
}
if (!hostname.HasValue())
{
Reporter.Error.WriteLine($"Option \"{hostname.LongName}\" is missing.");
return 1;
}
var program = new ProjectModelServerCommand(intPort, hostname.Value());
program.OpenChannel();
}
catch (Exception ex)
{
Reporter.Error.WriteLine($"Unhandled exception in server main: {ex}");
throw;
}
return 0;
});
return app.Execute(args);
}
public void OpenChannel()
{
_listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, _port));
_listenSocket.Listen(10);
Reporter.Output.WriteLine($"Process ID {Process.GetCurrentProcess().Id}");
Reporter.Output.WriteLine($"Listening on port {_port}");
while (true)
{
var acceptSocket = _listenSocket.Accept();
Reporter.Output.WriteLine($"Client accepted {acceptSocket.LocalEndPoint}");
var connection = new ConnectionContext(acceptSocket,
_hostName,
_protocolManager,
_workspaceContext,
_projects);
connection.QueueStart();
}
}
public void Shutdown()
{
if (_listenSocket.Connected)
{
_listenSocket.Shutdown(SocketShutdown.Both);
}
}
private static int CheckPort(CommandOption port)
{
if (!port.HasValue())
{
Reporter.Error.WriteLine($"Option \"{port.LongName}\" is missing.");
}
int result;
if (int.TryParse(port.Value(), out result))
{
return result;
}
else
{
Reporter.Error.WriteLine($"Option \"{port.LongName}\" is not a valid Int32 value.");
return -1;
}
}
private static bool MonitorHostProcess(CommandOption host)
{
if (!host.HasValue())
{
Console.Error.WriteLine($"Option \"{host.LongName}\" is missing.");
return false;
}
int hostPID;
if (int.TryParse(host.Value(), out hostPID))
{
var hostProcess = Process.GetProcessById(hostPID);
hostProcess.EnableRaisingEvents = true;
hostProcess.Exited += (s, e) =>
{
Process.GetCurrentProcess().Kill();
};
Reporter.Output.WriteLine($"Server will exit when process {hostPID} exits.");
return true;
}
else
{
Reporter.Error.WriteLine($"Option \"{host.LongName}\" is not a valid Int32 value.");
return false;
}
}
}
}