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 new file mode 100644 index 000000000..95972a110 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp @@ -0,0 +1,294 @@ +/* + * 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 "htc_htcmisc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + Result GetEnvironmentVariableTask::SetArguments(const char *args, size_t size) { + /* Copy to our name. */ + const size_t copied = util::Strlcpy(m_name, args, sizeof(m_name)); + m_name_size = copied; + + /* Require that the size be correct. */ + + R_UNLESS(size == copied || size == copied + 1, htc::ResultUnknown()); + + return ResultSuccess(); + } + + void GetEnvironmentVariableTask::Complete(HtcmiscResult result, const char *data, size_t size) { + /* Sanity check input. */ + if (size < sizeof(m_value)) { + /* Convert the result. */ + switch (result) { + case HtcmiscResult::Success: + /* Copy to our value. */ + std::memcpy(m_value, data, size); + m_value[size] = '\x00'; + m_value_size = size + 1; + + m_result = ResultSuccess(); + break; + case HtcmiscResult::UnknownError: + m_result = htc::ResultUnknown(); + break; + case HtcmiscResult::UnsupportedVersion: + m_result = htc::ResultConnectionFailure(); + break; + case HtcmiscResult::InvalidRequest: + m_result = htc::ResultNotFound(); + break; + } + } else { + m_result = htc::ResultUnknown(); + } + + /* Complete the task. */ + Task::Complete(); + } + + Result GetEnvironmentVariableTask::GetResult(size_t *out, char *dst, size_t size) { + /* Check our task state. */ + AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); + + /* Check that we succeeded. */ + R_TRY(m_result); + + /* Check that we can convert successfully. */ + R_UNLESS(util::IsIntValueRepresentable(size), htc::ResultUnknown()); + + /* Copy out. */ + const auto copied = util::Strlcpy(dst, m_value, size); + R_UNLESS(copied < static_cast(size), htc::ResultNotEnoughBuffer()); + + /* Set the output size. */ + *out = m_value_size; + + return ResultSuccess(); + } + + Result GetEnvironmentVariableTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Validate pre-conditions. */ + AMS_ASSERT(size >= sizeof(HtcmiscRpcPacket)); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcmiscProtocol, + .version = HtcmiscMaxVersion, + .category = HtcmiscPacketCategory::Request, + .type = HtcmiscPacketType::GetEnvironmentVariable, + .body_size = this->GetNameSize(), + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, this->GetName(), this->GetNameSize()); + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetNameSize(); + + return ResultSuccess(); + } + + Result GetEnvironmentVariableTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Process the packet. */ + this->Complete(static_cast(packet->params[0]), data + sizeof(*packet), size - sizeof(*packet)); + + /* Complete the task. */ + Task::Complete(); + + return ResultSuccess(); + } + + Result GetEnvironmentVariableLengthTask::SetArguments(const char *args, size_t size) { + /* Copy to our name. */ + const size_t copied = util::Strlcpy(m_name, args, sizeof(m_name)); + m_name_size = copied; + + /* Require that the size be correct. */ + + R_UNLESS(size == copied || size == copied + 1, htc::ResultUnknown()); + + return ResultSuccess(); + } + + void GetEnvironmentVariableLengthTask::Complete(HtcmiscResult result, const char *data, size_t size) { + /* Sanity check input. */ + if (size == sizeof(s64)) { + /* Convert the result. */ + switch (result) { + case HtcmiscResult::Success: + /* Copy to our value. */ + s64 tmp; + std::memcpy(std::addressof(tmp), data, sizeof(tmp)); + if (util::IsIntValueRepresentable(tmp)) { + m_value_size = static_cast(tmp); + } + + m_result = ResultSuccess(); + break; + case HtcmiscResult::UnknownError: + m_result = htc::ResultUnknown(); + break; + case HtcmiscResult::UnsupportedVersion: + m_result = htc::ResultConnectionFailure(); + break; + case HtcmiscResult::InvalidRequest: + m_result = htc::ResultNotFound(); + break; + } + } else { + m_result = htc::ResultUnknown(); + } + + /* Complete the task. */ + Task::Complete(); + } + + Result GetEnvironmentVariableLengthTask::GetResult(size_t *out) { + /* Check our task state. */ + AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); + + /* Check that we succeeded. */ + R_TRY(m_result); + + /* Set the output size. */ + *out = m_value_size; + + return ResultSuccess(); + } + + Result GetEnvironmentVariableLengthTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Validate pre-conditions. */ + AMS_ASSERT(size >= sizeof(HtcmiscRpcPacket)); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcmiscProtocol, + .version = HtcmiscMaxVersion, + .category = HtcmiscPacketCategory::Request, + .type = HtcmiscPacketType::GetEnvironmentVariableLength, + .body_size = this->GetNameSize(), + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, this->GetName(), this->GetNameSize()); + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetNameSize(); + + return ResultSuccess(); + } + + Result GetEnvironmentVariableLengthTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Process the packet. */ + this->Complete(static_cast(packet->params[0]), data + sizeof(*packet), size - sizeof(*packet)); + + /* Complete the task. */ + Task::Complete(); + + return ResultSuccess(); + } + + Result RunOnHostTask::SetArguments(const char *args, size_t size) { + /* Verify command fits in our buffer. */ + R_UNLESS(size < sizeof(m_command), htc::ResultNotEnoughBuffer()); + + /* Set our command. */ + std::memcpy(m_command, args, size); + m_command_size = size; + + return ResultSuccess(); + } + + void RunOnHostTask::Complete(int host_result) { + /* Set our host result. */ + m_host_result = host_result; + + /* Signal. */ + m_system_event.Signal(); + + /* Complete the task. */ + Task::Complete(); + } + + Result RunOnHostTask::GetResult(int *out) { + *out = m_host_result; + return ResultSuccess(); + } + + void RunOnHostTask::Cancel(RpcTaskCancelReason reason) { + /* Cancel the task. */ + Task::Cancel(reason); + + /* Signal our event. */ + m_system_event.Signal(); + } + + Result RunOnHostTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Validate pre-conditions. */ + AMS_ASSERT(size >= sizeof(HtcmiscRpcPacket)); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcmiscProtocol, + .version = HtcmiscMaxVersion, + .category = HtcmiscPacketCategory::Request, + .type = HtcmiscPacketType::RunOnHost, + .body_size = this->GetCommandSize(), + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, this->GetCommand(), this->GetCommandSize()); + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetCommandSize(); + + return ResultSuccess(); + } + + Result RunOnHostTask::ProcessResponse(const char *data, size_t size) { + this->Complete(reinterpret_cast(data)->params[0]); + return ResultSuccess(); + } + + os::SystemEventType *RunOnHostTask::GetSystemEvent() { + 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 new file mode 100644 index 000000000..d2015fb85 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp @@ -0,0 +1,141 @@ +/* + * 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 +#include "htc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + enum class HtcmiscTaskType { + GetEnvironmentVariable = 0, + GetEnvironmentVariableLength = 1, + SetTargetStatus = 2, + RunOnHost = 3, + }; + + enum class HtcmiscResult { + Success = 0, + UnknownError = 1, + UnsupportedVersion = 2, + InvalidRequest = 3, + }; + + enum class HtcmiscPacketCategory : s16 { + Request = 0, + Response = 1, + }; + + enum class HtcmiscPacketType : s16 { + GetMaxProtocolVersion = 0, + SetProtocolVersion = 1, + GetEnvironmentVariable = 16, + GetEnvironmentVariableLength = 17, + SetTargetStatus = 18, + RunOnHost = 19, + GetWorkingDirectory = 20, + GetWorkingDirectorySize = 21, + SetTargetName = 22, + }; + + constexpr inline s16 HtcmiscProtocol = 4; + constexpr inline s16 HtcmiscMaxVersion = 2; + + struct HtcmiscRpcPacket { + s16 protocol; + s16 version; + HtcmiscPacketCategory category; + HtcmiscPacketType type; + s64 body_size; + u32 task_id; + u64 params[5]; + u8 data[]; + }; + static_assert(sizeof(HtcmiscRpcPacket) == 0x40); + + class HtcmiscTask : public Task { + private: + HtcmiscTaskType m_task_type; + public: + HtcmiscTask(HtcmiscTaskType type) : m_task_type(type) { /* ... */ } + + HtcmiscTaskType GetTaskType() const { return m_task_type; } + }; + + class GetEnvironmentVariableTask : public HtcmiscTask { + private: + char m_name[0x800]; + int m_name_size; + Result m_result; + size_t m_value_size; + char m_value[0x8000]; + public: + GetEnvironmentVariableTask() : HtcmiscTask(HtcmiscTaskType::GetEnvironmentVariable) { /* ... */ } + + 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); + + const char *GetName() const { return m_name; } + int GetNameSize() const { return m_name_size; } + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class GetEnvironmentVariableLengthTask : public HtcmiscTask { + private: + char m_name[0x800]; + int m_name_size; + Result m_result; + size_t m_value_size; + public: + GetEnvironmentVariableLengthTask() : HtcmiscTask(HtcmiscTaskType::GetEnvironmentVariableLength) { /* ... */ } + + Result SetArguments(const char *args, size_t size); + void Complete(HtcmiscResult result, const char *data, size_t size); + Result GetResult(size_t *out); + + const char *GetName() const { return m_name; } + int GetNameSize() const { return m_name_size; } + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class RunOnHostTask : public HtcmiscTask { + private: + char m_command[0x2000]; + int m_command_size; + Result m_result; + int m_host_result; + os::SystemEvent m_system_event; + public: + RunOnHostTask() : HtcmiscTask(HtcmiscTaskType::RunOnHost), m_system_event(os::EventClearMode_ManualClear, true) { /* ... */ } + + Result SetArguments(const char *args, size_t size); + void Complete(int host_result); + Result GetResult(int *out); + + const char *GetCommand() const { return m_command; } + int GetCommandSize() const { return m_command_size; } + public: + virtual void Cancel(RpcTaskCancelReason reason) override; + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + virtual os::SystemEventType *GetSystemEvent() override; + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp new file mode 100644 index 000000000..9b8ef2566 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -0,0 +1,97 @@ +/* + * 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::htc::server::rpc { + + enum class RpcTaskCancelReason { + None = 0, + /* ... */ + }; + + enum class RpcTaskState { + Started = 0, + Completed = 1, + Cancelled = 2, + Notified = 3, + }; + + class Task { + private: + RpcTaskState m_state; + RpcTaskCancelReason m_cancel_reason; + os::Event m_event; + public: + Task() : m_state(RpcTaskState::Started), m_cancel_reason(RpcTaskCancelReason::None), m_event(os::EventClearMode_AutoClear) { /* ... */ } + virtual ~Task() { /* ... */ } + public: + void SetTaskState(RpcTaskState state) { m_state = state; } + RpcTaskState GetTaskState() const { return m_state; } + + RpcTaskCancelReason GetTaskCancelReason() const { return m_cancel_reason; } + + os::EventType *GetEvent() { return m_event.GetBase(); } + + void Notify() { + AMS_ASSERT(m_state == RpcTaskState::Started); + + m_state = RpcTaskState::Notified; + } + + void Complete() { + AMS_ASSERT(m_state == RpcTaskState::Started || m_state == RpcTaskState::Notified); + + m_state = RpcTaskState::Completed; + m_event.Signal(); + } + public: + virtual void Cancel(RpcTaskCancelReason reason) { + m_state = RpcTaskState::Cancelled; + m_cancel_reason = reason; + m_event.Signal(); + } + + virtual Result ProcessResponse(const char *data, size_t size) { + return ResultSuccess(); + } + + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + return ResultSuccess(); + } + + virtual Result ProcessNotification(const char *data, size_t size) { + return ResultSuccess(); + } + + virtual Result CreateNotification(size_t *out, char *data, size_t size, u32 task_id) { + return ResultSuccess(); + } + + virtual bool IsReceiveBufferRequired() { + return false; + } + + virtual bool IsSendBufferRequired() { + return false; + } + + virtual os::SystemEventType *GetSystemEvent() { + return nullptr; + } + }; + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 95c7f83e4..23b7e2e9a 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp new file mode 100644 index 000000000..152e6ed08 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -0,0 +1,28 @@ +/* + * 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::htc { + + R_DEFINE_NAMESPACE_RESULT_MODULE(18); + + R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); + R_DEFINE_ERROR_RESULT(NotFound, 2); + R_DEFINE_ERROR_RESULT(NotEnoughBuffer, 3); + R_DEFINE_ERROR_RESULT(Unknown, 1023); + +}