
Prior to system version 11.0, the kernel filled the resulting handle with junk in case of failure, when calling svcConnectToPort, etc. In some situations libctru could accidentally close valid handles.
328 lines
7.2 KiB
C
328 lines
7.2 KiB
C
/*
|
|
srv.c _ Service manager.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <3ds/types.h>
|
|
#include <3ds/result.h>
|
|
#include <3ds/srv.h>
|
|
#include <3ds/svc.h>
|
|
#include <3ds/ipc.h>
|
|
#include <3ds/synchronization.h>
|
|
#include <3ds/env.h>
|
|
|
|
#include <3ds/os.h>
|
|
#include <3ds/services/srvpm.h>
|
|
|
|
#include "internal.h"
|
|
|
|
static Handle srvHandle;
|
|
static int srvRefCount;
|
|
|
|
static bool srvGetBlockingPolicy(void)
|
|
{
|
|
ThreadVars *tv = getThreadVars();
|
|
return tv->magic == THREADVARS_MAGIC && tv->srv_blocking_policy;
|
|
}
|
|
|
|
Result srvInit(void)
|
|
{
|
|
Result rc = 0;
|
|
|
|
if (AtomicPostIncrement(&srvRefCount)) return 0;
|
|
|
|
if(GET_VERSION_MINOR(osGetKernelVersion()) < 39 && *srvPmGetSessionHandle() != 0)
|
|
rc = svcDuplicateHandle(&srvHandle, *srvPmGetSessionHandle()); // Prior to system version 7.0 srv:pm was a superset of srv:
|
|
else
|
|
rc = svcConnectToPort(&srvHandle, "srv:");
|
|
|
|
if (R_SUCCEEDED(rc))
|
|
rc = srvRegisterClient();
|
|
else
|
|
// Prior to system version 11.0, the kernel filled the resulting handle with junk in case of failure
|
|
srvHandle = 0;
|
|
|
|
if (R_FAILED(rc)) srvExit();
|
|
return rc;
|
|
}
|
|
|
|
void srvExit(void)
|
|
{
|
|
if (AtomicDecrement(&srvRefCount)) return;
|
|
|
|
if (srvHandle != 0) svcCloseHandle(srvHandle);
|
|
srvHandle = 0;
|
|
}
|
|
|
|
void srvSetBlockingPolicy(bool nonBlocking)
|
|
{
|
|
ThreadVars *tv = getThreadVars();
|
|
tv->srv_blocking_policy = nonBlocking;
|
|
}
|
|
|
|
Handle *srvGetSessionHandle(void)
|
|
{
|
|
return &srvHandle;
|
|
}
|
|
|
|
Result srvGetServiceHandle(Handle* out, const char* name)
|
|
{
|
|
/* Look in service-list given to us by loader. If we find find a match,
|
|
we return it. */
|
|
Handle h = envGetHandle(name);
|
|
|
|
if(h != 0) {
|
|
return svcDuplicateHandle(out, h);
|
|
}
|
|
|
|
/* Normal request to service manager. */
|
|
return srvGetServiceHandleDirect(out, name);
|
|
}
|
|
|
|
Result srvRegisterClient(void)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x1,0,2); // 0x10002
|
|
cmdbuf[1] = IPC_Desc_CurProcessId();
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvEnableNotification(Handle* semaphoreOut)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
if(semaphoreOut) *semaphoreOut = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvRegisterService(Handle* out, const char* name, int maxSessions)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x3,4,0); // 0x30100
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
cmdbuf[4] = maxSessions;
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvUnregisterService(const char* name)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x4,3,0); // 0x400C0
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvGetServiceHandleDirect(Handle* out, const char* name)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
cmdbuf[4] = (u32)srvGetBlockingPolicy(); // per-thread setting, default is blocking
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvRegisterPort(const char* name, Handle clientHandle)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x6,3,2); // 0x600C2
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
cmdbuf[4] = IPC_Desc_SharedHandles(1);
|
|
cmdbuf[5] = clientHandle;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvUnregisterPort(const char* name)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x7,3,0); // 0x700C0
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvGetPort(Handle* out, const char* name)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x8,4,0); // 0x80100
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
cmdbuf[4] = 0x0;
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
if(out) *out = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvWaitForPortRegistered(const char* name)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x8,4,0); // 0x80100
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
cmdbuf[4] = 0x1;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvSubscribe(u32 notificationId)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x9,1,0); // 0x90040
|
|
cmdbuf[1] = notificationId;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvUnsubscribe(u32 notificationId)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0xA,1,0); // 0xA0040
|
|
cmdbuf[1] = notificationId;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvReceiveNotification(u32* notificationIdOut)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
if(notificationIdOut) *notificationIdOut = R_SUCCEEDED(rc) ? cmdbuf[2] : 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvPublishToSubscriber(u32 notificationId, u32 flags)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0xC,2,0); // 0xC0080
|
|
cmdbuf[1] = notificationId;
|
|
cmdbuf[2] = flags;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvPublishAndGetSubscriber(u32* processIdCountOut, u32* processIdsOut, u32 notificationId)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0xD,1,0); // 0xD0040
|
|
cmdbuf[1] = notificationId;
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
|
|
if (R_SUCCEEDED(rc))
|
|
{
|
|
if(processIdCountOut) *processIdCountOut = cmdbuf[2];
|
|
if(processIdsOut) memcpy(processIdsOut, &cmdbuf[3], cmdbuf[2] * sizeof(u32));
|
|
}
|
|
else if(processIdCountOut)
|
|
*processIdCountOut = 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvIsServiceRegistered(bool* registeredOut, const char* name)
|
|
{
|
|
Result rc = 0;
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0xE,3,0); // 0xE00C0
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strnlen(name, 8);
|
|
|
|
rc = svcSendSyncRequest(srvHandle);
|
|
rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc;
|
|
|
|
if(registeredOut) *registeredOut = R_SUCCEEDED(rc) && (cmdbuf[2] & 0xFF) != 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvIsPortRegistered(bool* registeredOut, const char* name)
|
|
{
|
|
Handle port;
|
|
Result rc = srvGetPort(&port, name);
|
|
|
|
if(rc == 0xD8801BFA)
|
|
{
|
|
if(registeredOut) *registeredOut = false;
|
|
return 0;
|
|
}
|
|
else if(R_SUCCEEDED(rc))
|
|
{
|
|
if(registeredOut) *registeredOut = true;
|
|
svcCloseHandle(port);
|
|
}
|
|
return rc;
|
|
}
|