IOS/Network/KD: Implement GetSchedulerStat

This commit is contained in:
Sketch 2023-04-06 22:10:05 -04:00
parent 25fba7247e
commit 467b961e9c
6 changed files with 100 additions and 11 deletions

View File

@ -30,6 +30,7 @@ public:
void SetCookies(const std::string& cookies);
void UseIPv4();
void FollowRedirects(long max);
s32 GetLastResponseCode();
Response Fetch(const std::string& url, Method method, const Headers& headers, const u8* payload,
size_t size, AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);
@ -76,6 +77,11 @@ std::string HttpRequest::EscapeComponent(const std::string& string)
return m_impl->EscapeComponent(string);
}
s32 HttpRequest::GetLastResponseCode() const
{
return m_impl->GetLastResponseCode();
}
HttpRequest::Response HttpRequest::Get(const std::string& url, const Headers& headers,
AllowedReturnCodes codes)
{
@ -143,6 +149,13 @@ bool HttpRequest::Impl::IsValid() const
return m_curl != nullptr;
}
s32 HttpRequest::Impl::GetLastResponseCode()
{
s32 response_code{};
curl_easy_getinfo(m_curl.get(), CURLINFO_RESPONSE_CODE, &response_code);
return response_code;
}
void HttpRequest::Impl::SetCookies(const std::string& cookies)
{
curl_easy_setopt(m_curl.get(), CURLOPT_COOKIE, cookies.c_str());

View File

@ -39,6 +39,7 @@ public:
void SetCookies(const std::string& cookies);
void UseIPv4();
void FollowRedirects(long max = 1);
s32 GetLastResponseCode() const;
std::string EscapeComponent(const std::string& string);
Response Get(const std::string& url, const Headers& headers = {},
AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);

View File

@ -19,6 +19,8 @@ enum ErrorCode : s32
{
WC24_OK = 0,
WC24_ERR_FATAL = -1,
WC24_ERR_INVALID_VALUE = -3,
WC24_ERR_NULL = -5,
WC24_ERR_NOT_FOUND = -13,
WC24_ERR_BROKEN = -14,
WC24_ERR_FILE_OPEN = -16,

View File

@ -150,6 +150,9 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h
NetKDRequestDevice::NetKDRequestDevice(Kernel& ios, const std::string& device_name)
: Device(ios, device_name), config{ios.GetFS()}, m_dl_list{ios.GetFS()}
{
// Enable all NWC24 permissions
m_scheduler_buffer[1] = Common::swap32(-1);
m_work_queue.Reset("WiiConnect24 Worker", [this](AsyncTask task) {
const IPCReply reply = task.handler();
{
@ -177,6 +180,34 @@ void NetKDRequestDevice::Update()
}
}
void NetKDRequestDevice::LogError(ErrorType error_type, s32 error_code)
{
s32 new_code{};
switch (error_type)
{
case ErrorType::Account:
new_code = -(101200 - error_code);
break;
case ErrorType::Client:
new_code = -(107300 - error_code);
break;
case ErrorType::KD_Download:
new_code = -(107200 - error_code);
break;
case ErrorType::Server:
new_code = -(117000 + error_code);
break;
}
std::lock_guard lg(m_scheduler_buffer_lock);
m_scheduler_buffer[32 + (m_error_count % 32)] = Common::swap32(new_code);
m_error_count++;
m_scheduler_buffer[5] = Common::swap32(m_error_count);
m_scheduler_buffer[2] = Common::swap32(new_code);
}
NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
const std::optional<u8> subtask_id)
{
@ -194,7 +225,14 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
if (!response)
{
ERROR_LOG_FMT(IOS_WC24, "Failed to request data at {}", url);
const s32 last_response_code = m_http.GetLastResponseCode();
ERROR_LOG_FMT(IOS_WC24, "Failed to request data at {}. HTTP Status Code: {}", url,
last_response_code);
// On a real Wii, KD throws 107305 if it cannot connect to the host. While other issues other
// than invalid host may arise, this code is essentially a catch-all for HTTP client failure.
LogError(last_response_code ? ErrorType::Server : ErrorType::Client,
last_response_code ? last_response_code : NWC24::WC24_ERR_NULL);
return NWC24::WC24_ERR_SERVER;
}
@ -202,6 +240,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
if (response->size() < sizeof(NWC24::WC24File))
{
ERROR_LOG_FMT(IOS_WC24, "File at {} is too small to be a valid file.", url);
LogError(ErrorType::KD_Download, NWC24::WC24_ERR_BROKEN);
return NWC24::WC24_ERR_BROKEN;
}
@ -228,6 +267,9 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
NWC24::ErrorCode reply = IOS::HLE::NWC24::OpenVFF(m_dl_list.GetVFFPath(entry_index), content_name,
m_ios.GetFS(), file_data);
if (reply != NWC24::WC24_OK)
LogError(ErrorType::KD_Download, reply);
return reply;
}
@ -249,15 +291,17 @@ IPCReply NetKDRequestDevice::HandleNWC24DownloadNowEx(const IOCtlRequest& reques
if (entry_index >= NWC24::NWC24Dl::MAX_ENTRIES)
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: Entry index out of range.");
WriteReturnValue(NWC24::WC24_ERR_BROKEN, request.buffer_out);
return IPCReply(NWC24::WC24_ERR_BROKEN);
LogError(ErrorType::KD_Download, NWC24::WC24_ERR_INVALID_VALUE);
WriteReturnValue(NWC24::WC24_ERR_INVALID_VALUE, request.buffer_out);
return IPCReply(IPC_SUCCESS);
}
if (!m_dl_list.DoesEntryExist(entry_index))
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: Requested entry does not exist in download list!");
LogError(ErrorType::KD_Download, NWC24::WC24_ERR_NOT_FOUND);
WriteReturnValue(NWC24::WC24_ERR_NOT_FOUND, request.buffer_out);
return IPCReply(NWC24::WC24_ERR_NOT_FOUND);
return IPCReply(IPC_SUCCESS);
}
// While in theory reply will always get initialized by KDDownload, things happen.
@ -287,7 +331,7 @@ IPCReply NetKDRequestDevice::HandleNWC24DownloadNowEx(const IOCtlRequest& reques
}
WriteReturnValue(reply, request.buffer_out);
return IPCReply(reply);
return IPCReply(IPC_SUCCESS);
}
std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
@ -405,6 +449,7 @@ std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
}
else
{
LogError(ErrorType::Account, NWC24::WC24_ERR_INVALID_VALUE);
WriteReturnValue(NWC24::WC24_ERR_FATAL, request.buffer_out);
}
}
@ -421,8 +466,24 @@ std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
break;
case IOCTL_NWC24_GET_SCHEDULER_STAT:
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_GET_SCHEDULER_STAT - NI");
{
if (request.buffer_out == 0 || request.buffer_out % 4 != 0 || request.buffer_out_size < 16)
{
return_value = IPC_EINVAL;
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_GET_SCHEDULER_STAT = IPC_EINVAL");
break;
}
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_GET_SCHEDULER_STAT - buffer out size: {}",
request.buffer_out_size);
// On a real Wii, GetSchedulerStat copies memory containing a list of error codes recorded by
// KD among other things. In most instances there will never be more than one error code
// recorded as we do not have a scheduler.
const u32 out_size = std::min(request.buffer_out_size, 256U);
memory.CopyToEmu(request.buffer_out, m_scheduler_buffer.data(), out_size);
break;
}
case IOCTL_NWC24_SAVE_MAIL_NOW:
INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW - NI");

