diff --git a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp index be8e4c001..a93f6a353 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp @@ -15,6 +15,7 @@ */ #include #include "htc_htc_service_object.hpp" +#include "../../htcfs/htcfs_working_directory.hpp" namespace ams::htc::server { @@ -29,75 +30,125 @@ namespace ams::htc::server { } Result HtcServiceObject::GetEnvironmentVariable(sf::Out out_size, const sf::OutBuffer &out, const sf::InBuffer &name) { - AMS_ABORT("HtcServiceObject::GetEnvironmentVariable"); + /* Get the variable. */ + size_t var_size; + R_TRY(m_misc_impl.GetEnvironmentVariable(std::addressof(var_size), reinterpret_cast(out.GetPointer()), out.GetSize(), reinterpret_cast(name.GetPointer()), name.GetSize())); + + /* Check the output size. */ + R_UNLESS(util::IsIntValueRepresentable(var_size), htc::ResultUnknown()); + + /* Set the output size. */ + *out_size = static_cast(var_size); + return ResultSuccess(); } Result HtcServiceObject::GetEnvironmentVariableLength(sf::Out out_size, const sf::InBuffer &name) { - AMS_ABORT("HtcServiceObject::GetEnvironmentVariableLength"); + /* Get the variable. */ + size_t var_size; + R_TRY(m_misc_impl.GetEnvironmentVariableLength(std::addressof(var_size), reinterpret_cast(name.GetPointer()), name.GetSize())); + + /* Check the output size. */ + R_UNLESS(util::IsIntValueRepresentable(var_size), htc::ResultUnknown()); + + /* Set the output size. */ + *out_size = static_cast(var_size); + return ResultSuccess(); } Result HtcServiceObject::GetHostConnectionEvent(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostConnectionEvent"); + /* Set the output handle. */ + *out = m_observer.GetConnectEvent()->GetReadableHandle(); + return ResultSuccess(); } Result HtcServiceObject::GetHostDisconnectionEvent(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostDisconnectionEvent"); + /* Set the output handle. */ + *out = m_observer.GetDisconnectEvent()->GetReadableHandle(); + return ResultSuccess(); } Result HtcServiceObject::GetHostConnectionEventForSystem(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostConnectionEventForSystem"); + /* NOTE: Nintendo presumably reserved this command in case they need it, but they haven't implemented it yet. */ + AMS_ABORT("HostEventForSystem not implemented."); } Result HtcServiceObject::GetHostDisconnectionEventForSystem(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostDisconnectionEventForSystem"); - } - - Result HtcServiceObject::GetBridgeIpAddress(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgeIpAddress"); - } - - Result HtcServiceObject::GetBridgePort(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgePort"); - } - - Result HtcServiceObject::SetCradleAttached(bool attached) { - AMS_ABORT("HtcServiceObject::SetCradleAttached"); - } - - Result HtcServiceObject::GetBridgeSubnetMask(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgeSubnetMask"); - } - - Result HtcServiceObject::GetBridgeMacAddress(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgeMacAddress"); + /* NOTE: Nintendo presumably reserved this command in case they need it, but they haven't implemented it yet. */ + AMS_ABORT("HostEventForSystem not implemented."); } Result HtcServiceObject::GetWorkingDirectoryPath(const sf::OutBuffer &out, s32 max_len) { - AMS_ABORT("HtcServiceObject::GetWorkingDirectoryPath"); + return htcfs::GetWorkingDirectory(reinterpret_cast(out.GetPointer()), max_len); } Result HtcServiceObject::GetWorkingDirectoryPathSize(sf::Out out_size) { - AMS_ABORT("HtcServiceObject::GetWorkingDirectoryPathSize"); + return htcfs::GetWorkingDirectorySize(out_size.GetPointer()); } Result HtcServiceObject::RunOnHostStart(sf::Out out_id, sf::OutCopyHandle out, const sf::InBuffer &args) { - AMS_ABORT("HtcServiceObject::RunOnHostStart"); + /* Begin the run on host task. */ + R_TRY(m_misc_impl.RunOnHostBegin(out_id.GetPointer(), out.GetHandlePointer(), reinterpret_cast(args.GetPointer()), args.GetSize())); + + /* Add the task id to our set. */ + { + std::scoped_lock lk(m_mutex); + m_set.insert(*out_id); + } + + /* Mark the output event as managed. */ + out.SetManaged(true); + return ResultSuccess(); } Result HtcServiceObject::RunOnHostResults(sf::Out out_result, u32 id) { - AMS_ABORT("HtcServiceObject::RunOnHostResults"); + /* Verify that we have the task. */ + { + std::scoped_lock lk(m_mutex); + R_UNLESS(m_set.erase(id), htc::ResultInvalidTaskId()); + } + + /* Finish the run on host task. */ + return m_misc_impl.RunOnHostEnd(out_result.GetPointer(), id); + } + + Result HtcServiceObject::GetBridgeIpAddress(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::GetBridgePort(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::SetCradleAttached(bool attached) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::GetBridgeSubnetMask(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::GetBridgeMacAddress(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } Result HtcServiceObject::SetBridgeIpAddress(const sf::InBuffer &arg) { - AMS_ABORT("HtcServiceObject::SetBridgeIpAddress"); + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } Result HtcServiceObject::SetBridgeSubnetMask(const sf::InBuffer &arg) { - AMS_ABORT("HtcServiceObject::SetBridgeSubnetMask"); + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } Result HtcServiceObject::SetBridgePort(const sf::InBuffer &arg) { - AMS_ABORT("HtcServiceObject::SetBridgePort"); + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } } diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index 3ed5d71e7..88728eea4 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -76,6 +76,61 @@ namespace ams::htc::server { m_cancel_event.Signal(); } + void HtcmiscImpl::WaitTask(u32 task_id) { + return m_rpc_client.Wait(task_id); + } + + Result HtcmiscImpl::GetEnvironmentVariable(size_t *out_size, char *dst, size_t dst_size, const char *name, size_t name_size) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client.Begin(std::addressof(task_id), name, name_size)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + R_TRY(m_rpc_client.End(task_id, out_size, dst, dst_size)); + + return ResultSuccess(); + } + + Result HtcmiscImpl::GetEnvironmentVariableLength(size_t *out_size, const char *name, size_t name_size) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client.Begin(std::addressof(task_id), name, name_size)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + R_TRY(m_rpc_client.End(task_id, out_size)); + + return ResultSuccess(); + } + + Result HtcmiscImpl::RunOnHostBegin(u32 *out_task_id, Handle *out_event, const char *args, size_t args_size) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client.Begin(std::addressof(task_id), args, args_size)); + + /* Detach the task. */ + *out_task_id = task_id; + *out_event = m_rpc_client.DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcmiscImpl::RunOnHostEnd(s32 *out_result, u32 task_id) { + /* Finish the task. */ + s32 res; + R_TRY(m_rpc_client.End(task_id, std::addressof(res))); + + /* Set output. */ + *out_result = res; + + return ResultSuccess(); + } + void HtcmiscImpl::ClientThread() { /* Loop so long as we're not cancelled. */ while (!m_cancelled) { diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp index 3317bda4c..d3e9b4e68 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -55,9 +55,16 @@ namespace ams::htc::server { void SetServerConnectionEvent(bool en); void UpdateConnectionEvent(); + + void WaitTask(u32 task_id); public: void Cancel(); - /* TODO */ + + Result GetEnvironmentVariable(size_t *out_size, char *dst, size_t dst_size, const char *name, size_t name_size); + Result GetEnvironmentVariableLength(size_t *out_size, const char *name, size_t name_size); + + Result RunOnHostBegin(u32 *out_task_id, Handle *out_event, const char *args, size_t args_size); + Result RunOnHostEnd(s32 *out_result, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.hpp b/libraries/libstratosphere/source/htc/server/htc_observer.hpp index ea9bf2d2d..ecd78e295 100644 --- a/libraries/libstratosphere/source/htc/server/htc_observer.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_observer.hpp @@ -41,6 +41,9 @@ namespace ams::htc::server { Result Start(); void UpdateEvent(); + public: + os::SystemEvent *GetConnectEvent() { return std::addressof(m_connect_event); } + os::SystemEvent *GetDisconnectEvent() { return std::addressof(m_disconnect_event); } }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp index 95972a110..fd67ffd9d 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp @@ -61,7 +61,7 @@ namespace ams::htc::server::rpc { Task::Complete(); } - Result GetEnvironmentVariableTask::GetResult(size_t *out, char *dst, size_t size) { + Result GetEnvironmentVariableTask::GetResult(size_t *out, char *dst, size_t size) const { /* Check our task state. */ AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); @@ -166,7 +166,7 @@ namespace ams::htc::server::rpc { Task::Complete(); } - Result GetEnvironmentVariableLengthTask::GetResult(size_t *out) { + Result GetEnvironmentVariableLengthTask::GetResult(size_t *out) const { /* Check our task state. */ AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); @@ -241,7 +241,7 @@ namespace ams::htc::server::rpc { Task::Complete(); } - Result RunOnHostTask::GetResult(int *out) { + Result RunOnHostTask::GetResult(int *out) const { *out = m_host_result; return ResultSuccess(); } @@ -290,5 +290,4 @@ namespace ams::htc::server::rpc { return m_system_event.GetBase(); } - } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp index 20268570f..588d5b9a0 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp @@ -88,7 +88,7 @@ namespace ams::htc::server::rpc { Result SetArguments(const char *args, size_t size); void Complete(HtcmiscResult result, const char *data, size_t size); - Result GetResult(size_t *out, char *dst, size_t size); + Result GetResult(size_t *out, char *dst, size_t size) const; const char *GetName() const { return m_name; } int GetNameSize() const { return m_name_size; } @@ -110,7 +110,7 @@ namespace ams::htc::server::rpc { Result SetArguments(const char *args, size_t size); void Complete(HtcmiscResult result, const char *data, size_t size); - Result GetResult(size_t *out); + Result GetResult(size_t *out) const; const char *GetName() const { return m_name; } int GetNameSize() const { return m_name_size; } @@ -133,7 +133,7 @@ namespace ams::htc::server::rpc { Result SetArguments(const char *args, size_t size); void Complete(int host_result); - Result GetResult(int *out); + Result GetResult(int *out) const; const char *GetCommand() const { return m_command; } int GetCommandSize() const { return m_command_size; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 716d025c9..34165f541 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -60,6 +60,9 @@ namespace ams::htcfs { Result FlushFile(s32 handle) { return ConvertToFsResult(m_impl.FlushFile(handle)); } Result GetPriorityForFile(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForFile(out, handle)); } Result SetPriorityForFile(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForFile(priority, handle)); } + + Result GetWorkingDirectory(char *dst, size_t dst_size) { return ConvertToFsResult(m_impl.GetWorkingDirectory(dst, dst_size)); } + Result GetWorkingDirectorySize(s32 *out) { return ConvertToFsResult(m_impl.GetWorkingDirectorySize(out)); } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index fbeacf55c..afed4f5c5 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -1539,5 +1539,83 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::GetWorkingDirectory(char *dst, size_t dst_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetWorkingDirectoryHeader(std::addressof(request)); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check that the body size is valid. */ + R_UNLESS(response.body_size < static_cast(dst_size), htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the response body. */ + R_TRY(this->ReceiveFromRpcChannel(dst, response.body_size)); + + /* Null-terminate the response body. */ + dst[response.body_size] = '\x00'; + + return ResultSuccess(); + } + + Result ClientImpl::GetWorkingDirectorySize(s32 *out) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetWorkingDirectorySizeHeader(std::addressof(request)); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check that the size is representable. */ + R_UNLESS(util::IsIntValueRepresentable(response.params[1]), htcfs::ResultInvalidSize()); + + /* Set the output size. */ + *out = static_cast(response.params[1]); + + return ResultSuccess(); + } } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index 219f289a1..ebe9fb428 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -90,6 +90,9 @@ namespace ams::htcfs { Result FlushFile(s32 handle); Result GetPriorityForFile(s32 *out, s32 handle); Result SetPriorityForFile(s32 priority, s32 handle); + + Result GetWorkingDirectory(char *dst, size_t dst_size); + Result GetWorkingDirectorySize(s32 *out); private: int WaitAny(htclow::ChannelState state, os::EventType *event); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index b998c47eb..636f9553d 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -179,6 +179,14 @@ namespace ams::htcfs { return this->MakeRequestHeader(out, PacketType::GetEntryCount, 0, handle); } + void MakeGetWorkingDirectoryHeader(Header *out) { + return this->MakeRequestHeader(out, PacketType::GetWorkingDirectory, 0); + } + + void MakeGetWorkingDirectorySizeHeader(Header *out) { + return this->MakeRequestHeader(out, PacketType::GetWorkingDirectorySize, 0); + } + void MakeReadDirectoryHeader(Header *out, s32 handle, size_t max_out_entries) { return this->MakeRequestHeader(out, PacketType::ReadDirectory, 0, handle, max_out_entries); } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp new file mode 100644 index 000000000..22053d917 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_client.hpp" + +namespace ams::htcfs { + + Result GetWorkingDirectory(char *dst, size_t dst_size) { + return htcfs::GetClient().GetWorkingDirectory(dst, dst_size); + } + + Result GetWorkingDirectorySize(s32 *out) { + return htcfs::GetClient().GetWorkingDirectorySize(out); + } + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp new file mode 100644 index 000000000..53807573d --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + Result GetWorkingDirectory(char *dst, size_t dst_size); + Result GetWorkingDirectorySize(s32 *out); + +} diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp index fc35e68f9..68ca930f5 100644 --- a/libraries/libvapours/include/vapours/results/htcfs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -35,6 +35,7 @@ namespace ams::htcfs { R_DEFINE_ERROR_RESULT(UnexpectedResponseBody, 116); R_DEFINE_ERROR_RANGE(InternalError, 200, 299); + R_DEFINE_ERROR_RESULT(InvalidSize, 201); R_DEFINE_ERROR_RESULT(UnknownError, 211); R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); R_DEFINE_ERROR_RESULT(InvalidRequest, 213);