libnx/nx/source/services/bsd.c
friedkeenan da4c060278 Add a bunch of GetServiceSession functions (#305)
* Add a bunch of GetServiceSession functions
2019-07-13 15:09:28 -04:00

854 lines
20 KiB
C

// Copyright 2017 plutoo
#include <errno.h>
#include <string.h>
// Complete definition of struct timeout:
#include <sys/time.h>
#include <fcntl.h>
// For ioctls:
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include "types.h"
#include "result.h"
#include "kernel/ipc.h"
#include "kernel/shmem.h"
#include "kernel/rwlock.h"
#include "services/bsd.h"
#include "services/sm.h"
__thread Result g_bsdResult;
__thread int g_bsdErrno;
static Service g_bsdSrv;
static size_t g_bsdSrvIpcBufferSize;
static Service g_bsdMonitor;
static u64 g_bsdClientPid = -1;
static TransferMemory g_bsdTmem;
static const BsdInitConfig g_defaultBsdInitConfig = {
.version = 1,
.tcp_tx_buf_size = 0x8000,
.tcp_rx_buf_size = 0x10000,
.tcp_tx_buf_max_size = 0x40000,
.tcp_rx_buf_max_size = 0x40000,
.udp_tx_buf_size = 0x2400,
.udp_rx_buf_size = 0xA500,
.sb_efficiency = 4,
};
/*
This function computes the minimal size of the transfer memory to be passed to @ref bsdInitalize.
Should the transfer memory be smaller than that, the BSD sockets service would only send
ZeroWindow packets (for TCP), resulting in a transfer rate not exceeding 1 byte/s.
*/
static size_t _bsdGetTransferMemSizeForConfig(const BsdInitConfig *config) {
u32 tcp_tx_buf_max_size = config->tcp_tx_buf_max_size != 0 ? config->tcp_tx_buf_max_size : config->tcp_tx_buf_size;
u32 tcp_rx_buf_max_size = config->tcp_rx_buf_max_size != 0 ? config->tcp_rx_buf_max_size : config->tcp_rx_buf_size;
u32 sum = tcp_tx_buf_max_size + tcp_rx_buf_max_size + config->udp_tx_buf_size + config->udp_rx_buf_size;
sum = ((sum + 0xFFF) >> 12) << 12; // page round-up
return (size_t)(config->sb_efficiency * sum);
}
static Result _bsdRegisterClient(Service* srv, TransferMemory* tmem, const BsdInitConfig *config, u64* pid_out) {
IpcCommand c;
ipcInitialize(&c);
ipcSendPid(&c);
ipcSendHandleCopy(&c, tmem->handle);
struct {
u64 magic;
u64 cmd_id;
BsdInitConfig config;
u64 pid_reserved;
u64 tmem_sz;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 0;
raw->config = *config;
raw->pid_reserved = 0;
raw->tmem_sz = tmem->size;
Result rc = serviceIpcDispatch(srv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
u64 pid;
} *resp = r.Raw;
*pid_out = resp->pid;
g_bsdResult = rc = resp->result;
g_bsdErrno = 0;
}
return rc;
}
static Result _bsdStartMonitor(Service* srv, u64 pid) {
IpcCommand c;
ipcInitialize(&c);
ipcSendPid(&c);
struct {
u64 magic;
u64 cmd_id;
u64 pid;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 1;
raw->pid = pid;
Result rc = serviceIpcDispatch(srv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
g_bsdResult = rc = resp->result;
g_bsdErrno = 0;
}
return rc;
}
typedef struct {
u64 magic;
u64 result;
int ret;
int errno_;
} BsdIpcResponseBase;
static int _bsdDispatchBasicCommand(IpcCommand *c, IpcParsedCommand *rOut) {
Result rc = serviceIpcDispatch(&g_bsdSrv);
IpcParsedCommand r;
int ret = -1;
if (R_SUCCEEDED(rc)) {
ipcParse(&r);
BsdIpcResponseBase *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
ret = resp->ret;
g_bsdErrno = (ret < 0) ? resp->errno_ : 0;
}
}
if (R_FAILED(rc))
g_bsdErrno = -1;
if(rOut != NULL)
*rOut = r;
g_bsdResult = rc;
return ret;
}
static int _bsdDispatchCommandWithOutAddrlen(IpcCommand *c, socklen_t *addrlen) {
IpcParsedCommand r;
int ret = _bsdDispatchBasicCommand(c, &r);
if(ret != -1 && addrlen != NULL) {
struct {
BsdIpcResponseBase bsd_resp;
socklen_t addrlen;
} *resp = r.Raw;
*addrlen = resp->addrlen;
}
return ret;
}
static int _bsdNameGetterCommand(u32 cmd_id, int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
IpcCommand c;
ipcInitialize(&c);
socklen_t maxaddrlen = addrlen == NULL ? 0 : *addrlen;
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, addr, maxaddrlen, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
} PACKED *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = cmd_id;
raw->sockfd = sockfd;
return _bsdDispatchCommandWithOutAddrlen(&c, addrlen);
}
static int _bsdSocketCreationCommand(u32 cmd_id, int domain, int type, int protocol) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int domain;
int type;
int protocol;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = cmd_id;
raw->domain = domain;
raw->type = type;
raw->protocol = protocol;
return _bsdDispatchBasicCommand(&c, NULL);
}
const BsdInitConfig *bsdGetDefaultInitConfig(void) {
return &g_defaultBsdInitConfig;
}
Result bsdInitialize(const BsdInitConfig *config) {
const char* bsd_srv = "bsd:s";
if(serviceIsActive(&g_bsdSrv) || serviceIsActive(&g_bsdMonitor))
return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
Result rc = smGetService(&g_bsdSrv, bsd_srv);
if (R_FAILED(rc)) {
bsd_srv = "bsd:u";
rc = smGetService(&g_bsdSrv, bsd_srv);
}
if(R_FAILED(rc)) goto error;
rc = ipcQueryPointerBufferSize(g_bsdSrv.handle, &g_bsdSrvIpcBufferSize);
if(R_FAILED(rc)) goto error;
rc = smGetService(&g_bsdMonitor, bsd_srv);
if(R_FAILED(rc)) goto error;
rc = tmemCreate(&g_bsdTmem, _bsdGetTransferMemSizeForConfig(config), 0);
if(R_FAILED(rc)) goto error;
rc = _bsdRegisterClient(&g_bsdSrv, &g_bsdTmem, config, &g_bsdClientPid);
if(R_FAILED(rc)) goto error;
rc = _bsdStartMonitor(&g_bsdMonitor, g_bsdClientPid);
if(R_FAILED(rc)) goto error;
return rc;
error:
bsdExit();
return rc;
}
void bsdExit(void) {
g_bsdSrvIpcBufferSize = 0;
g_bsdClientPid = 0;
serviceClose(&g_bsdMonitor);
serviceClose(&g_bsdSrv);
tmemClose(&g_bsdTmem);
}
Service* bsdGetServiceSession(void) {
return &g_bsdSrv;
}
int bsdSocket(int domain, int type, int protocol) {
return _bsdSocketCreationCommand(2, domain, type, protocol);
}
int bsdSocketExempt(int domain, int type, int protocol) {
return _bsdSocketCreationCommand(3, domain, type, protocol);
}
int bsdOpen(const char *pathname, int flags) {
IpcCommand c;
ipcInitialize(&c);
size_t pathlen = strlen(pathname) + 1;
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, pathname, pathlen, 0);
struct {
u64 magic;
u64 cmd_id;
int flags;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 4;
raw->flags = flags;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdSelect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, readfds, readfds == NULL ? 0 : sizeof(fd_set), 0);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, writefds, writefds == NULL ? 0 : sizeof(fd_set), 1);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 2);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 3);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, readfds, readfds == NULL ? 0 : sizeof(fd_set), 0);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, writefds, writefds == NULL ? 0 : sizeof(fd_set), 1);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 2);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 3);
struct {
u64 magic;
u64 cmd_id;
int nfds;
struct timeval timeout;
bool nullTimeout;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 5;
raw->nfds = nfds;
if(!(raw->nullTimeout = timeout == NULL))
raw->timeout = *timeout;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdPoll(struct pollfd *fds, nfds_t nfds, int timeout) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, fds, nfds * sizeof(struct pollfd), 0);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, fds, nfds * sizeof(struct pollfd), 0);
struct {
u64 magic;
u64 cmd_id;
nfds_t nfds;
int timeout;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 6;
raw->nfds = nfds;
raw->timeout = timeout;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdSysctl(const int *name, unsigned int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) {
IpcCommand c;
size_t inlen = oldlenp == NULL ? 0 : *oldlenp;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, name, 4 * namelen, 0);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, newp, newlen, 1);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, oldp, inlen, 0);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 7;
IpcParsedCommand r;
int ret = _bsdDispatchBasicCommand(&c, &r);
if(ret != -1 && oldlenp != NULL) {
struct {
BsdIpcResponseBase bsd_resp;
size_t oldlenp;
} *resp = r.Raw;
*oldlenp = resp->oldlenp;
}
return ret;
}
ssize_t bsdRecv(int sockfd, void *buf, size_t len, int flags) {
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, buf, len, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int flags;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 8;
raw->sockfd = sockfd;
raw->flags = flags;
return _bsdDispatchBasicCommand(&c, NULL);
}
ssize_t bsdRecvFrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen){
IpcCommand c;
socklen_t inaddrlen = addrlen == NULL ? 0 : *addrlen;
ipcInitialize(&c);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, buf, len, 0);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, src_addr, inaddrlen, 1);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int flags;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 9;
raw->sockfd = sockfd;
raw->flags = flags;
return _bsdDispatchCommandWithOutAddrlen(&c, addrlen);
}
ssize_t bsdSend(int sockfd, const void* buf, size_t len, int flags) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, buf, len, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int flags;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 10;
raw->sockfd = sockfd;
raw->flags = flags;
return _bsdDispatchBasicCommand(&c, NULL);
}
ssize_t bsdSendTo(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, buf, len, 0);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, dest_addr, addrlen, 1);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int flags;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 11;
raw->sockfd = sockfd;
raw->flags = flags;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdAccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
return _bsdNameGetterCommand(12, sockfd, addr, addrlen);
}
int bsdBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, addr, addrlen, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 13;
raw->sockfd = sockfd;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdConnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, addr, addrlen, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 14;
raw->sockfd = sockfd;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdGetPeerName(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
return _bsdNameGetterCommand(15, sockfd, addr, addrlen);
}
int bsdGetSockName(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
return _bsdNameGetterCommand(16, sockfd, addr, addrlen);
}
int bsdGetSockOpt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
IpcCommand c;
ipcInitialize(&c);
socklen_t inoptlen = optlen == NULL ? 0 : *optlen;
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, optval, inoptlen, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int level;
int optname;
} PACKED *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 17;
raw->sockfd = sockfd;
raw->level = level;
raw->optname = optname;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdListen(int sockfd, int backlog) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int backlog;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 18;
raw->sockfd = sockfd;
raw->backlog = backlog;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdIoctl(int fd, int request, void *data) {
IpcCommand c;
const void *in1 = NULL, *in2 = NULL, *in3 = NULL, *in4 = NULL;
size_t in1sz = 0, in2sz = 0, in3sz = 0, in4sz = 0;
void *out1 = NULL, *out2 = NULL, *out3 = NULL, *out4 = NULL;
size_t out1sz = 0, out2sz = 0, out3sz = 0, out4sz = 0;
int bufcount = 1;
switch(request) {
case SIOCGIFCONF: {
struct ifconf *data_ = (struct ifconf *)data;
in1 = out1 = data;
in1sz = out1sz = sizeof(struct ifconf);
in2 = out2 = data_->ifc_req;
in2sz = out2sz = data_->ifc_len;
bufcount = 2;
break;
}
case SIOCGIFMEDIA:
case SIOCGIFXMEDIA: {
struct ifmediareq *data_ = (struct ifmediareq *)data;
in1 = out1 = data;
in1sz = out1sz = sizeof(struct ifmediareq);
in2 = out2 = data_->ifm_ulist;
in2sz = out2sz = 8 * data_->ifm_count;
bufcount = 2;
break;
}
// Generic ioctl
default: {
void *data_ = NULL;
if(request & IOC_INOUT)
data_ = data;
if(request & IOC_IN) {
in1 = data_;
in1sz = IOCPARM_LEN(request);
}
if(request & IOC_OUT) {
out1 = data_;
out1sz = IOCPARM_LEN(request);
}
break;
}
}
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, in1, in1sz, 0);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, in2, in2sz, 1);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, in3, in3sz, 2);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, in4, in4sz, 3);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, out1, out1sz, 0);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, out2, out2sz, 1);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, out3, out3sz, 2);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, out4, out4sz, 3);
struct {
u64 magic;
u64 cmd_id;
int fd;
int request;
int bufcount;
} PACKED *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 19;
raw->fd = fd;
raw->request = request;
raw->bufcount = bufcount;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdFcntl(int fd, int cmd, int flags) {
IpcCommand c;
if(cmd != F_GETFL && cmd != F_SETFL) {
g_bsdResult = 0;
g_bsdErrno = EOPNOTSUPP;
return -1;
}
if(cmd == F_GETFL)
flags = 0;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int fd;
int cmd;
int flags;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 20;
raw->fd = fd;
raw->cmd = cmd;
raw->flags = flags;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdSetSockOpt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, optval, optlen, 0);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int level;
int optname;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 21;
raw->sockfd = sockfd;
raw->level = level;
raw->optname = optname;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdShutdown(int sockfd, int how) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
int how;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 22;
raw->sockfd = sockfd;
raw->how = how;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdShutdownAllSockets(int how) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int how;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 23;
raw->how = how;
return _bsdDispatchBasicCommand(&c, NULL);
}
ssize_t bsdWrite(int fd, const void *buf, size_t count) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendSmart(&c, g_bsdSrvIpcBufferSize, buf, count, 0);
struct {
u64 magic;
u64 cmd_id;
int fd;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 24;
raw->fd = fd;
return _bsdDispatchBasicCommand(&c, NULL);
}
ssize_t bsdRead(int fd, void *buf, size_t count) {
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvSmart(&c, g_bsdSrvIpcBufferSize, buf, count, 0);
struct {
u64 magic;
u64 cmd_id;
int fd;
} PACKED *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 25;
raw->fd = fd;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdClose(int fd) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int fd;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 26;
raw->fd = fd;
return _bsdDispatchBasicCommand(&c, NULL);
}
int bsdDuplicateSocket(int sockfd) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
int sockfd;
u64 reserved;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 27;
raw->sockfd = sockfd;
raw->reserved = 0;
return _bsdDispatchBasicCommand(&c, NULL);
}