View File

@ -3,6 +3,7 @@
#pragma once
#include <array>
#include <queue>
#include <string>
@ -16,8 +17,6 @@
namespace IOS::HLE
{
constexpr const char DL_CNT_PATH[] = "/" WII_WC24CONF_DIR "/dlcnt.bin";
// KD is the IOS module responsible for implementing WiiConnect24 functionality.
// It can perform HTTPS downloads, send and receive mail via SMTP, and execute a
// JavaScript-like language while the Wii is in standby mode.
@ -52,11 +51,24 @@ private:
return std::nullopt;
}
enum class ErrorType
{
Account,
KD_Download,
Client,
Server,
};
void LogError(ErrorType error_type, s32 error_code);
NWC24::NWC24Config config;
NWC24::NWC24Dl m_dl_list;
Common::WorkQueueThread<AsyncTask> m_work_queue;
std::mutex m_async_reply_lock;
std::mutex m_scheduler_buffer_lock;
std::queue<AsyncReply> m_async_replies;
u32 m_error_count = 0;
std::array<u32, 256> m_scheduler_buffer{};
// TODO: Maybe move away from Common::HttpRequest?
Common::HttpRequest m_http{std::chrono::minutes{1}};
};

View File

@ -267,7 +267,7 @@ ErrorCode OpenVFF(const std::string& path, const std::string& filename,
if (!temp)
{
ERROR_LOG_FMT(IOS_WC24, "Failed to open VFF at: {}", path);
return_value = WC24_ERR_NOT_FOUND;
return_value = WC24_ERR_FILE_OPEN;
return;
}
@ -281,7 +281,7 @@ ErrorCode OpenVFF(const std::string& path, const std::string& filename,
{
// The VFF is most likely broken.
ERROR_LOG_FMT(IOS_WC24, "Failed to mount VFF at: {}", path);
return_value = WC24_ERR_BROKEN;
return_value = WC24_ERR_FILE_READ;
return;
}
@ -290,7 +290,7 @@ ErrorCode OpenVFF(const std::string& path, const std::string& filename,
{
// The VFF is most likely broken.
ERROR_LOG_FMT(IOS_WC24, "Failed to mount VFF at: {}", path);
return_value = WC24_ERR_BROKEN;
return_value = WC24_ERR_FILE_READ;
return;
}