Skip to content

Commit

Permalink
Merge pull request #110 from microsoft/clientcli
Browse files Browse the repository at this point in the history
Add client cli tool
  • Loading branch information
abeltrano authored Jan 18, 2024
2 parents 7e5f414 + 7f9703d commit 2e96a67
Show file tree
Hide file tree
Showing 21 changed files with 798 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"davidanson.vscode-markdownlint",
"ms-vscode-remote.vscode-remote-extensionpack",
"coolbear.systemd-unit-file",
"redhat.vscode-yaml"
"redhat.vscode-yaml",
"vadimcn.vscode-lldb"
]
}
14 changes: 10 additions & 4 deletions protocol/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

set(NETREMOTE_PROTOCOL_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
set(NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_SUFFIX microsoft/net/remote/protocol)
set(NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_PREFIX ${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE}/${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_SUFFIX})

set(PROTO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protos)
set(PROTO_DIR_BINARY_SUFFIX microsoft/net/remote/protocol)
set(PROTO_DIR_BINARY ${CMAKE_CURRENT_BINARY_DIR}/${PROTO_DIR_BINARY_SUFFIX})
set(PROTO_DIR_BINARY ${CMAKE_CURRENT_BINARY_DIR}/${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_SUFFIX})
set(PROTO_FILES
${PROTO_DIR}/NetRemote.proto
${PROTO_DIR}/NetRemoteService.proto
Expand All @@ -13,7 +16,7 @@ set(PROTO_FILES
# we've encountered intermittent errors when building with external vcpkg (eg.
# via port) which results in empty file lists and thus, the generated headers
# are not installed. It appears to be some sort of race condition in CMake
# itself
# itself.
#
# file(GLOB PROTO_FILES_GENERATED "${PROTO_DIR_BINARY}/*.pb.h")
# string(REGEX REPLACE "[.]proto$" ".pb.h" PROTO_HEADERS_GENERATED "${PROTO_FILES_GENERATED}")
Expand All @@ -40,10 +43,13 @@ target_sources(${PROJECT_NAME}-protocol
${PROTO_FILES}
PUBLIC
FILE_SET HEADERS
BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}
BASE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE}
FILES
${PROTO_HEADERS_GENERATED}
${PROTO_HEADERS_GENERATED_GRPC}
${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_PREFIX}/NetRemoteProtocol.hxx
)

target_link_libraries(${PROJECT_NAME}-protocol
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

#ifndef NET_REMOTE_PROTOCOL_HXX
#define NET_REMOTE_PROTOCOL_HXX

#include <cstdint>
#include <ranges>
#include <string_view>

namespace Microsoft::Net::Remote::Protocol
{
static constexpr uint32_t NetRemotePortDefault = 5047;
static constexpr std::string_view NetRemoteAddressHttpDefault = "localhost:5047";

/**
* @brief Static NetRemote protocol information.
*/
struct NetRemoteProtocol
{
#define IP_DEFAULT "localhost"
#define PORT_DEFAULT 5047
#define xstr(s) str(s)
#define str(s) #s

static constexpr uint32_t PortDefault{ 5047 };
static constexpr std::string_view IpDefault{ "localhost" };
static constexpr std::string_view AddressDefault{ IP_DEFAULT ":" xstr(PORT_DEFAULT) };

#undef IP_DEFAULT
#undef PORT_DEFAULT
#undef xstr
#undef str
};

} // namespace Microsoft::Net::Remote::Protocol

#endif // NET_REMOTE_PROTOCOL_HXX
1 change: 1 addition & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ add_subdirectory(dotnet)
add_subdirectory(server)
add_subdirectory(service)
add_subdirectory(shared)
add_subdirectory(tools)
add_subdirectory(wifi)
20 changes: 19 additions & 1 deletion src/common/client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@

add_library(${PROJECT_NAME}-client STATIC "")

set(NETREMOTE_CLIENT_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
set(NETREMOTE_CLIENT_PUBLIC_INCLUDE_SUFFIX microsoft/net/remote)
set(NETREMOTE_CLIENT_PUBLIC_INCLUDE_PREFIX ${NETREMOTE_CLIENT_PUBLIC_INCLUDE}/${NETREMOTE_CLIENT_PUBLIC_INCLUDE_SUFFIX})

target_sources(${PROJECT_NAME}-client
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/Placeholder.cxx
NetRemoteServerConnection.cxx
PUBLIC
FILE_SET HEADERS
BASE_DIRS ${NETREMOTE_CLIENT_PUBLIC_INCLUDE}
FILES
${NETREMOTE_CLIENT_PUBLIC_INCLUDE_PREFIX}/NetRemoteServerConnection.hxx
)

target_link_libraries(${PROJECT_NAME}-client
PRIVATE
plog::plog
PUBLIC
${PROJECT_NAME}-protocol
)

install(
TARGETS ${PROJECT_NAME}-client
EXPORT ${PROJECT_NAME}
FILE_SET HEADERS
PUBLIC_HEADER DESTINATION "${NETREMOTE_DIR_INSTALL_PUBLIC_HEADER_BASE}/${NETREMOTE_CLIENT_PUBLIC_INCLUDE_SUFFIX}"
)
42 changes: 42 additions & 0 deletions src/common/client/NetRemoteServerConnection.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <microsoft/net/remote/NetRemoteServerConnection.hxx>
#include <plog/Log.h>

using namespace Microsoft::Net::Remote;

using namespace Microsoft::Net::Remote::Service;
using namespace Microsoft::Net::Remote::Wifi;

NetRemoteServerConnection::NetRemoteServerConnection(std::string_view address, std::shared_ptr<grpc::Channel> channel, std::unique_ptr<NetRemote::Stub> client) :
Address(address),
Channel(channel),
Client(std::move(client))
{
}

/* static */
std::shared_ptr<NetRemoteServerConnection>
NetRemoteServerConnection::TryEstablishConnection(std::string_view address)
{
// Attempt to establish a connection (channel) to the server.
static constexpr auto DontTryToConnect{ false };
auto channel = grpc::CreateChannel(std::data(address), grpc::InsecureChannelCredentials());
if (channel == nullptr || channel->GetState(DontTryToConnect) != GRPC_CHANNEL_IDLE) {
LOGE << "Failed to establish connection channel";
return nullptr;
}

// Attempt to create a new client API stub.
auto client = NetRemote::NewStub(channel);
if (client == nullptr) {
LOGE << "Failed to create NetRemote API stub";
return nullptr;
}

// Return a new connection object.
auto netRemoteServerConnection = std::make_shared<NetRemoteServerConnection>(address, channel, std::move(client));
return netRemoteServerConnection;
}
19 changes: 19 additions & 0 deletions src/common/client/include/microsoft/net/remote/NetRemoteClient.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

#ifndef NET_REMOTE_CLIENT
#define NET_REMOTE_CLIENT

#include <string_view>

namespace Microsoft::Net::Remote
{
/**
* @brief Identifiers for commands supported by the server.
*/
enum class NetRemoteCommandId
{
None,
WifiEnumerateAcessPoints,
};
} // namespace Microsoft::Net::Remote

#endif // NET_REMOTE_CLIENT
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

#ifndef NET_REMOTE_SERVER_CONNECTION
#define NET_REMOTE_SERVER_CONNECTION

#include <memory>
#include <string>
#include <string_view>

#include <grpcpp/channel.h>
#include <microsoft/net/remote/protocol/NetRemoteService.grpc.pb.h>

namespace Microsoft::Net::Remote
{
/**
* @brief Helper structure to collect server connection information.
*/
struct NetRemoteServerConnection
{
std::string Address;
std::shared_ptr<grpc::Channel> Channel;
std::unique_ptr<Microsoft::Net::Remote::Service::NetRemote::Stub> Client;

/**
* @brief Construct a new NetRemoteServerConnection object with the specified address, channel, and client.
*
* @param address The address that was used to connect.
* @param channel The established channel.
* @param client The client stub that was obtained from 'channel'.
*/
NetRemoteServerConnection(std::string_view address, std::shared_ptr<grpc::Channel> channel, std::unique_ptr<Microsoft::Net::Remote::Service::NetRemote::Stub> client);

/**
* @brief Attempt to establish a basic connection to a remote server.
*
* @param address The address to use to connect. Must be in the form <host>:<port>.
* @return std::shared_ptr<NetRemoteServerConnection>
*/
static std::shared_ptr<NetRemoteServerConnection>
TryEstablishConnection(std::string_view address);

NetRemoteServerConnection(const NetRemoteServerConnection& other) = delete;
NetRemoteServerConnection(NetRemoteServerConnection&& other) = default;

NetRemoteServerConnection
operator=(const NetRemoteServerConnection& other) = delete;

NetRemoteServerConnection&
operator=(NetRemoteServerConnection&& other) = default;
};
} // namespace Microsoft::Net::Remote

#endif // NET_REMOTE_SERVER_CONNECTION
2 changes: 2 additions & 0 deletions src/common/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

add_subdirectory(cli)
39 changes: 39 additions & 0 deletions src/common/tools/cli/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

add_library(${PROJECT_NAME}-cli STATIC "")

set(NETREMOTE_CLI_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
set(NETREMOTE_CLI_PUBLIC_INCLUDE_SUFFIX microsoft/net/remote)
set(NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX ${NETREMOTE_CLI_PUBLIC_INCLUDE}/${NETREMOTE_CLI_PUBLIC_INCLUDE_SUFFIX})

target_sources(${PROJECT_NAME}-cli
PRIVATE
NetRemoteCli.cxx
NetRemoteCliHandler.cxx
NetRemoteCliHandlerOperations.cxx
PUBLIC
FILE_SET HEADERS
BASE_DIRS ${NETREMOTE_CLI_PUBLIC_INCLUDE}
FILES
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/INetRemoteCliHandlerOperations.hxx
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCli.hxx
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCliData.hxx
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCliHandler.hxx
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCliHandlerOperations.hxx
)

target_link_libraries(${PROJECT_NAME}-cli
PRIVATE
CLI11::CLI11
magic_enum::magic_enum
notstd
plog::plog
PUBLIC
${PROJECT_NAME}-client
)

install(
TARGETS ${PROJECT_NAME}-cli
EXPORT ${PROJECT_NAME}
FILE_SET HEADERS
PUBLIC_HEADER DESTINATION "${NETREMOTE_DIR_INSTALL_PUBLIC_HEADER_BASE}/${NETREMOTE_CLI_PUBLIC_INCLUDE_SUFFIX}"
)
114 changes: 114 additions & 0 deletions src/common/tools/cli/NetRemoteCli.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@

#include <string>

#include <microsoft/net/remote/NetRemoteCli.hxx>
#include <notstd/Memory.hxx>
#include <plog/Log.h>

using namespace Microsoft::Net::Remote;

NetRemoteCli::NetRemoteCli(std::shared_ptr<NetRemoteCliData> cliData, std::shared_ptr<NetRemoteCliHandler> cliHandler) :
m_cliData{ cliData },
m_cliHandler{ cliHandler },
m_cliApp{ CreateParser() }
{
}

/* static */
std::shared_ptr<NetRemoteCli>
NetRemoteCli::Create(std::shared_ptr<NetRemoteCliData> cliData, std::shared_ptr<NetRemoteCliHandler> cliHandler)
{
auto instance = std::make_shared<notstd::enable_make_protected<NetRemoteCli>>(std::move(cliData), cliHandler);
cliHandler->SetParent(instance->weak_from_this());

return std::move(instance);
}

CLI::App&
NetRemoteCli::GetParser()
{
return *m_cliApp;
}

std::shared_ptr<NetRemoteCliData>
NetRemoteCli::GetData() const
{
return m_cliData;
}

int
NetRemoteCli::Parse(int argc, char* argv[]) noexcept
{
try {
m_cliApp->parse(argc, argv);
} catch (const CLI::ParseError& e) {
return m_cliApp->exit(e);
} catch (...) {
LOGF << "Unhandled exception";
}

return 0;
}

std::unique_ptr<CLI::App>
NetRemoteCli::CreateParser() noexcept
{
auto app = std::make_unique<CLI::App>("A command line tool to interact with netremote servers", "netremote-cli");

app->require_subcommand();

auto optionServer = app->add_option_function<std::string>("-s,--server", [this](const std::string& serverAddress) {
OnServerAddressChanged(serverAddress);
});
optionServer->description("The address of the netremote server to connect to");

m_cliAppServerAddress = optionServer;
m_cliAppWifi = AddSubcommandWifi(app.get());

return app;
}

CLI::App*
NetRemoteCli::AddSubcommandWifi(CLI::App* parent)
{
// Top-level command.
auto* wifiApp = parent->add_subcommand("wifi", "Wi-Fi operations");
wifiApp->needs(m_cliAppServerAddress);

// Sub-commands.
m_cliAppWifiEnumerateAccessPoints = AddSubcommandWifiEnumerateAccessPoints(wifiApp);

return wifiApp;
}

CLI::App*
NetRemoteCli::AddSubcommandWifiEnumerateAccessPoints(CLI::App* parent)
{
auto cliAppWifiEnumerateAccessPoints = parent->add_subcommand("enumerate-access-points", "Enumerate available Wi-Fi access points");
cliAppWifiEnumerateAccessPoints->alias("enumaps");
cliAppWifiEnumerateAccessPoints->callback([this] {
OnWifiEnumerateAccessPoints();
});

return cliAppWifiEnumerateAccessPoints;
}

void
NetRemoteCli::OnServerAddressChanged(const std::string& serverAddress)
{
m_cliData->ServerAddress = serverAddress;

auto connection = NetRemoteServerConnection::TryEstablishConnection(serverAddress);
if (connection == nullptr) {
LOGE << "Failed to create server connection";
return;
}

m_cliHandler->SetConnection(std::move(connection));
}

void
NetRemoteCli::OnWifiEnumerateAccessPoints()
{
m_cliHandler->HandleCommandWifiEnumerateAccessPoints();
}
Loading

0 comments on commit 2e96a67

Please sign in to comment.