1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-23 04:12:02 +00:00

pm: update for libstratosphere refactor

This commit is contained in:
Michael Scire 2018-10-29 17:29:35 -07:00 committed by SciresM
parent 9a8c70ed68
commit 887b4e0275
17 changed files with 229 additions and 526 deletions

View file

@ -1,7 +1,7 @@
{
"name": "ProcessMana",
"title_id": "0x0100000000000003",
"main_thread_stack_size": "0x00001000",
"main_thread_stack_size": "0x00002000",
"main_thread_priority": 49,
"default_cpu_id": 3,
"process_category": 1,

View file

@ -173,7 +173,7 @@ void EmbeddedBoot2::Main() {
}
closedir(titles_dir);
}
/* We no longer need the SD card. */
fsdevUnmountAll();
}

View file

@ -20,33 +20,10 @@
static bool g_is_maintenance_boot = false;
Result BootModeService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601;
switch ((BootModeCmd)cmd_id) {
case BootMode_Cmd_GetBootMode:
rc = WrapIpcCommandImpl<&BootModeService::get_boot_mode>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case BootMode_Cmd_SetMaintenanceBoot:
rc = WrapIpcCommandImpl<&BootModeService::set_maintenance_boot>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
return rc;
void BootModeService::GetBootMode(Out<bool> out) {
out.SetValue(g_is_maintenance_boot);
}
Result BootModeService::handle_deferred() {
/* This service is never deferrable. */
return 0;
}
std::tuple<Result, bool> BootModeService::get_boot_mode() {
return {0, g_is_maintenance_boot};
}
std::tuple<Result> BootModeService::set_maintenance_boot() {
void BootModeService::SetMaintenanceBoot() {
g_is_maintenance_boot = true;
return {0};
}

View file

@ -16,24 +16,21 @@
#pragma once
#include <switch.h>
#include <stratosphere/iserviceobject.hpp>
#include <stratosphere.hpp>
enum BootModeCmd {
BootMode_Cmd_GetBootMode = 0,
BootMode_Cmd_SetMaintenanceBoot = 1
};
class BootModeService final : public IServiceObject {
public:
Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) override;
Result handle_deferred() override;
BootModeService *clone() override {
return new BootModeService(*this);
}
class BootModeService final : public IServiceObject {
private:
/* Actual commands. */
std::tuple<Result, bool> get_boot_mode();
std::tuple<Result> set_maintenance_boot();
void GetBootMode(Out<bool> out);
void SetMaintenanceBoot();
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MakeServiceCommandMeta<BootMode_Cmd_GetBootMode, &BootModeService::GetBootMode>(),
MakeServiceCommandMeta<BootMode_Cmd_SetMaintenanceBoot, &BootModeService::SetMaintenanceBoot>(),
};
};

View file

@ -20,172 +20,88 @@
#include "pm_resource_limits.hpp"
#include "pm_debug_monitor.hpp"
Result DebugMonitorService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601;
if (kernelAbove500()) {
switch ((DmntCmd_5X)cmd_id) {
case Dmnt_Cmd_5X_GetDebugProcessIds:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_debug_process_ids>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_5X_LaunchDebugProcess:
rc = WrapIpcCommandImpl<&DebugMonitorService::launch_debug_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_5X_GetTitleProcessId:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_title_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_5X_EnableDebugForTitleId:
rc = WrapIpcCommandImpl<&DebugMonitorService::enable_debug_for_tid>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_5X_GetApplicationProcessId:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_application_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_5X_EnableDebugForApplication:
rc = WrapIpcCommandImpl<&DebugMonitorService::enable_debug_for_application>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_6X_DisableDebug:
if (kernelAbove600()) {
rc = WrapIpcCommandImpl<&DebugMonitorService::disable_debug>(this, r, out_c, pointer_buffer, pointer_buffer_size);
}
break;
case Dmnt_Cmd_5X_AtmosphereGetProcessHandle:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_process_handle>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_5X_AtmosphereGetCurrentLimitInfo:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_current_limit_info>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
} else {
switch ((DmntCmd)cmd_id) {
case Dmnt_Cmd_GetUnknownStub:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_unknown_stub>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_GetDebugProcessIds:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_debug_process_ids>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_LaunchDebugProcess:
rc = WrapIpcCommandImpl<&DebugMonitorService::launch_debug_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_GetTitleProcessId:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_title_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_EnableDebugForTitleId:
rc = WrapIpcCommandImpl<&DebugMonitorService::enable_debug_for_tid>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_GetApplicationProcessId:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_application_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_EnableDebugForApplication:
rc = WrapIpcCommandImpl<&DebugMonitorService::enable_debug_for_application>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_AtmosphereGetProcessHandle:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_process_handle>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Dmnt_Cmd_AtmosphereGetCurrentLimitInfo:
rc = WrapIpcCommandImpl<&DebugMonitorService::get_current_limit_info>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
Result DebugMonitorService::GetUnknownStub(Out<u32> count, OutBuffer<u8> out_buf, u64 in_unk) {
/* This command seems stubbed. */
if (out_buf.num_elements >> 31) {
return 0xC0F;
}
return rc;
count.SetValue(0);
return 0x0;
}
Result DebugMonitorService::handle_deferred() {
/* This service is never deferrable. */
return 0;
}
std::tuple<Result, u32> DebugMonitorService::get_unknown_stub(u64 unknown, OutBuffer<u8> out_unknown) {
/* This command seem stubbed on retail. */
if (out_unknown.num_elements >> 31) {
return {0xC0F, 0};
}
return {0x0, 0};
}
std::tuple<Result, u32> DebugMonitorService::get_debug_process_ids(OutBuffer<u64> out_pids) {
u32 num_out = 0;
Result rc;
Result DebugMonitorService::GetDebugProcessIds(Out<u32> count, OutBuffer<u64> out_pids) {
if (out_pids.num_elements >> 31) {
return {0xC0F, 0};
return 0xC0F;
}
rc = Registration::GetDebugProcessIds(out_pids.buffer, out_pids.num_elements, &num_out);
return {rc, num_out};
return Registration::GetDebugProcessIds(out_pids.buffer, out_pids.num_elements, count.GetPointer());
}
std::tuple<Result> DebugMonitorService::launch_debug_process(u64 pid) {
return {Registration::LaunchDebugProcess(pid)};
Result DebugMonitorService::LaunchDebugProcess(u64 pid) {
return Registration::LaunchDebugProcess(pid);
}
std::tuple<Result, u64> DebugMonitorService::get_title_process_id(u64 tid) {
Result DebugMonitorService::GetTitleProcessId(Out<u64> pid, u64 tid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> proc = Registration::GetProcessByTitleId(tid);
if (proc != nullptr) {
return {0, proc->pid};
} else {
return {0x20F, 0};
pid.SetValue(proc->pid);
return 0;
}
return 0x20F;
}
std::tuple<Result, CopiedHandle> DebugMonitorService::enable_debug_for_tid(u64 tid) {
Handle h = 0;
Result rc = Registration::EnableDebugForTitleId(tid, &h);
return {rc, h};
Result DebugMonitorService::EnableDebugForTitleId(Out<CopiedHandle> event, u64 tid) {
return Registration::EnableDebugForTitleId(tid, event.GetHandlePointer());
}
std::tuple<Result, u64> DebugMonitorService::get_application_process_id() {
Result DebugMonitorService::GetApplicationProcessId(Out<u64> pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> app_proc;
if (Registration::HasApplicationProcess(&app_proc)) {
return {0, app_proc->pid};
pid.SetValue(app_proc->pid);
return 0x0;
}
return {0x20F, 0};
return 0x20F;
}
std::tuple<Result, CopiedHandle> DebugMonitorService::enable_debug_for_application() {
Handle h = 0;
Result rc = Registration::EnableDebugForApplication(&h);
return {rc, h};
Result DebugMonitorService::EnableDebugForApplication(Out<CopiedHandle> event) {
return Registration::EnableDebugForApplication(event.GetHandlePointer());
}
std::tuple<Result> DebugMonitorService::disable_debug(u32 which) {
return {Registration::DisableDebug(which)};
Result DebugMonitorService::DisableDebug(u32 which) {
return Registration::DisableDebug(which);
}
std::tuple<Result, CopiedHandle> DebugMonitorService::get_process_handle(u64 pid) {
std::shared_ptr<Registration::Process> proc = Registration::GetProcess(pid);
if(proc == NULL) {
return {0x20F, 0};
Result DebugMonitorService::AtmosphereGetProcessHandle(Out<CopiedHandle> proc_hand, u64 pid) {
auto proc = Registration::GetProcess(pid);
if(proc != nullptr) {
proc_hand.SetValue(proc->handle);
return 0;
}
return {0, proc->handle};
return 0x20F;
}
std::tuple<Result, u64, u64> DebugMonitorService::get_current_limit_info(u32 category, u32 resource) {
Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(Out<u64> cur_val, Out<u64> lim_val, u32 category, u32 resource) {
Result rc;
if(category > ResourceLimitUtils::ResourceLimitCategory::ResourceLimitCategory_Applet) {
return {0xf001, 0, 0};
return 0xF001;
}
Handle limit_h = ResourceLimitUtils::GetResourceLimitHandleByCategory((ResourceLimitUtils::ResourceLimitCategory) category);
uint64_t current_value, limit_value;
Result r;
r = svcGetResourceLimitCurrentValue(&current_value, limit_h, (LimitableResource) resource);
if(R_FAILED(r)) {
return {r, 0, 0};
rc = svcGetResourceLimitCurrentValue(cur_val.GetPointer(), limit_h, (LimitableResource) resource);
if(R_FAILED(rc)) {
return rc;
}
r = svcGetResourceLimitLimitValue(&limit_value, limit_h, (LimitableResource) resource);
if(R_FAILED(r)) {
return {r, 0, 0};
rc = svcGetResourceLimitLimitValue(lim_val.GetPointer(), limit_h, (LimitableResource) resource);
if(R_FAILED(rc)) {
return rc;
}
return {0, current_value, limit_value};
return 0;
}

View file

@ -16,7 +16,7 @@
#pragma once
#include <switch.h>
#include <stratosphere/iserviceobject.hpp>
#include <stratosphere.hpp>
#include "pm_registration.hpp"
@ -28,12 +28,7 @@ enum DmntCmd {
Dmnt_Cmd_EnableDebugForTitleId = 4,
Dmnt_Cmd_GetApplicationProcessId = 5,
Dmnt_Cmd_EnableDebugForApplication = 6,
Dmnt_Cmd_AtmosphereGetProcessHandle = 65000,
Dmnt_Cmd_AtmosphereGetCurrentLimitInfo = 65001,
};
enum DmntCmd_5X {
Dmnt_Cmd_5X_GetDebugProcessIds = 0,
Dmnt_Cmd_5X_LaunchDebugProcess = 1,
Dmnt_Cmd_5X_GetTitleProcessId = 2,
@ -43,31 +38,49 @@ enum DmntCmd_5X {
Dmnt_Cmd_6X_DisableDebug = 6,
Dmnt_Cmd_5X_AtmosphereGetProcessHandle = 65000,
Dmnt_Cmd_5X_AtmosphereGetCurrentLimitInfo = 65001,
Dmnt_Cmd_AtmosphereGetProcessHandle = 65000,
Dmnt_Cmd_AtmosphereGetCurrentLimitInfo = 65001,
};
class DebugMonitorService final : public IServiceObject {
public:
Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) override;
Result handle_deferred() override;
DebugMonitorService *clone() override {
return new DebugMonitorService(*this);
}
private:
/* Actual commands. */
std::tuple<Result, u32> get_unknown_stub(u64 unknown, OutBuffer<u8> out_unknown);
std::tuple<Result, u32> get_debug_process_ids(OutBuffer<u64> out_processes);
std::tuple<Result> launch_debug_process(u64 pid);
std::tuple<Result, u64> get_title_process_id(u64 tid);
std::tuple<Result, CopiedHandle> enable_debug_for_tid(u64 tid);
std::tuple<Result, u64> get_application_process_id();
std::tuple<Result, CopiedHandle> enable_debug_for_application();
std::tuple<Result> disable_debug(u32 which);
Result GetUnknownStub(Out<u32> count, OutBuffer<u8> out_buf, u64 in_unk);
Result GetDebugProcessIds(Out<u32> count, OutBuffer<u64> out_pids);
Result LaunchDebugProcess(u64 pid);
Result GetTitleProcessId(Out<u64> pid, u64 tid);
Result EnableDebugForTitleId(Out<CopiedHandle> event, u64 tid);
Result GetApplicationProcessId(Out<u64> pid);
Result EnableDebugForApplication(Out<CopiedHandle> event);
Result DisableDebug(u32 which);
/* Atmosphere commands. */
std::tuple<Result, CopiedHandle> get_process_handle(u64 pid);
std::tuple<Result, u64, u64> get_current_limit_info(u32 category, u32 resource);
Result AtmosphereGetProcessHandle(Out<CopiedHandle> proc_hand, u64 pid);
Result AtmosphereGetCurrentLimitInfo(Out<u64> cur_val, Out<u64> lim_val, u32 category, u32 resource);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0-4.1.0 */
MakeServiceCommandMeta<Dmnt_Cmd_GetUnknownStub, &DebugMonitorService::GetUnknownStub, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Dmnt_Cmd_GetDebugProcessIds, &DebugMonitorService::GetDebugProcessIds, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Dmnt_Cmd_LaunchDebugProcess, &DebugMonitorService::LaunchDebugProcess, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Dmnt_Cmd_GetTitleProcessId, &DebugMonitorService::GetTitleProcessId, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Dmnt_Cmd_EnableDebugForTitleId, &DebugMonitorService::EnableDebugForTitleId, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Dmnt_Cmd_GetApplicationProcessId, &DebugMonitorService::GetApplicationProcessId, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Dmnt_Cmd_EnableDebugForApplication, &DebugMonitorService::EnableDebugForApplication, FirmwareVersion_Min, FirmwareVersion_400>(),
/* 5.0.0-* */
MakeServiceCommandMeta<Dmnt_Cmd_5X_GetDebugProcessIds, &DebugMonitorService::GetDebugProcessIds, FirmwareVersion_500>(),
MakeServiceCommandMeta<Dmnt_Cmd_5X_LaunchDebugProcess, &DebugMonitorService::LaunchDebugProcess, FirmwareVersion_500>(),
MakeServiceCommandMeta<Dmnt_Cmd_5X_GetTitleProcessId, &DebugMonitorService::GetTitleProcessId, FirmwareVersion_500>(),
MakeServiceCommandMeta<Dmnt_Cmd_5X_EnableDebugForTitleId, &DebugMonitorService::EnableDebugForTitleId, FirmwareVersion_500>(),
MakeServiceCommandMeta<Dmnt_Cmd_5X_GetApplicationProcessId, &DebugMonitorService::GetApplicationProcessId, FirmwareVersion_500>(),
MakeServiceCommandMeta<Dmnt_Cmd_5X_EnableDebugForApplication, &DebugMonitorService::EnableDebugForApplication, FirmwareVersion_500>(),
/* 6.0.0-* */
MakeServiceCommandMeta<Dmnt_Cmd_6X_DisableDebug, &DebugMonitorService::DisableDebug, FirmwareVersion_600>(),
/* Atmosphere extensions. */
MakeServiceCommandMeta<Dmnt_Cmd_AtmosphereGetProcessHandle, &DebugMonitorService::AtmosphereGetProcessHandle>(),
MakeServiceCommandMeta<Dmnt_Cmd_AtmosphereGetCurrentLimitInfo, &DebugMonitorService::AtmosphereGetCurrentLimitInfo>(),
};
};

View file

@ -18,32 +18,13 @@
#include "pm_registration.hpp"
#include "pm_info.hpp"
Result InformationService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601;
switch ((InformationCmd)cmd_id) {
case Information_Cmd_GetTitleId:
rc = WrapIpcCommandImpl<&InformationService::get_title_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
return rc;
}
Result InformationService::handle_deferred() {
/* This service is never deferrable. */
return 0;
}
std::tuple<Result, u64> InformationService::get_title_id(u64 pid) {
Result InformationService::GetTitleId(Out<u64> tid, u64 pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> proc = Registration::GetProcess(pid);
if (proc != NULL) {
return {0x0, proc->tid_sid.title_id};
} else {
return {0x20F, 0x0};
tid.SetValue(proc->tid_sid.title_id);
return 0;
}
return 0x20F;
}

View file

@ -17,22 +17,17 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include <stratosphere/iserviceobject.hpp>
enum InformationCmd {
Information_Cmd_GetTitleId = 0,
};
class InformationService final : public IServiceObject {
public:
Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) override;
Result handle_deferred() override;
InformationService *clone() override {
return new InformationService(*this);
}
private:
/* Actual commands. */
std::tuple<Result, u64> get_title_id(u64 pid);
Result GetTitleId(Out<u64> tid, u64 pid);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MakeServiceCommandMeta<Information_Cmd_GetTitleId, &InformationService::GetTitleId>(),
};
};

View file

@ -28,7 +28,6 @@
#include "pm_process_track.hpp"
#include "pm_registration.hpp"
#include "pm_debug_monitor.hpp"
#include "smm_ams.h"
extern "C" {
extern u32 __start__;
@ -150,16 +149,16 @@ int main(int argc, char **argv)
}
/* TODO: What's a good timeout value to use here? */
WaitableManager *server_manager = new WaitableManager(U64_MAX);
auto server_manager = new WaitableManager(1);
/* TODO: Create services. */
server_manager->add_waitable(new ServiceServer<ShellService>("pm:shell", 3));
server_manager->add_waitable(new ServiceServer<DebugMonitorService>("pm:dmnt", 2));
server_manager->add_waitable(new ServiceServer<BootModeService>("pm:bm", 5));
server_manager->add_waitable(new ServiceServer<InformationService>("pm:info", 1));
server_manager->AddWaitable(new ServiceServer<ShellService>("pm:shell", 3));
server_manager->AddWaitable(new ServiceServer<DebugMonitorService>("pm:dmnt", 2));
server_manager->AddWaitable(new ServiceServer<BootModeService>("pm:bm", 5));
server_manager->AddWaitable(new ServiceServer<InformationService>("pm:info", 1));
/* Loop forever, servicing our services. */
server_manager->process();
server_manager->Process();
/* Cleanup. */
delete server_manager;

View file

@ -21,12 +21,11 @@
void ProcessTracking::MainLoop(void *arg) {
/* Make a new waitable manager. */
MultiThreadedWaitableManager *process_waiter = new MultiThreadedWaitableManager(1, U64_MAX);
process_waiter->add_waitable(Registration::GetProcessLaunchStartEvent());
Registration::SetProcessListManager(process_waiter);
auto process_waiter = new WaitableManager(1);
process_waiter->AddWaitable(Registration::GetProcessLaunchStartEvent());
/* Service processes. */
process_waiter->process();
process_waiter->Process();
delete process_waiter;
svcExitThread();

View file

@ -28,41 +28,28 @@ class ProcessWaiter final : public IWaitable {
/* ... */
}
std::shared_ptr<Registration::Process> get_process() {
std::shared_ptr<Registration::Process> GetProcess() {
return this->process;
}
/* IWaitable */
Handle get_handle() override {
Handle GetHandle() override {
return this->process->handle;
}
void handle_deferred() override {
/* TODO: Panic, because we can never be deferred. */
}
Result handle_signaled(u64 timeout) override {
return Registration::HandleSignaledProcess(this->get_process());
Result HandleSignaled(u64 timeout) override {
return Registration::HandleSignaledProcess(this->GetProcess());
}
};
class ProcessList final {
private:
HosRecursiveMutex mutex;
WaitableManager *manager;
public:
std::vector<std::shared_ptr<Registration::Process>> processes;
auto get_unique_lock() {
auto GetUniqueLock() {
return std::unique_lock{this->mutex};
}
void set_manager(WaitableManager *manager) {
this->manager = manager;
}
WaitableManager *get_manager() {
return this->manager;
}
};

View file

@ -25,7 +25,7 @@
static ProcessList g_process_list;
static ProcessList g_dead_process_list;
static SystemEvent *g_process_launch_start_event = NULL;
static IEvent *g_process_launch_start_event = nullptr;
static HosSemaphore g_sema_finish_launch;
static HosMutex g_process_launch_mutex;
@ -34,29 +34,27 @@ static Registration::ProcessLaunchState g_process_launch_state;
static std::atomic_bool g_debug_next_application(false);
static std::atomic<u64> g_debug_on_launch_tid(0);
static SystemEvent *g_process_event = NULL;
static SystemEvent *g_debug_title_event = NULL;
static SystemEvent *g_debug_application_event = NULL;
static IEvent *g_process_event = nullptr;
static IEvent *g_debug_title_event = nullptr;
static IEvent *g_debug_application_event = nullptr;
std::unique_lock<HosRecursiveMutex> Registration::GetProcessListUniqueLock() {
return g_process_list.get_unique_lock();
}
void Registration::SetProcessListManager(WaitableManager *m) {
g_process_list.set_manager(m);
return g_process_list.GetUniqueLock();
}
void Registration::InitializeSystemResources() {
g_process_event = new SystemEvent(NULL, &IEvent::PanicCallback);
g_debug_title_event = new SystemEvent(NULL, &IEvent::PanicCallback);
g_debug_application_event = new SystemEvent(NULL, &IEvent::PanicCallback);
g_process_launch_start_event = new SystemEvent(NULL, &Registration::ProcessLaunchStartCallback);
g_process_event = CreateWriteOnlySystemEvent();
g_debug_title_event = CreateWriteOnlySystemEvent();
g_debug_application_event = CreateWriteOnlySystemEvent();
/* Auto-clear non-system event. */
g_process_launch_start_event = CreateSystemEvent(&Registration::ProcessLaunchStartCallback);
ResourceLimitUtils::InitializeLimits();
}
Result Registration::ProcessLaunchStartCallback(void *arg, Handle *handles, size_t num_handles, u64 timeout) {
svcClearEvent(handles[0]);
Result Registration::ProcessLaunchStartCallback(u64 timeout) {
g_process_launch_start_event->Clear();
Registration::HandleProcessLaunch();
return 0;
}
@ -138,11 +136,11 @@ void Registration::HandleProcessLaunch() {
/* Signal, if relevant. */
if (new_process.tid_sid.title_id == g_debug_on_launch_tid.load()) {
g_debug_title_event->signal_event();
g_debug_title_event->Signal();
g_debug_on_launch_tid = 0;
rc = 0;
} else if ((new_process.flags & PROCESSFLAGS_APPLICATION) && g_debug_next_application.load()) {
g_debug_application_event->signal_event();
g_debug_application_event->Signal();
g_debug_next_application = false;
rc = 0;
} else if (LAUNCHFLAGS_STARTSUSPENDED(launch_flags)) {
@ -181,6 +179,7 @@ HANDLE_PROCESS_LAUNCH_END:
if (R_SUCCEEDED(rc)) {
*out_pid = new_process.pid;
}
g_sema_finish_launch.Signal();
}
@ -220,9 +219,9 @@ Result Registration::LaunchProcess(u64 title_id, FsStorageId storage_id, u64 lau
g_process_launch_state.out_pid = out_pid;
/* Start a launch, and wait for it to exit. */
g_process_launch_start_event->signal_event();
g_process_launch_start_event->Signal();
g_sema_finish_launch.Wait();
return g_process_launch_state.result;
}
@ -253,7 +252,7 @@ Result Registration::HandleSignaledProcess(std::shared_ptr<Registration::Process
if (process->flags & PROCESSFLAGS_NOTIFYDEBUGEVENTS) {
process->flags &= ~(PROCESSFLAGS_DEBUGEVENTPENDING | PROCESSFLAGS_DEBUGSUSPENDED);
process->flags |= PROCESSFLAGS_DEBUGEVENTPENDING;
g_process_event->signal_event();
g_process_event->Signal();
}
if (kernelAbove200() && process->flags & PROCESSFLAGS_NOTIFYDEBUGSPECIAL) {
process->flags &= ~(PROCESSFLAGS_NOTIFYDEBUGSPECIAL | PROCESSFLAGS_DEBUGDETACHED);
@ -262,18 +261,18 @@ Result Registration::HandleSignaledProcess(std::shared_ptr<Registration::Process
break;
case ProcessState_Crashed:
process->flags |= (PROCESSFLAGS_CRASHED | PROCESSFLAGS_CRASH_DEBUG);
g_process_event->signal_event();
g_process_event->Signal();
break;
case ProcessState_RunningAttached:
if (process->flags & PROCESSFLAGS_NOTIFYDEBUGEVENTS) {
process->flags &= ~(PROCESSFLAGS_DEBUGEVENTPENDING | PROCESSFLAGS_DEBUGSUSPENDED);
process->flags |= PROCESSFLAGS_DEBUGEVENTPENDING;
g_process_event->signal_event();
g_process_event->Signal();
}
break;
case ProcessState_Exited:
if (process->flags & PROCESSFLAGS_NOTIFYWHENEXITED && !kernelAbove500()) {
g_process_event->signal_event();
g_process_event->Signal();
} else {
FinalizeExitedProcess(process);
}
@ -281,7 +280,7 @@ Result Registration::HandleSignaledProcess(std::shared_ptr<Registration::Process
case ProcessState_DebugSuspended:
if (process->flags & PROCESSFLAGS_NOTIFYDEBUGEVENTS) {
process->flags |= (PROCESSFLAGS_DEBUGEVENTPENDING | PROCESSFLAGS_DEBUGSUSPENDED);
g_process_event->signal_event();
g_process_event->Signal();
}
break;
}
@ -310,7 +309,7 @@ void Registration::FinalizeExitedProcess(std::shared_ptr<Registration::Process>
/* Insert into dead process list, if relevant. */
if (signal_debug_process_5x) {
auto lk = g_dead_process_list.get_unique_lock();
auto lk = g_dead_process_list.GetUniqueLock();
g_dead_process_list.processes.push_back(process);
}
@ -319,14 +318,14 @@ void Registration::FinalizeExitedProcess(std::shared_ptr<Registration::Process>
auto_lock.unlock();
if (signal_debug_process_5x) {
g_process_event->signal_event();
g_process_event->Signal();
}
}
void Registration::AddProcessToList(std::shared_ptr<Registration::Process> process) {
auto auto_lock = GetProcessListUniqueLock();
g_process_list.processes.push_back(process);
g_process_list.get_manager()->add_waitable(new ProcessWaiter(process));
g_process_launch_start_event->GetManager()->AddWaitable(new ProcessWaiter(process));
}
void Registration::RemoveProcessFromList(u64 pid) {
@ -412,7 +411,7 @@ Result Registration::GetDebugProcessIds(u64 *out_pids, u32 max_out, u32 *num_out
}
Handle Registration::GetProcessEventHandle() {
return g_process_event->get_handle();
return g_process_event->GetHandle();
}
void Registration::GetProcessEventType(u64 *out_pid, u64 *out_type) {
@ -451,7 +450,7 @@ void Registration::GetProcessEventType(u64 *out_pid, u64 *out_type) {
}
if (kernelAbove500()) {
auto_lock.unlock();
auto dead_process_list_lock = g_dead_process_list.get_unique_lock();
auto dead_process_list_lock = g_dead_process_list.GetUniqueLock();
if (g_dead_process_list.processes.size()) {
std::shared_ptr<Registration::Process> process = g_dead_process_list.processes[0];
g_dead_process_list.processes.erase(g_dead_process_list.processes.begin());
@ -471,13 +470,13 @@ Result Registration::EnableDebugForTitleId(u64 tid, Handle *out) {
g_debug_on_launch_tid = old;
return 0x80F;
}
*out = g_debug_title_event->get_handle();
*out = g_debug_title_event->GetHandle();
return 0x0;
}
Result Registration::EnableDebugForApplication(Handle *out) {
g_debug_next_application = true;
*out = g_debug_application_event->get_handle();
*out = g_debug_application_event->GetHandle();
return 0;
}

View file

@ -172,8 +172,7 @@ class Registration {
static void InitializeSystemResources();
static IWaitable *GetProcessLaunchStartEvent();
static std::unique_lock<HosRecursiveMutex> GetProcessListUniqueLock();
static void SetProcessListManager(WaitableManager *m);
static Result ProcessLaunchStartCallback(void *arg, Handle *handles, size_t num_handles, u64 timeout);
static Result ProcessLaunchStartCallback(u64 timeout);
static Result HandleSignaledProcess(std::shared_ptr<Process> process);
static void FinalizeExitedProcess(std::shared_ptr<Process> process);

View file

@ -23,170 +23,84 @@
static bool g_has_boot_finished = false;
Result ShellService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
Result rc = 0xF601;
if (kernelAbove500()) {
switch ((ShellCmd_5X)cmd_id) {
case Shell_Cmd_5X_LaunchProcess:
rc = WrapIpcCommandImpl<&ShellService::launch_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_TerminateProcessId:
rc = WrapIpcCommandImpl<&ShellService::terminate_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_TerminateTitleId:
rc = WrapIpcCommandImpl<&ShellService::terminate_title_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_GetProcessWaitEvent:
rc = WrapIpcCommandImpl<&ShellService::get_process_wait_event>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_GetProcessEventType:
rc = WrapIpcCommandImpl<&ShellService::get_process_event_type>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_NotifyBootFinished:
rc = WrapIpcCommandImpl<&ShellService::notify_boot_finished>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_GetApplicationProcessId:
rc = WrapIpcCommandImpl<&ShellService::get_application_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_5X_BoostSystemMemoryResourceLimit:
rc = WrapIpcCommandImpl<&ShellService::boost_system_memory_resource_limit>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
} else {
switch ((ShellCmd)cmd_id) {
case Shell_Cmd_LaunchProcess:
rc = WrapIpcCommandImpl<&ShellService::launch_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_TerminateProcessId:
rc = WrapIpcCommandImpl<&ShellService::terminate_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_TerminateTitleId:
rc = WrapIpcCommandImpl<&ShellService::terminate_title_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_GetProcessWaitEvent:
rc = WrapIpcCommandImpl<&ShellService::get_process_wait_event>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_GetProcessEventType:
rc = WrapIpcCommandImpl<&ShellService::get_process_event_type>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_FinalizeExitedProcess:
rc = WrapIpcCommandImpl<&ShellService::finalize_exited_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_ClearProcessNotificationFlag:
rc = WrapIpcCommandImpl<&ShellService::clear_process_notification_flag>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_NotifyBootFinished:
rc = WrapIpcCommandImpl<&ShellService::notify_boot_finished>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_GetApplicationProcessId:
rc = WrapIpcCommandImpl<&ShellService::get_application_process_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
case Shell_Cmd_BoostSystemMemoryResourceLimit:
rc = WrapIpcCommandImpl<&ShellService::boost_system_memory_resource_limit>(this, r, out_c, pointer_buffer, pointer_buffer_size);
break;
default:
break;
}
}
return rc;
Result ShellService::LaunchProcess(Out<u64> pid, u64 launch_flags, Registration::TidSid tid_sid) {
return Registration::LaunchProcessByTidSid(tid_sid, launch_flags, pid.GetPointer());
}
Result ShellService::handle_deferred() {
/* This service is never deferrable. */
return 0;
}
std::tuple<Result, u64> ShellService::launch_process(u64 launch_flags, Registration::TidSid tid_sid) {
u64 pid = 0;
Result rc = Registration::LaunchProcessByTidSid(tid_sid, launch_flags, &pid);
return {rc, pid};
}
std::tuple<Result> ShellService::terminate_process_id(u64 pid) {
Result ShellService::TerminateProcessId(u64 pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> proc = Registration::GetProcess(pid);
auto proc = Registration::GetProcess(pid);
if (proc != nullptr) {
return svcTerminateProcess(proc->handle);
} else {
return 0x20F;
}
}
Result ShellService::TerminateTitleId(u64 tid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
auto proc = Registration::GetProcessByTitleId(tid);
if (proc != NULL) {
return {svcTerminateProcess(proc->handle)};
return svcTerminateProcess(proc->handle);
} else {
return {0x20F};
return 0x20F;
}
}
std::tuple<Result> ShellService::terminate_title_id(u64 tid) {
void ShellService::GetProcessWaitEvent(Out<CopiedHandle> event) {
event.SetValue(Registration::GetProcessEventHandle());
}
void ShellService::GetProcessEventType(Out<u64> type, Out<u64> pid) {
Registration::GetProcessEventType(pid.GetPointer(), type.GetPointer());
}
Result ShellService::FinalizeExitedProcess(u64 pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> proc = Registration::GetProcessByTitleId(tid);
if (proc != NULL) {
return {svcTerminateProcess(proc->handle)};
} else {
return {0x20F};
}
}
std::tuple<Result, CopiedHandle> ShellService::get_process_wait_event() {
return {0x0, Registration::GetProcessEventHandle()};
}
std::tuple<Result, u64, u64> ShellService::get_process_event_type() {
u64 type, pid;
Registration::GetProcessEventType(&pid, &type);
return {0x0, type, pid};
}
std::tuple<Result> ShellService::finalize_exited_process(u64 pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> proc = Registration::GetProcess(pid);
auto proc = Registration::GetProcess(pid);
if (proc == NULL) {
return {0x20F};
return 0x20F;
} else if (proc->state != ProcessState_Exited) {
return {0x60F};
return 0x60F;
} else {
Registration::FinalizeExitedProcess(proc);
return {0x0};
return 0x0;
}
}
std::tuple<Result> ShellService::clear_process_notification_flag(u64 pid) {
Result ShellService::ClearProcessNotificationFlag(u64 pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> proc = Registration::GetProcess(pid);
auto proc = Registration::GetProcess(pid);
if (proc != NULL) {
proc->flags &= ~PROCESSFLAGS_CRASHED;
return {0x0};
return 0x0;
} else {
return {0x20F};
return 0x20F;
}
}
std::tuple<Result> ShellService::notify_boot_finished() {
void ShellService::NotifyBootFinished() {
if (!g_has_boot_finished) {
g_has_boot_finished = true;
EmbeddedBoot2::Main();
}
return {0};
}
std::tuple<Result, u64> ShellService::get_application_process_id() {
Result ShellService::GetApplicationProcessId(Out<u64> pid) {
auto auto_lock = Registration::GetProcessListUniqueLock();
std::shared_ptr<Registration::Process> app_proc;
if (Registration::HasApplicationProcess(&app_proc)) {
return {0, app_proc->pid};
pid.SetValue(app_proc->pid);
return 0;
}
return {0x20F, 0};
return 0x20F;
}
std::tuple<Result> ShellService::boost_system_memory_resource_limit(u64 sysmem_size) {
if (!kernelAbove400()) {
return {0xF601};
}
/* TODO */
return {ResourceLimitUtils::BoostSystemMemoryResourceLimit(sysmem_size)};
Result ShellService::BoostSystemMemoryResourceLimit(u64 sysmem_size) {
return ResourceLimitUtils::BoostSystemMemoryResourceLimit(sysmem_size);
}

View file

@ -16,7 +16,7 @@
#pragma once
#include <switch.h>
#include <stratosphere/iserviceobject.hpp>
#include <stratosphere.hpp>
#include "pm_registration.hpp"
@ -46,26 +46,43 @@ enum ShellCmd_5X {
Shell_Cmd_5X_BoostSystemMemoryResourceLimit = 7
};
class ShellService final : public IServiceObject {
public:
Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) override;
Result handle_deferred() override;
ShellService *clone() override {
return new ShellService(*this);
}
class ShellService final : public IServiceObject {
private:
/* Actual commands. */
std::tuple<Result, u64> launch_process(u64 launch_flags, Registration::TidSid tid_sid);
std::tuple<Result> terminate_process_id(u64 pid);
std::tuple<Result> terminate_title_id(u64 tid);
std::tuple<Result, CopiedHandle> get_process_wait_event();
std::tuple<Result, u64, u64> get_process_event_type();
std::tuple<Result> finalize_exited_process(u64 pid);
std::tuple<Result> clear_process_notification_flag(u64 pid);
std::tuple<Result> notify_boot_finished();
std::tuple<Result, u64> get_application_process_id();
std::tuple<Result> boost_system_memory_resource_limit(u64 sysmem_size);
Result LaunchProcess(Out<u64> pid, u64 launch_flags, Registration::TidSid tid_sid);
Result TerminateProcessId(u64 pid);
Result TerminateTitleId(u64 tid);
void GetProcessWaitEvent(Out<CopiedHandle> event);
void GetProcessEventType(Out<u64> type, Out<u64> pid);
Result FinalizeExitedProcess(u64 pid);
Result ClearProcessNotificationFlag(u64 pid);
void NotifyBootFinished();
Result GetApplicationProcessId(Out<u64> pid);
Result BoostSystemMemoryResourceLimit(u64 sysmem_size);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0-4.0.0 */
MakeServiceCommandMeta<Shell_Cmd_LaunchProcess, &ShellService::LaunchProcess, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_TerminateProcessId, &ShellService::TerminateProcessId, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_TerminateTitleId, &ShellService::TerminateTitleId, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_GetProcessWaitEvent, &ShellService::GetProcessWaitEvent, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_GetProcessEventType, &ShellService::GetProcessEventType, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_FinalizeExitedProcess, &ShellService::FinalizeExitedProcess, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_ClearProcessNotificationFlag, &ShellService::ClearProcessNotificationFlag, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_NotifyBootFinished, &ShellService::NotifyBootFinished, FirmwareVersion_Min, FirmwareVersion_400>(),
MakeServiceCommandMeta<Shell_Cmd_GetApplicationProcessId, &ShellService::GetApplicationProcessId, FirmwareVersion_Min, FirmwareVersion_400>(),
/* 4.0.0-4.0.0 */
MakeServiceCommandMeta<Shell_Cmd_BoostSystemMemoryResourceLimit, &ShellService::BoostSystemMemoryResourceLimit, FirmwareVersion_400, FirmwareVersion_400>(),
/* 5.0.0-* */
MakeServiceCommandMeta<Shell_Cmd_5X_LaunchProcess, &ShellService::LaunchProcess, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_TerminateProcessId, &ShellService::TerminateProcessId, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_TerminateTitleId, &ShellService::TerminateTitleId, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_GetProcessWaitEvent, &ShellService::GetProcessWaitEvent, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_GetProcessEventType, &ShellService::GetProcessEventType, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_NotifyBootFinished, &ShellService::NotifyBootFinished, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_GetApplicationProcessId, &ShellService::GetApplicationProcessId, FirmwareVersion_500>(),
MakeServiceCommandMeta<Shell_Cmd_5X_BoostSystemMemoryResourceLimit, &ShellService::BoostSystemMemoryResourceLimit, FirmwareVersion_500>(),
};
};

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2018 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 <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <switch/arm/atomics.h>
#include "smm_ams.h"
static Service g_smManagerAmsSrv;
static u64 g_smManagerAmsRefcnt;
Result smManagerAmsInitialize(void) {
atomicIncrement64(&g_smManagerAmsRefcnt);
if (serviceIsActive(&g_smManagerAmsSrv))
return 0;
return smGetService(&g_smManagerAmsSrv, "sm:m");
}
void smManagerAmsExit(void) {
if (atomicDecrement64(&g_smManagerAmsRefcnt) == 0)
serviceClose(&g_smManagerAmsSrv);
}
Result smManagerAmsEndInitialDefers(void) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(&g_smManagerAmsSrv, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 65000;
Result rc = serviceIpcDispatch(&g_smManagerAmsSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(&g_smManagerAmsSrv, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
}
return rc;
}

View file

@ -1,21 +0,0 @@
/**
* @file smm_ams.h
* @brief Service manager (sm:m) IPC wrapper for Atmosphere extensions.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
Result smManagerAmsInitialize(void);
void smManagerAmsExit(void);
Result smManagerAmsEndInitialDefers(void);
#ifdef __cplusplus
}
#endif