Skip to content

Commit 2e96a67

Browse files
authored
Merge pull request #110 from microsoft/clientcli
Add client cli tool
2 parents 7e5f414 + 7f9703d commit 2e96a67

21 files changed

+798
-6
lines changed

.vscode/extensions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"davidanson.vscode-markdownlint",
99
"ms-vscode-remote.vscode-remote-extensionpack",
1010
"coolbear.systemd-unit-file",
11-
"redhat.vscode-yaml"
11+
"redhat.vscode-yaml",
12+
"vadimcn.vscode-lldb"
1213
]
1314
}

protocol/CMakeLists.txt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11

2+
set(NETREMOTE_PROTOCOL_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
3+
set(NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_SUFFIX microsoft/net/remote/protocol)
4+
set(NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_PREFIX ${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE}/${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_SUFFIX})
5+
26
set(PROTO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protos)
3-
set(PROTO_DIR_BINARY_SUFFIX microsoft/net/remote/protocol)
4-
set(PROTO_DIR_BINARY ${CMAKE_CURRENT_BINARY_DIR}/${PROTO_DIR_BINARY_SUFFIX})
7+
set(PROTO_DIR_BINARY ${CMAKE_CURRENT_BINARY_DIR}/${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_SUFFIX})
58
set(PROTO_FILES
69
${PROTO_DIR}/NetRemote.proto
710
${PROTO_DIR}/NetRemoteService.proto
@@ -13,7 +16,7 @@ set(PROTO_FILES
1316
# we've encountered intermittent errors when building with external vcpkg (eg.
1417
# via port) which results in empty file lists and thus, the generated headers
1518
# are not installed. It appears to be some sort of race condition in CMake
16-
# itself
19+
# itself.
1720
#
1821
# file(GLOB PROTO_FILES_GENERATED "${PROTO_DIR_BINARY}/*.pb.h")
1922
# string(REGEX REPLACE "[.]proto$" ".pb.h" PROTO_HEADERS_GENERATED "${PROTO_FILES_GENERATED}")
@@ -40,10 +43,13 @@ target_sources(${PROJECT_NAME}-protocol
4043
${PROTO_FILES}
4144
PUBLIC
4245
FILE_SET HEADERS
43-
BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}
46+
BASE_DIRS
47+
${CMAKE_CURRENT_BINARY_DIR}
48+
${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE}
4449
FILES
4550
${PROTO_HEADERS_GENERATED}
4651
${PROTO_HEADERS_GENERATED_GRPC}
52+
${NETREMOTE_PROTOCOL_PUBLIC_INCLUDE_PREFIX}/NetRemoteProtocol.hxx
4753
)
4854

4955
target_link_libraries(${PROJECT_NAME}-protocol
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
#ifndef NET_REMOTE_PROTOCOL_HXX
3+
#define NET_REMOTE_PROTOCOL_HXX
4+
5+
#include <cstdint>
6+
#include <ranges>
7+
#include <string_view>
8+
9+
namespace Microsoft::Net::Remote::Protocol
10+
{
11+
static constexpr uint32_t NetRemotePortDefault = 5047;
12+
static constexpr std::string_view NetRemoteAddressHttpDefault = "localhost:5047";
13+
14+
/**
15+
* @brief Static NetRemote protocol information.
16+
*/
17+
struct NetRemoteProtocol
18+
{
19+
#define IP_DEFAULT "localhost"
20+
#define PORT_DEFAULT 5047
21+
#define xstr(s) str(s)
22+
#define str(s) #s
23+
24+
static constexpr uint32_t PortDefault{ 5047 };
25+
static constexpr std::string_view IpDefault{ "localhost" };
26+
static constexpr std::string_view AddressDefault{ IP_DEFAULT ":" xstr(PORT_DEFAULT) };
27+
28+
#undef IP_DEFAULT
29+
#undef PORT_DEFAULT
30+
#undef xstr
31+
#undef str
32+
};
33+
34+
} // namespace Microsoft::Net::Remote::Protocol
35+
36+
#endif // NET_REMOTE_PROTOCOL_HXX

src/common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ add_subdirectory(dotnet)
44
add_subdirectory(server)
55
add_subdirectory(service)
66
add_subdirectory(shared)
7+
add_subdirectory(tools)
78
add_subdirectory(wifi)

src/common/client/CMakeLists.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11

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

4+
set(NETREMOTE_CLIENT_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
5+
set(NETREMOTE_CLIENT_PUBLIC_INCLUDE_SUFFIX microsoft/net/remote)
6+
set(NETREMOTE_CLIENT_PUBLIC_INCLUDE_PREFIX ${NETREMOTE_CLIENT_PUBLIC_INCLUDE}/${NETREMOTE_CLIENT_PUBLIC_INCLUDE_SUFFIX})
7+
48
target_sources(${PROJECT_NAME}-client
59
PRIVATE
6-
${CMAKE_CURRENT_LIST_DIR}/Placeholder.cxx
10+
NetRemoteServerConnection.cxx
11+
PUBLIC
12+
FILE_SET HEADERS
13+
BASE_DIRS ${NETREMOTE_CLIENT_PUBLIC_INCLUDE}
14+
FILES
15+
${NETREMOTE_CLIENT_PUBLIC_INCLUDE_PREFIX}/NetRemoteServerConnection.hxx
716
)
817

918
target_link_libraries(${PROJECT_NAME}-client
19+
PRIVATE
20+
plog::plog
1021
PUBLIC
1122
${PROJECT_NAME}-protocol
1223
)
24+
25+
install(
26+
TARGETS ${PROJECT_NAME}-client
27+
EXPORT ${PROJECT_NAME}
28+
FILE_SET HEADERS
29+
PUBLIC_HEADER DESTINATION "${NETREMOTE_DIR_INSTALL_PUBLIC_HEADER_BASE}/${NETREMOTE_CLIENT_PUBLIC_INCLUDE_SUFFIX}"
30+
)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
#include <grpcpp/client_context.h>
3+
#include <grpcpp/create_channel.h>
4+
#include <grpcpp/security/credentials.h>
5+
#include <microsoft/net/remote/NetRemoteServerConnection.hxx>
6+
#include <plog/Log.h>
7+
8+
using namespace Microsoft::Net::Remote;
9+
10+
using namespace Microsoft::Net::Remote::Service;
11+
using namespace Microsoft::Net::Remote::Wifi;
12+
13+
NetRemoteServerConnection::NetRemoteServerConnection(std::string_view address, std::shared_ptr<grpc::Channel> channel, std::unique_ptr<NetRemote::Stub> client) :
14+
Address(address),
15+
Channel(channel),
16+
Client(std::move(client))
17+
{
18+
}
19+
20+
/* static */
21+
std::shared_ptr<NetRemoteServerConnection>
22+
NetRemoteServerConnection::TryEstablishConnection(std::string_view address)
23+
{
24+
// Attempt to establish a connection (channel) to the server.
25+
static constexpr auto DontTryToConnect{ false };
26+
auto channel = grpc::CreateChannel(std::data(address), grpc::InsecureChannelCredentials());
27+
if (channel == nullptr || channel->GetState(DontTryToConnect) != GRPC_CHANNEL_IDLE) {
28+
LOGE << "Failed to establish connection channel";
29+
return nullptr;
30+
}
31+
32+
// Attempt to create a new client API stub.
33+
auto client = NetRemote::NewStub(channel);
34+
if (client == nullptr) {
35+
LOGE << "Failed to create NetRemote API stub";
36+
return nullptr;
37+
}
38+
39+
// Return a new connection object.
40+
auto netRemoteServerConnection = std::make_shared<NetRemoteServerConnection>(address, channel, std::move(client));
41+
return netRemoteServerConnection;
42+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
#ifndef NET_REMOTE_CLIENT
3+
#define NET_REMOTE_CLIENT
4+
5+
#include <string_view>
6+
7+
namespace Microsoft::Net::Remote
8+
{
9+
/**
10+
* @brief Identifiers for commands supported by the server.
11+
*/
12+
enum class NetRemoteCommandId
13+
{
14+
None,
15+
WifiEnumerateAcessPoints,
16+
};
17+
} // namespace Microsoft::Net::Remote
18+
19+
#endif // NET_REMOTE_CLIENT
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
#ifndef NET_REMOTE_SERVER_CONNECTION
3+
#define NET_REMOTE_SERVER_CONNECTION
4+
5+
#include <memory>
6+
#include <string>
7+
#include <string_view>
8+
9+
#include <grpcpp/channel.h>
10+
#include <microsoft/net/remote/protocol/NetRemoteService.grpc.pb.h>
11+
12+
namespace Microsoft::Net::Remote
13+
{
14+
/**
15+
* @brief Helper structure to collect server connection information.
16+
*/
17+
struct NetRemoteServerConnection
18+
{
19+
std::string Address;
20+
std::shared_ptr<grpc::Channel> Channel;
21+
std::unique_ptr<Microsoft::Net::Remote::Service::NetRemote::Stub> Client;
22+
23+
/**
24+
* @brief Construct a new NetRemoteServerConnection object with the specified address, channel, and client.
25+
*
26+
* @param address The address that was used to connect.
27+
* @param channel The established channel.
28+
* @param client The client stub that was obtained from 'channel'.
29+
*/
30+
NetRemoteServerConnection(std::string_view address, std::shared_ptr<grpc::Channel> channel, std::unique_ptr<Microsoft::Net::Remote::Service::NetRemote::Stub> client);
31+
32+
/**
33+
* @brief Attempt to establish a basic connection to a remote server.
34+
*
35+
* @param address The address to use to connect. Must be in the form <host>:<port>.
36+
* @return std::shared_ptr<NetRemoteServerConnection>
37+
*/
38+
static std::shared_ptr<NetRemoteServerConnection>
39+
TryEstablishConnection(std::string_view address);
40+
41+
NetRemoteServerConnection(const NetRemoteServerConnection& other) = delete;
42+
NetRemoteServerConnection(NetRemoteServerConnection&& other) = default;
43+
44+
NetRemoteServerConnection
45+
operator=(const NetRemoteServerConnection& other) = delete;
46+
47+
NetRemoteServerConnection&
48+
operator=(NetRemoteServerConnection&& other) = default;
49+
};
50+
} // namespace Microsoft::Net::Remote
51+
52+
#endif // NET_REMOTE_SERVER_CONNECTION

src/common/tools/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
add_subdirectory(cli)

src/common/tools/cli/CMakeLists.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
add_library(${PROJECT_NAME}-cli STATIC "")
3+
4+
set(NETREMOTE_CLI_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
5+
set(NETREMOTE_CLI_PUBLIC_INCLUDE_SUFFIX microsoft/net/remote)
6+
set(NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX ${NETREMOTE_CLI_PUBLIC_INCLUDE}/${NETREMOTE_CLI_PUBLIC_INCLUDE_SUFFIX})
7+
8+
target_sources(${PROJECT_NAME}-cli
9+
PRIVATE
10+
NetRemoteCli.cxx
11+
NetRemoteCliHandler.cxx
12+
NetRemoteCliHandlerOperations.cxx
13+
PUBLIC
14+
FILE_SET HEADERS
15+
BASE_DIRS ${NETREMOTE_CLI_PUBLIC_INCLUDE}
16+
FILES
17+
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/INetRemoteCliHandlerOperations.hxx
18+
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCli.hxx
19+
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCliData.hxx
20+
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCliHandler.hxx
21+
${NETREMOTE_CLI_PUBLIC_INCLUDE_PREFIX}/NetRemoteCliHandlerOperations.hxx
22+
)
23+
24+
target_link_libraries(${PROJECT_NAME}-cli
25+
PRIVATE
26+
CLI11::CLI11
27+
magic_enum::magic_enum
28+
notstd
29+
plog::plog
30+
PUBLIC
31+
${PROJECT_NAME}-client
32+
)
33+
34+
install(
35+
TARGETS ${PROJECT_NAME}-cli
36+
EXPORT ${PROJECT_NAME}
37+
FILE_SET HEADERS
38+
PUBLIC_HEADER DESTINATION "${NETREMOTE_DIR_INSTALL_PUBLIC_HEADER_BASE}/${NETREMOTE_CLI_PUBLIC_INCLUDE_SUFFIX}"
39+
)

src/common/tools/cli/NetRemoteCli.cxx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
#include <string>
3+
4+
#include <microsoft/net/remote/NetRemoteCli.hxx>
5+
#include <notstd/Memory.hxx>
6+
#include <plog/Log.h>
7+
8+
using namespace Microsoft::Net::Remote;
9+
10+
NetRemoteCli::NetRemoteCli(std::shared_ptr<NetRemoteCliData> cliData, std::shared_ptr<NetRemoteCliHandler> cliHandler) :
11+
m_cliData{ cliData },
12+
m_cliHandler{ cliHandler },
13+
m_cliApp{ CreateParser() }
14+
{
15+
}
16+
17+
/* static */
18+
std::shared_ptr<NetRemoteCli>
19+
NetRemoteCli::Create(std::shared_ptr<NetRemoteCliData> cliData, std::shared_ptr<NetRemoteCliHandler> cliHandler)
20+
{
21+
auto instance = std::make_shared<notstd::enable_make_protected<NetRemoteCli>>(std::move(cliData), cliHandler);
22+
cliHandler->SetParent(instance->weak_from_this());
23+
24+
return std::move(instance);
25+
}
26+
27+
CLI::App&
28+
NetRemoteCli::GetParser()
29+
{
30+
return *m_cliApp;
31+
}
32+
33+
std::shared_ptr<NetRemoteCliData>
34+
NetRemoteCli::GetData() const
35+
{
36+
return m_cliData;
37+
}
38+
39+
int
40+
NetRemoteCli::Parse(int argc, char* argv[]) noexcept
41+
{
42+
try {
43+
m_cliApp->parse(argc, argv);
44+
} catch (const CLI::ParseError& e) {
45+
return m_cliApp->exit(e);
46+
} catch (...) {
47+
LOGF << "Unhandled exception";
48+
}
49+
50+
return 0;
51+
}
52+
53+
std::unique_ptr<CLI::App>
54+
NetRemoteCli::CreateParser() noexcept
55+
{
56+
auto app = std::make_unique<CLI::App>("A command line tool to interact with netremote servers", "netremote-cli");
57+
58+
app->require_subcommand();
59+
60+
auto optionServer = app->add_option_function<std::string>("-s,--server", [this](const std::string& serverAddress) {
61+
OnServerAddressChanged(serverAddress);
62+
});
63+
optionServer->description("The address of the netremote server to connect to");
64+
65+
m_cliAppServerAddress = optionServer;
66+
m_cliAppWifi = AddSubcommandWifi(app.get());
67+
68+
return app;
69+
}
70+
71+
CLI::App*
72+
NetRemoteCli::AddSubcommandWifi(CLI::App* parent)
73+
{
74+
// Top-level command.
75+
auto* wifiApp = parent->add_subcommand("wifi", "Wi-Fi operations");
76+
wifiApp->needs(m_cliAppServerAddress);
77+
78+
// Sub-commands.
79+
m_cliAppWifiEnumerateAccessPoints = AddSubcommandWifiEnumerateAccessPoints(wifiApp);
80+
81+
return wifiApp;
82+
}
83+
84+
CLI::App*
85+
NetRemoteCli::AddSubcommandWifiEnumerateAccessPoints(CLI::App* parent)
86+
{
87+
auto cliAppWifiEnumerateAccessPoints = parent->add_subcommand("enumerate-access-points", "Enumerate available Wi-Fi access points");
88+
cliAppWifiEnumerateAccessPoints->alias("enumaps");
89+
cliAppWifiEnumerateAccessPoints->callback([this] {
90+
OnWifiEnumerateAccessPoints();
91+
});
92+
93+
return cliAppWifiEnumerateAccessPoints;
94+
}
95+
96+
void
97+
NetRemoteCli::OnServerAddressChanged(const std::string& serverAddress)
98+
{
99+
m_cliData->ServerAddress = serverAddress;
100+
101+
auto connection = NetRemoteServerConnection::TryEstablishConnection(serverAddress);
102+
if (connection == nullptr) {
103+
LOGE << "Failed to create server connection";
104+
return;
105+
}
106+
107+
m_cliHandler->SetConnection(std::move(connection));
108+
}
109+
110+
void
111+
NetRemoteCli::OnWifiEnumerateAccessPoints()
112+
{
113+
m_cliHandler->HandleCommandWifiEnumerateAccessPoints();
114+
}

0 commit comments

Comments
 (0)