From 6c2f005a621765d42d4b4eb9f225fb073ffbebb1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Mar 2021 17:39:49 -0700 Subject: [PATCH] scs: implement DoShellServer --- .../include/stratosphere/scs/scs_shell.hpp | 2 +- .../libstratosphere/source/scs/scs_shell.cpp | 57 ++++++++- .../source/scs/scs_shell_server.cpp | 116 +++++++++++++++++- 3 files changed, 169 insertions(+), 6 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp index 9ba1caf22..07036c781 100644 --- a/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp @@ -26,7 +26,7 @@ namespace ams::scs { void RegisterCommonProcessEventHandler(ProcessEventHandler on_start, ProcessEventHandler on_exit, ProcessEventHandler on_jit_debug); - bool RegisterSocket(s32 socket); + Result RegisterSocket(s32 socket, u64 id); void UnregisterSocket(s32 socket); Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags); diff --git a/libraries/libstratosphere/source/scs/scs_shell.cpp b/libraries/libstratosphere/source/scs/scs_shell.cpp index 0f511064f..905491b6a 100644 --- a/libraries/libstratosphere/source/scs/scs_shell.cpp +++ b/libraries/libstratosphere/source/scs/scs_shell.cpp @@ -39,7 +39,7 @@ namespace ams::scs { class SocketInfoManager { private: - SocketInfo m_infos[MaxProgramInfo]; + SocketInfo m_infos[MaxSocketInfo]; int m_count; public: constexpr SocketInfoManager() = default; @@ -48,6 +48,42 @@ namespace ams::scs { /* Clear our count. */ m_count = 0; } + + Result Register(s32 socket, u64 id) { + /* Check that the socket isn't already registered. */ + for (auto i = 0; i < m_count; ++i) { + R_UNLESS(m_infos[i].socket != socket, scs::ResultNoSocket()); + } + + /* Check that we can allocate a new socket info. */ + if (m_count >= MaxSocketInfo) { + /* NOTE: Nintendo aborts with this result here. */ + R_ABORT_UNLESS(scs::ResultOutOfResource()); + } + + /* Create the new socket info. */ + m_infos[m_count++] = { + .id = id, + .socket = socket, + }; + + return ResultSuccess(); + } + + void Unregister(s32 socket) { + /* Unregister the socket, if it's registered. */ + for (auto i = 0; i < m_count; ++i) { + if (m_infos[i].socket == socket) { + /* Ensure that the valid socket infos remain in bounds. */ + std::memcpy(m_infos + i, m_infos + i + 1, (m_count - (i + 1)) * sizeof(*m_infos)); + + /* Note that we now have one fewer socket info. */ + --m_count; + + break; + } + } + } }; class ProgramInfoManager { @@ -77,6 +113,8 @@ namespace ams::scs { constinit SocketInfoManager g_socket_info_manager; constinit ProgramInfoManager g_program_info_manager; + constinit os::SdkMutex g_manager_mutex; + void EventHandlerThread(void *) { /* TODO */ AMS_ABORT("scs::EventHandlerThread"); @@ -130,8 +168,21 @@ namespace ams::scs { g_common_jit_debug_handler = on_jit_debug; } - bool RegisterSocket(s32 socket); - void UnregisterSocket(s32 socket); + Result RegisterSocket(s32 socket, u64 id) { + /* Acquire exclusive access to the socket info manager. */ + std::scoped_lock lk(g_manager_mutex); + + /* Register the socket. */ + return g_socket_info_manager.Register(socket, id); + } + + void UnregisterSocket(s32 socket) { + /* Acquire exclusive access to the socket info manager. */ + std::scoped_lock lk(g_manager_mutex); + + /* Unregister the socket. */ + return g_socket_info_manager.Unregister(socket); + } Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags) { /* Set up the arguments. */ diff --git a/libraries/libstratosphere/source/scs/scs_shell_server.cpp b/libraries/libstratosphere/source/scs/scs_shell_server.cpp index 286fa31d9..e54c201b1 100644 --- a/libraries/libstratosphere/source/scs/scs_shell_server.cpp +++ b/libraries/libstratosphere/source/scs/scs_shell_server.cpp @@ -17,6 +17,73 @@ namespace ams::scs { + namespace { + + s32 CreateSocket() { + while (true) { + /* Try to create a socket. */ + if (const auto desc = htcs::Socket(); desc >= 0) { + return desc; + } + + /* Wait 100ms before trying again. */ + os::SleepThread(TimeSpan::FromMilliSeconds(100)); + } + } + + s32 AcceptSocket(s32 listen_socket) { + htcs::SockAddrHtcs temp; + return htcs::Accept(listen_socket, std::addressof(temp)); + } + + s32 Bind(s32 socket, const htcs::HtcsPortName &port_name) { + /* Set up the bind address. */ + htcs::SockAddrHtcs addr; + addr.family = htcs::HTCS_AF_HTCS; + addr.peer_name = htcs::GetPeerNameAny(); + std::strcpy(addr.port_name.name, port_name.name); + + /* Bind. */ + return htcs::Bind(socket, std::addressof(addr)); + } + + htcs::ssize_t Receive(s32 socket, void *buffer, size_t size) { + u8 *dst = static_cast(buffer); + size_t received = 0; + + while (received < size) { + const auto ret = htcs::Recv(socket, dst + received, size - received, 0); + if (ret <= 0) { + return ret; + } + + received += ret; + } + + return static_cast(received); + } + + bool ReceiveCommand(CommandHeader *header, void *buffer, size_t buffer_size, s32 socket) { + /* Receive the header. */ + if (Receive(socket, header, sizeof(*header)) != sizeof(*header)) { + return false; + } + + /* Check that the body will fit in the buffer. */ + if (header->body_size >= buffer_size) { + return false; + } + + /* Receive the body. */ + if(Receive(socket, buffer, header->body_size) != static_cast(header->body_size)) { + return false; + } + + return true; + } + + } + void ShellServer::Initialize(const char *port_name, void *stack, size_t stack_size, CommandProcessor *command_processor) { /* Set our variables. */ m_command_processor = command_processor; @@ -34,8 +101,53 @@ namespace ams::scs { } void ShellServer::DoShellServer() { - /* TODO */ - AMS_ABORT("ShellServer::DoShellServer"); + /* Loop servicing the shell server. */ + while (true) { + /* Create a socket to listen on. */ + const auto listen_socket = CreateSocket(); + ON_SCOPE_EXIT { htcs::Close(listen_socket); }; + + /* Bind to the listen socket. */ + if (Bind(listen_socket, m_port_name) != 0) { + continue; + } + + /* Loop processing on our bound socket. */ + while (true) { + /* Listen on the socket. */ + if (const s32 listen_result = htcs::Listen(listen_socket, 0); listen_result != 0) { + /* TODO: logging. */ + break; + } + + /* Accept a socket. */ + const s32 socket = AcceptSocket(listen_socket); + if (socket <= 0) { + /* TODO: logging. */ + continue; + } + + /* Ensure that the socket is cleaned up when we're done with it. */ + ON_SCOPE_EXIT { + UnregisterSocket(socket); + htcs::Close(socket); + }; + + /* Loop servicing the socket. */ + while (true) { + /* Receive a command header. */ + CommandHeader header; + if (!ReceiveCommand(std::addressof(header), m_buffer, sizeof(m_buffer), socket)) { + break; + } + + /* Process the command. */ + if (!m_command_processor->ProcessCommand(header, m_buffer, socket)) { + break; + } + } + } + } } }