361 lines
8.5 KiB
C++
361 lines
8.5 KiB
C++
/*
|
|
* \brief Unix emulation environment for Genode
|
|
* \author Josef Soentgen
|
|
* \date 2012-04-13
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2012 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
|
|
/* Genode includes */
|
|
#include <cap_session/connection.h>
|
|
#include <dataspace/client.h>
|
|
|
|
#include <lwip/genode.h>
|
|
|
|
/* Noux includes */
|
|
#include <child.h>
|
|
#include <socket_descriptor_registry.h>
|
|
#include <socket_io_channel.h>
|
|
#include <shared_pointer.h>
|
|
|
|
using namespace Noux;
|
|
|
|
/* Libc includes */
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
#include <netdb.h>
|
|
|
|
void (*libc_select_notify)();
|
|
void (*close_socket)(int);
|
|
|
|
/* set select() timeout to lwip's lowest possible value */
|
|
struct timeval timeout = { 0, 10000 };
|
|
|
|
|
|
/**
|
|
* This callback function is called from lwip via the libc_select_notify
|
|
* function pointer if an event occurs.
|
|
*/
|
|
|
|
static void select_notify()
|
|
{
|
|
fd_set readfds;
|
|
fd_set writefds;
|
|
fd_set exceptfds;
|
|
int ready;
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_ZERO(&writefds);
|
|
FD_ZERO(&exceptfds);
|
|
|
|
/* for now set each currently used socket descriptor true */
|
|
for (int sd = 0; sd < MAX_SOCKET_DESCRIPTORS; sd++) {
|
|
if (Socket_descriptor_registry<Socket_io_channel>::instance()->sd_in_use(sd)) {
|
|
int real_sd = Socket_descriptor_registry<Socket_io_channel>::instance()->io_channel_by_sd(sd)->get_socket();
|
|
|
|
FD_SET(real_sd, &readfds);
|
|
FD_SET(real_sd, &writefds);
|
|
FD_SET(real_sd, &exceptfds);
|
|
}
|
|
}
|
|
|
|
ready = ::select(MAX_SOCKET_DESCRIPTORS, &readfds, &writefds, &exceptfds, &timeout);
|
|
|
|
/* if any socket is ready for reading */
|
|
if (ready > 0) {
|
|
for (int sd = 0; sd < MAX_SOCKET_DESCRIPTORS; sd++) {
|
|
if (Socket_descriptor_registry<Socket_io_channel>::instance()->sd_in_use(sd)) {
|
|
int real_sd = Socket_descriptor_registry<Socket_io_channel>::instance()->io_channel_by_sd(sd)->get_socket();
|
|
|
|
if (FD_ISSET(real_sd, &readfds)
|
|
|| FD_ISSET(real_sd, &writefds)
|
|
|| FD_ISSET(real_sd, &exceptfds)) {
|
|
Shared_pointer<Socket_io_channel> sio = Socket_descriptor_registry<Socket_io_channel>::instance()->io_channel_by_sd(sd);
|
|
|
|
if (FD_ISSET(real_sd, &readfds))
|
|
sio->set_unblock(true, false, false);
|
|
if (FD_ISSET(real_sd, &writefds))
|
|
sio->set_unblock(false, true, false);
|
|
if (FD_ISSET(real_sd, &exceptfds))
|
|
sio->set_unblock(false, false, true);
|
|
|
|
sio->invoke_all_notifiers();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void _close_socket(int sd)
|
|
{
|
|
Socket_descriptor_registry<Socket_io_channel>::instance()->remove_io_channel(sd);
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialise the network subsystem by directly using lwip
|
|
*/
|
|
|
|
void init_network()
|
|
{
|
|
lwip_tcpip_init();
|
|
lwip_nic_init(0, 0, 0);
|
|
|
|
if (!libc_select_notify)
|
|
libc_select_notify = select_notify;
|
|
|
|
if (!close_socket)
|
|
close_socket = _close_socket;
|
|
}
|
|
|
|
/*********************************
|
|
** Noux net syscall dispatcher **
|
|
*********************************/
|
|
|
|
#define GET_SOCKET_IO_CHANNEL(fd, handle) \
|
|
Shared_pointer<Io_channel> io = _lookup_channel(fd); \
|
|
Shared_pointer<Socket_io_channel> handle = \
|
|
io.dynamic_pointer_cast<Socket_io_channel>();
|
|
|
|
bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
|
{
|
|
switch (sc) {
|
|
/**
|
|
* Keep compiler from complaining
|
|
*/
|
|
case SYSCALL_GETCWD:
|
|
case SYSCALL_WRITE:
|
|
case SYSCALL_READ:
|
|
case SYSCALL_STAT:
|
|
case SYSCALL_LSTAT:
|
|
case SYSCALL_FSTAT:
|
|
case SYSCALL_FCNTL:
|
|
case SYSCALL_OPEN:
|
|
case SYSCALL_CLOSE:
|
|
case SYSCALL_IOCTL:
|
|
case SYSCALL_LSEEK:
|
|
case SYSCALL_DIRENT:
|
|
case SYSCALL_FCHDIR:
|
|
case SYSCALL_EXECVE:
|
|
case SYSCALL_SELECT:
|
|
case SYSCALL_FORK:
|
|
case SYSCALL_GETPID:
|
|
case SYSCALL_WAIT4:
|
|
case SYSCALL_PIPE:
|
|
case SYSCALL_DUP2:
|
|
case SYSCALL_INVALID:
|
|
case SYSCALL_UNLINK:
|
|
case SYSCALL_RENAME:
|
|
case SYSCALL_MKDIR:
|
|
break;
|
|
case SYSCALL_SOCKET:
|
|
{
|
|
Socket_io_channel *socket_io_channel = new Socket_io_channel();
|
|
|
|
if (!socket_io_channel->socket(_sysio)) {
|
|
delete socket_io_channel;
|
|
return false;
|
|
}
|
|
|
|
Shared_pointer<Io_channel> io_channel(socket_io_channel, Genode::env()->heap());
|
|
|
|
_sysio->socket_out.fd = add_io_channel(io_channel);
|
|
|
|
/* add socket to registry */
|
|
Socket_descriptor_registry<Socket_io_channel>::instance()->add_io_channel(io_channel.dynamic_pointer_cast<Socket_io_channel>(),
|
|
_sysio->socket_out.fd);
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_GETSOCKOPT:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->getsockopt_in.fd, socket_io_channel)
|
|
|
|
if (!socket_io_channel->getsockopt(_sysio))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_SETSOCKOPT:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->setsockopt_in.fd, socket_io_channel)
|
|
|
|
if (!socket_io_channel->setsockopt(_sysio)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_ACCEPT:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->accept_in.fd, socket_io_channel)
|
|
|
|
int new_socket = socket_io_channel->accept(_sysio);
|
|
if (new_socket == -1)
|
|
return false;
|
|
|
|
Socket_io_channel *new_socket_io_channel = new Socket_io_channel(new_socket);
|
|
Shared_pointer<Io_channel> channel(new_socket_io_channel, Genode::env()->heap());
|
|
|
|
_sysio->accept_out.fd = add_io_channel(channel);
|
|
|
|
/* add new socket to registry */
|
|
Socket_descriptor_registry<Socket_io_channel>::instance()->add_io_channel(channel.dynamic_pointer_cast<Socket_io_channel>(),
|
|
_sysio->accept_out.fd);
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_BIND:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->bind_in.fd, socket_io_channel)
|
|
|
|
if (socket_io_channel->bind(_sysio) == -1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_LISTEN:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->listen_in.fd, socket_io_channel)
|
|
|
|
if (socket_io_channel->listen(_sysio) == -1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_SEND:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->send_in.fd, socket_io_channel)
|
|
|
|
ssize_t len = socket_io_channel->send(_sysio);
|
|
|
|
if (len == -1)
|
|
return false;
|
|
|
|
_sysio->send_out.len = len;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_SENDTO:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->sendto_in.fd, socket_io_channel)
|
|
|
|
ssize_t len = socket_io_channel->sendto(_sysio);
|
|
|
|
if (len == -1)
|
|
return false;
|
|
|
|
_sysio->sendto_out.len = len;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_RECV:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->recv_in.fd, socket_io_channel)
|
|
|
|
ssize_t len = socket_io_channel->recv(_sysio);
|
|
|
|
if (len == -1)
|
|
return false;
|
|
|
|
_sysio->recv_out.len = len;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_GETPEERNAME:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->getpeername_in.fd, socket_io_channel)
|
|
|
|
int res = socket_io_channel->getpeername(_sysio);
|
|
|
|
if (res == -1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_SHUTDOWN:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->shutdown_in.fd, socket_io_channel)
|
|
|
|
int res = socket_io_channel->shutdown(_sysio);
|
|
|
|
if (res == -1)
|
|
return false;
|
|
|
|
/* remove sd from registry */
|
|
_close_socket(_sysio->shutdown_in.fd);
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_CONNECT:
|
|
{
|
|
GET_SOCKET_IO_CHANNEL(_sysio->connect_in.fd, socket_io_channel);
|
|
|
|
int res = socket_io_channel->connect(_sysio);
|
|
|
|
if (res == -1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
case SYSCALL_GETADDRINFO:
|
|
{
|
|
#if 0
|
|
struct addrinfo *result, *rp = NULL;
|
|
|
|
int res = lwip_getaddrinfo(_sysio->getaddrinfo_in.hostname,
|
|
_sysio->getaddrinfo_in.servname,
|
|
(const struct addrinfo *)&_sysio->getaddrinfo_in.hints,
|
|
&result);
|
|
|
|
if (res != 0) {
|
|
PERR("::getaddrinfo() returns %d", res);
|
|
return false;
|
|
}
|
|
|
|
PINF("SYSCALL_GETADDRINFO: deep-copy");
|
|
/* wipe-out old state */
|
|
memset(_sysio->getaddrinfo_in.res, 0, sizeof (_sysio->getaddrinfo_in.res));
|
|
|
|
int i = 0; rp = result;
|
|
while (i < Noux::Sysio::MAX_ADDRINFO_RESULTS && rp != NULL) {
|
|
memcpy(&_sysio->getaddrinfo_in.res[i].addrinfo, rp, sizeof (struct addrinfo));
|
|
if (rp->ai_addr) {
|
|
memcpy(&_sysio->getaddrinfo_in.res[i].ai_addr, rp->ai_addr, sizeof (struct sockaddr));
|
|
}
|
|
else
|
|
memset(&_sysio->getaddrinfo_in.res[i].ai_addr, 0, sizeof (struct sockaddr));
|
|
|
|
if (rp->ai_canonname) {
|
|
memcpy(&_sysio->getaddrinfo_in.res[i].ai_canonname,
|
|
rp->ai_canonname, strlen(rp->ai_canonname));
|
|
PINF("kopiere canonname: '%s'", rp->ai_canonname);
|
|
}
|
|
else
|
|
memset(&_sysio->getaddrinfo_in.res[i].ai_canonname, 0,
|
|
sizeof (_sysio->getaddrinfo_in.res[i].ai_canonname));
|
|
|
|
i++; rp = rp->ai_next;
|
|
}
|
|
|
|
_sysio->getaddrinfo_out.addr_num = i;
|
|
PINF("SYSCALL_GETADDRINFO: deep-copy successfull");
|
|
|
|
lwip_freeaddrinfo(result);
|
|
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|