diff --git a/ports/include/noux_session/noux_session.h b/ports/include/noux_session/noux_session.h index caa46564f..f9050075c 100644 --- a/ports/include/noux_session/noux_session.h +++ b/ports/include/noux_session/noux_session.h @@ -57,6 +57,19 @@ namespace Noux { SYSCALL_UNLINK, SYSCALL_RENAME, SYSCALL_MKDIR, + SYSCALL_SOCKET, + SYSCALL_GETSOCKOPT, + SYSCALL_SETSOCKOPT, + SYSCALL_ACCEPT, + SYSCALL_BIND, + SYSCALL_LISTEN, + SYSCALL_SEND, + SYSCALL_SENDTO, + SYSCALL_RECV, + SYSCALL_GETPEERNAME, + SYSCALL_SHUTDOWN, + SYSCALL_CONNECT, + SYSCALL_GETADDRINFO, SYSCALL_INVALID = -1 }; @@ -86,6 +99,19 @@ namespace Noux { NOUX_DECL_SYSCALL_NAME(UNLINK) NOUX_DECL_SYSCALL_NAME(RENAME) NOUX_DECL_SYSCALL_NAME(MKDIR) + NOUX_DECL_SYSCALL_NAME(SOCKET) + NOUX_DECL_SYSCALL_NAME(GETSOCKOPT) + NOUX_DECL_SYSCALL_NAME(SETSOCKOPT) + NOUX_DECL_SYSCALL_NAME(ACCEPT) + NOUX_DECL_SYSCALL_NAME(BIND) + NOUX_DECL_SYSCALL_NAME(LISTEN) + NOUX_DECL_SYSCALL_NAME(SEND) + NOUX_DECL_SYSCALL_NAME(SENDTO) + NOUX_DECL_SYSCALL_NAME(RECV) + NOUX_DECL_SYSCALL_NAME(GETPEERNAME) + NOUX_DECL_SYSCALL_NAME(SHUTDOWN) + NOUX_DECL_SYSCALL_NAME(CONNECT) + NOUX_DECL_SYSCALL_NAME(GETADDRINFO) case SYSCALL_INVALID: return 0; } return 0; diff --git a/ports/include/noux_session/sysio.h b/ports/include/noux_session/sysio.h index 543a6794c..d95de1c21 100644 --- a/ports/include/noux_session/sysio.h +++ b/ports/include/noux_session/sysio.h @@ -57,6 +57,7 @@ namespace Noux { typedef char Env[ENV_MAX_LEN]; typedef __SIZE_TYPE__ size_t; + typedef long int ssize_t; /** * Flags of 'mode' argument of open syscall @@ -132,6 +133,7 @@ namespace Noux { enum Fcntl_cmd { FCNTL_CMD_GET_FILE_STATUS_FLAGS, + FCNTL_CMD_SET_FILE_STATUS_FLAGS, FCNTL_CMD_SET_FD_FLAGS }; @@ -218,6 +220,56 @@ namespace Noux { bool zero() const { return (sec == 0) && (usec == 0); } }; + /** + * Socket related structures + */ + enum { MAX_HOSTNAME_LEN = 255 }; + typedef char Hostname[MAX_HOSTNAME_LEN]; + + enum { MAX_SERVNAME_LEN = 255 }; + typedef char Servname[MAX_SERVNAME_LEN]; + + enum { MAX_ADDRINFO_RESULTS = 4 }; + + struct in_addr + { + unsigned int s_addr; + }; + + struct sockaddr + { + unsigned char sa_len; + unsigned char sa_family; + char sa_data[14]; + }; + + struct sockaddr_in { + unsigned char sin_len; + unsigned char sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; + }; + + typedef unsigned socklen_t; + + struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; + }; + + struct Addrinfo { + struct addrinfo addrinfo; + struct sockaddr ai_addr; + char ai_canonname[255]; + }; + enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS }; enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS }; enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS }; @@ -292,7 +344,50 @@ namespace Noux { SYSIO_DECL(rename, { Path from_path; Path to_path; }, { }); - SYSIO_DECL(mkdir, { Path path; int mode; }, { }); + SYSIO_DECL(mkdir, { Path path; int mode; }, { }); + + SYSIO_DECL(socket, { int domain; int type; int protocol; }, + { int fd; }); + + /* XXX for now abuse Chunk for passing optval */ + SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval; + socklen_t optlen; }, { int result; }); + + SYSIO_DECL(setsockopt, { int fd; int level; + int optname; Chunk optval; + socklen_t optlen; }, { }); + + SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; }, + { int fd; }); + + SYSIO_DECL(bind, { int fd; struct sockaddr addr; + socklen_t addrlen; }, { int result; }); + + SYSIO_DECL(getpeername, { int fd; struct sockaddr addr; + socklen_t addrlen; }, { }); + + SYSIO_DECL(listen, { int fd; int type; int backlog; }, + { int result; }); + + SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; }, + { ssize_t len; }); + + SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags; + struct sockaddr dest_addr; socklen_t addrlen; }, + { ssize_t len; }); + + SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; }, + { size_t len; }); + + SYSIO_DECL(shutdown, { int fd; int how; }, { }); + + SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; }, + { int result; }); + + SYSIO_DECL(getaddrinfo, { Hostname hostname; Servname servname; + Addrinfo hints; + Addrinfo res[MAX_ADDRINFO_RESULTS]; }, + { int addr_num; }); }; }; }; diff --git a/ports/run/noux.run b/ports/run/noux.run index 8a9e411ba..5882d84ac 100644 --- a/ports/run/noux.run +++ b/ports/run/noux.run @@ -7,7 +7,7 @@ if {[have_spec linux]} { exit 0 } -build "core init drivers/timer drivers/uart noux lib/libc_noux noux-pkg/coreutils" +build "core init drivers/timer drivers/uart noux/minimal lib/libc_noux noux-pkg/coreutils" # strip coreutils binaries and create tar archive exec sh -c "[cross_dev_prefix]strip bin/coreutils/bin/*" diff --git a/ports/run/noux_bash.run b/ports/run/noux_bash.run index f34209d28..abfdcb4d7 100644 --- a/ports/run/noux_bash.run +++ b/ports/run/noux_bash.run @@ -10,7 +10,7 @@ if {![have_spec x86]} { #exec rm -rf noux-pkg/bash bin/bash set build_components { - core init drivers/timer noux lib/libc_noux + core init drivers/timer noux/minimal lib/libc_noux drivers/framebuffer drivers/pci drivers/input server/terminal server/ram_fs test/libports/ncurses diff --git a/ports/run/noux_fork.run b/ports/run/noux_fork.run index d78386694..05168a3b3 100644 --- a/ports/run/noux_fork.run +++ b/ports/run/noux_fork.run @@ -7,7 +7,7 @@ if {[have_spec linux]} { exit 0 } -build "core init drivers/timer drivers/uart noux lib/libc_noux test/noux_fork" +build "core init drivers/timer drivers/uart noux/minimal lib/libc_noux test/noux_fork" # strip coreutils binaries and create tar archive exec tar cfv bin/noux_fork.tar -h -C bin test-noux_fork diff --git a/ports/run/noux_tool_chain.run b/ports/run/noux_tool_chain.run index a95bc63e3..099f0efda 100644 --- a/ports/run/noux_tool_chain.run +++ b/ports/run/noux_tool_chain.run @@ -4,7 +4,7 @@ if {![have_spec x86]} { } set build_components { - core init drivers/timer noux lib/libc_noux + core init drivers/timer noux/minimal lib/libc_noux drivers/framebuffer drivers/pci drivers/input server/terminal server/ram_fs test/libports/ncurses diff --git a/ports/run/noux_vim.run b/ports/run/noux_vim.run index 9c9eefbf4..59fcfa570 100644 --- a/ports/run/noux_vim.run +++ b/ports/run/noux_vim.run @@ -10,7 +10,7 @@ if {![have_spec x86]} { #exec rm -rf noux-pkg/vim bin/vim set build_components { - core init drivers/timer noux lib/libc_noux + core init drivers/timer noux/minimal lib/libc_noux drivers/framebuffer drivers/pci drivers/input server/terminal } diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index ab051d763..160a09ea0 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -540,6 +540,10 @@ namespace { bool supports_unlink(char const *) { return true; } bool supports_rename(const char *, const char *) { return true; } bool supports_mkdir(const char *, mode_t) { return true; } + bool supports_socket(int, int, int) { return true; } + bool supports_freeaddrinfo(struct addrinfo *) { return true; } + bool supports_getaddrinfo(const char *, const char *, + struct addrinfo **) { return true; } Libc::File_descriptor *open(char const *, int); ssize_t write(Libc::File_descriptor *, const void *, ::size_t); @@ -559,6 +563,31 @@ namespace { int unlink(char const *path); int rename(const char *oldpath, const char *newpath); int mkdir(const char *path, mode_t mode); + + /* Network related functions */ + Libc::File_descriptor *socket(int, int, int); + Libc::File_descriptor *accept(Libc::File_descriptor *, + struct sockaddr *, socklen_t *); + int bind(Libc::File_descriptor *, const struct sockaddr *, + socklen_t); + int connect(Libc::File_descriptor *, const struct sockaddr *addr, + socklen_t addrlen); + void freeaddrinfo(struct addrinfo *); + int getaddrinfo(const char *, const char *, const struct addrinfo *, + struct addrinfo **); + int getpeername(Libc::File_descriptor *, struct sockaddr *, + socklen_t *); + int listen(Libc::File_descriptor *, int); + ssize_t send(Libc::File_descriptor *, const void *, ::size_t, + int flags); + ssize_t sendto(Libc::File_descriptor *, const void *, size_t, int, + const struct sockaddr *, socklen_t); + ssize_t recv(Libc::File_descriptor *, void *, ::size_t, int); + int getsockopt(Libc::File_descriptor *, int, int, void *, + socklen_t *); + int setsockopt(Libc::File_descriptor *, int , int , const void *, + socklen_t); + int shutdown(Libc::File_descriptor *, int how); }; @@ -1015,6 +1044,373 @@ namespace { return 0; } + + Libc::File_descriptor *Plugin::socket(int domain, int type, int protocol) + { + sysio()->socket_in.domain = domain; + sysio()->socket_in.type = type; + sysio()->socket_in.protocol = protocol; + + if (!noux()->syscall(Noux::Session::SYSCALL_SOCKET)) + return 0; + + Libc::Plugin_context *context = noux_context(sysio()->socket_out.fd); + return Libc::file_descriptor_allocator()->alloc(this, context, + sysio()->socket_out.fd); + } + + + int Plugin::getsockopt(Libc::File_descriptor *fd, int level, int optname, + void *optval, socklen_t *optlen) + { + sysio()->getsockopt_in.fd = noux_fd(fd->context); + sysio()->getsockopt_in.level = level; + sysio()->getsockopt_in.optname = optname; + + /* wipe-old state */ + sysio()->getsockopt_in.optlen = *optlen; + Genode::memset(sysio()->getsockopt_in.optval, 0, + sizeof (sysio()->getsockopt_in.optval)); + + if (!noux()->syscall(Noux::Session::SYSCALL_GETSOCKOPT)) + return -1; + + Genode::memcpy(optval, sysio()->setsockopt_in.optval, + sysio()->getsockopt_in.optlen); + + return 0; + } + + + int Plugin::setsockopt(Libc::File_descriptor *fd, int level, int optname, + const void *optval, socklen_t optlen) + { + if (optlen > sizeof(sysio()->setsockopt_in.optval)) { + /* XXX */ + return -1; + } + + sysio()->setsockopt_in.fd = noux_fd(fd->context); + sysio()->setsockopt_in.level = level; + sysio()->setsockopt_in.optname = optname; + sysio()->setsockopt_in.optlen = optlen; + + Genode::memcpy(sysio()->setsockopt_in.optval, optval, optlen); + + if (!noux()->syscall(Noux::Session::SYSCALL_SETSOCKOPT)) { + /* XXX */ + return -1; + } + + return 0; + } + + + Libc::File_descriptor *Plugin::accept(Libc::File_descriptor *fd, struct sockaddr *addr, + socklen_t *addrlen) + { + sysio()->accept_in.fd = noux_fd(fd->context); + + if (addr != NULL) { + Genode::memcpy(&sysio()->accept_in.addr, addr, sizeof (struct sockaddr)); + sysio()->accept_in.addrlen = *addrlen; + } + else { + Genode::memset(&sysio()->accept_in.addr, 0, sizeof (struct sockaddr)); + sysio()->accept_in.addrlen = 0; + } + + if (!noux()->syscall(Noux::Session::SYSCALL_ACCEPT)) { + return 0; + } + + if (addr != NULL) + *addrlen = sysio()->accept_in.addrlen; + + Libc::Plugin_context *context = noux_context(sysio()->accept_out.fd); + return Libc::file_descriptor_allocator()->alloc(this, context, + sysio()->accept_out.fd); + + } + + + int Plugin::bind(Libc::File_descriptor *fd, const struct sockaddr *addr, + socklen_t addrlen) + { + sysio()->bind_in.fd = noux_fd(fd->context); + + Genode::memcpy(&sysio()->bind_in.addr, addr, sizeof (struct sockaddr)); + sysio()->bind_in.addrlen = addrlen; + + if (!noux()->syscall(Noux::Session::SYSCALL_BIND)) { + errno = EACCES; + return -1; + } + + return 0; + } + + + int Plugin::connect(Libc::File_descriptor *fd, const struct sockaddr *addr, + socklen_t addrlen) + { + sysio()->connect_in.fd = noux_fd(fd->context); + + Genode::memcpy(&sysio()->connect_in.addr, addr, sizeof (struct sockaddr)); + sysio()->connect_in.addrlen = addrlen; + + if (!noux()->syscall(Noux::Session::SYSCALL_CONNECT)) { + /* XXX errno */ + return -1; + } + + return 0; + } + + + void Plugin::freeaddrinfo(struct addrinfo *res) + { +#if 0 + struct addrinfo *next; + + while (res) { + if (res->ai_addr) { + free(res->ai_addr); + } + + if (res->ai_canonname) { + free(res->ai_canonname); + } + + next = res->ai_next; + free(res); + res = next; + } +#endif + } + + + int Plugin::getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) + { +#if 0 + const char *service = NULL; + /** + * We have to fetch the portnumber manually because lwip only + * supports getting the service by portnumber. So we first check + * if servname is already a ascii portnumber and if it is not we + * call getservent(servername, NULL). + */ + char buf[6] = { 0 }; + int port = atoi(servname); + if (port <= 0 || port > 0xffff) { + struct servent *se = getservbyname(servname, NULL); + if (se != NULL) { + port = htons(se->s_port); + snprintf(buf, 6, "%d", port); + service = buf; + } + else { + return -1; + } + } + else + service = servname; + + size_t len = strlen(hostname); + len = min(len, 255); + memcpy(sysio()->getaddrinfo_in.hostname, hostname, len); + sysio()->getaddrinfo_in.hostname[len] = '\0'; + + len = strlen(service); + len = min(len, 255); + memcpy(sysio()->getaddrinfo_in.servname, service, len); + sysio()->getaddrinfo_in.servname[len] = '\0'; + + if (!noux()->syscall(Noux::Session::SYSCALL_GETADDRINFO)) + return -1; + + struct addrinfo *rp = 0, *result; + for (int i = 0; i < sysio()->getaddrinfo_out.addr_num; i++) { + if (!rp) { + rp = (struct addrinfo *)malloc(sizeof (struct addrinfo)); + *res = rp; + } + else { + rp->ai_next = (struct addrinfo *)malloc(sizeof (struct addrinfo)); + rp = rp->ai_next; + } + + rp->ai_flags = sysio()->getaddrinfo_in.res[i].addrinfo.ai_flags; + rp->ai_family = sysio()->getaddrinfo_in.res[i].addrinfo.ai_family; + rp->ai_socktype = sysio()->getaddrinfo_in.res[i].addrinfo.ai_socktype; + rp->ai_protocol = sysio()->getaddrinfo_in.res[i].addrinfo.ai_protocol; + rp->ai_addrlen = sysio()->getaddrinfo_in.res[i].addrinfo.ai_addrlen; + + if (sysio()->getaddrinfo_in.res[i].ai_addr.sa_len != 0) { + rp->ai_addr = (struct sockaddr *)malloc(sizeof (struct sockaddr)); + memcpy(rp->ai_addr, &sysio()->getaddrinfo_in.res[i].ai_addr, sizeo + f (struct sockaddr)); + } + else + rp->ai_addr = 0; + + if (sysio()->getaddrinfo_in.res[i].ai_canonname != 0) { + size_t len = strlen(sysio()->getaddrinfo_in.res[i].ai_canonname) + + 1; + + rp->ai_canonname = (char *)malloc(len); + strncpy(rp->ai_canonname, sysio()->getaddrinfo_in.res[i].ai_canonn + ame, len); + } + else + rp->ai_canonname = 0; + + rp->ai_next = 0; + } +#endif + + return -1; + } + + + int Plugin::getpeername(Libc::File_descriptor *fd, struct sockaddr *addr, + socklen_t *addrlen) + { + sysio()->getpeername_in.fd = noux_fd(fd->context); + + if (!noux()->syscall(Noux::Session::SYSCALL_GETPEERNAME)) { + /* errno */ + return -1; + } + Genode::memcpy(&sysio()->getpeername_in.addr, addr, + sizeof (struct sockaddr)); + sysio()->bind_in.addrlen = *addrlen; + + return 0; + } + + + int Plugin::listen(Libc::File_descriptor *fd, int backlog) + { + sysio()->listen_in.fd = noux_fd(fd->context); + sysio()->listen_in.backlog = backlog; + + if (!noux()->syscall(Noux::Session::SYSCALL_LISTEN)) { + /* errno = EACCES; */ + return -1; + } + + return 0; + } + + + ssize_t Plugin::recv(Libc::File_descriptor *fd, void *buf, ::size_t len, int flags) + { + Genode::size_t sum_recv_count = 0; + + while (len) { + Genode::size_t curr_len = + Genode::min(len, sizeof(sysio()->recv_in.buf)); + + sysio()->recv_in.fd = noux_fd(fd->context); + sysio()->recv_in.len = curr_len; + + if (!noux()->syscall(Noux::Session::SYSCALL_RECV)) { + /* XXX set errno */ + return -1; + } + + Genode::memcpy(buf, sysio()->recv_in.buf, sysio()->recv_in.len); + + sum_recv_count += sysio()->recv_in.len; + + if (sysio()->recv_out.len < sysio()->recv_in.len) + break; + + if (sysio()->recv_out.len <= len) + len -= sysio()->recv_out.len; + else + break; + } + + return sum_recv_count; + } + + + ssize_t Plugin::send(Libc::File_descriptor *fd, const void *buf, ::size_t len, int flags) + { + /* remember original len for the return value */ + int const orig_count = len; + char *src = (char *)buf; + + sysio()->send_in.fd = noux_fd(fd->context); + while (len > 0) { + + Genode::size_t curr_len = Genode::min(sizeof (sysio()->send_in.buf), len); + + sysio()->send_in.len = curr_len; + Genode::memcpy(sysio()->send_in.buf, src, curr_len); + + if (!noux()->syscall(Noux::Session::SYSCALL_SEND)) { + PERR("write error %d", sysio()->error.general); + } + + len -= curr_len; + src += curr_len; + } + return orig_count; + } + + + ssize_t Plugin::sendto(Libc::File_descriptor *fd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) + { + int const orig_count = len; + + if (addrlen > sizeof (sysio()->sendto_in.dest_addr)) { + /* XXX errno */ + return -1; + } + + sysio()->sendto_in.fd = noux_fd(fd->context); + sysio()->sendto_in.addrlen = addrlen; + Genode::memcpy(&sysio()->sendto_in.dest_addr, dest_addr, addrlen); + + /* wipe-out sendto buffer */ + Genode::memset(sysio()->sendto_in.buf, 0, sizeof (sysio()->sendto_in.buf)); + + char *src = (char *)buf; + while (len > 0) { + size_t curr_len = Genode::min(sizeof *sysio()->sendto_in.buf, len); + + sysio()->sendto_in.len = curr_len; + Genode::memcpy(sysio()->sendto_in.buf, src, curr_len); + + if (!noux()->syscall(Noux::Session::SYSCALL_SENDTO)) { + return -1; + } + + len -= curr_len; + src += curr_len; + } + + return orig_count; + } + + + int Plugin::shutdown(Libc::File_descriptor *fd, int how) + { + sysio()->shutdown_in.fd = noux_fd(fd->context); + sysio()->shutdown_in.how = how; + + if (!noux()->syscall(Noux::Session::SYSCALL_SHUTDOWN)) { + return -1; + } + + return 0; + } + } /* unnamed namespace */ diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 8a0399def..8af2dbb99 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -252,6 +252,12 @@ namespace Noux { io->unregister_wake_up_notifier(¬ifier); } + /** + * Method for handling noux network related system calls + */ + + bool _syscall_net(Syscall sc); + public: struct Binary_does_not_exist : Exception { }; diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 83e40c0cf..eddbe8148 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -52,7 +52,7 @@ #include -enum { verbose_syscall = false }; +enum { verbose_syscall = true }; namespace Noux { @@ -66,6 +66,9 @@ namespace Noux { extern "C" void wait_for_continue(); +extern void (*close_socket)(int); + +extern void init_network(); /***************************** ** Noux syscall dispatcher ** @@ -169,6 +172,13 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_CLOSE: { + /** + * We have to explicitly close Socket_io_channel fd's because + * these are currently handled separately. + */ + if (close_socket) + close_socket(_sysio->close_in.fd); + remove_io_channel(_sysio->close_in.fd); return true; } @@ -464,6 +474,22 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) return _root_dir->mkdir(_sysio, Absolute_path(_sysio->mkdir_in.path, _env.pwd()).base()); + case SYSCALL_SOCKET: + case SYSCALL_GETSOCKOPT: + case SYSCALL_SETSOCKOPT: + case SYSCALL_ACCEPT: + case SYSCALL_BIND: + case SYSCALL_LISTEN: + case SYSCALL_SEND: + case SYSCALL_SENDTO: + case SYSCALL_RECV: + case SYSCALL_GETPEERNAME: + case SYSCALL_SHUTDOWN: + case SYSCALL_CONNECT: + case SYSCALL_GETADDRINFO: + { + return _syscall_net(sc); + } case SYSCALL_INVALID: break; } } @@ -610,6 +636,8 @@ int main(int argc, char **argv) static Dir_file_system root_dir(config()->xml_node().sub_node("fstab")); + /* initialize network */ + init_network(); /* * Entrypoint used to virtualize child resources such as RAM, RM */ diff --git a/ports/src/noux/minimal/dummy_net.cc b/ports/src/noux/minimal/dummy_net.cc new file mode 100644 index 000000000..4a6869a0e --- /dev/null +++ b/ports/src/noux/minimal/dummy_net.cc @@ -0,0 +1,21 @@ +/* + * \brief Dummy stubs for network-related Noux functions + * \author Norman Feske + * \date 2012-05-24 + */ + +/* + * 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. + */ + +/* local includes */ +#include + +void (*close_socket)(int) = 0; + +void init_network() { } + +bool Noux::Child::_syscall_net(Noux::Session::Syscall sc) { return false; } diff --git a/ports/src/noux/minimal/target.mk b/ports/src/noux/minimal/target.mk new file mode 100644 index 000000000..c6840370e --- /dev/null +++ b/ports/src/noux/minimal/target.mk @@ -0,0 +1,8 @@ +TARGET = noux +LIBS = cxx env server process signal +SRC_CC = main.cc dummy_net.cc +INC_DIR += $(PRG_DIR) +INC_DIR += $(PRG_DIR)/../ + +vpath main.cc $(PRG_DIR)/.. +vpath dummy_net.cc $(PRG_DIR) diff --git a/ports/src/noux/net/README b/ports/src/noux/net/README new file mode 100644 index 000000000..21cb59589 --- /dev/null +++ b/ports/src/noux/net/README @@ -0,0 +1,4 @@ +This directory contains the implementation of network related stuff for noux. + +Currently network support is provided by using the libc and lwip directly +(only for initialization of the actual network subsystem). diff --git a/ports/src/noux/net/net.cc b/ports/src/noux/net/net.cc new file mode 100644 index 000000000..e5a962470 --- /dev/null +++ b/ports/src/noux/net/net.cc @@ -0,0 +1,360 @@ +/* + * \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 +#include + +#include + +/* Noux includes */ +#include +#include +#include +#include + +using namespace Noux; + +/* Libc includes */ +#include +#include +#include + +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::instance()->sd_in_use(sd)) { + int real_sd = Socket_descriptor_registry::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::instance()->sd_in_use(sd)) { + int real_sd = Socket_descriptor_registry::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 sio = Socket_descriptor_registry::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::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 = _lookup_channel(fd); \ + Shared_pointer handle = \ + io.dynamic_pointer_cast(); + +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(socket_io_channel, Genode::env()->heap()); + + _sysio->socket_out.fd = add_io_channel(io_channel); + + /* add socket to registry */ + Socket_descriptor_registry::instance()->add_io_channel(io_channel.dynamic_pointer_cast(), + _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 channel(new_socket_io_channel, Genode::env()->heap()); + + _sysio->accept_out.fd = add_io_channel(channel); + + /* add new socket to registry */ + Socket_descriptor_registry::instance()->add_io_channel(channel.dynamic_pointer_cast(), + _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; +} diff --git a/ports/src/noux/net/socket_descriptor_registry.h b/ports/src/noux/net/socket_descriptor_registry.h new file mode 100644 index 000000000..6807c1bab --- /dev/null +++ b/ports/src/noux/net/socket_descriptor_registry.h @@ -0,0 +1,106 @@ +/* + * \brief I/O channel for sockets + * \author Josef Söntgen + * \date 2012-04-12 + */ + +/* + * 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. + */ + + +#ifndef _NOUX__SOCKET_DESCRIPTOR_REGISTRY_H_ +#define _NOUX__SOCKET_DESCRIPTOR_REGISTRY_H_ + +/* Noux includes */ +#include + +namespace Noux { + + enum { MAX_SOCKET_DESCRIPTORS = 64 }; + + template + class Socket_descriptor_registry + { + private: + + struct { + bool allocated; + Shared_pointer io_channel; + } _sds[MAX_SOCKET_DESCRIPTORS]; + + void _assign_sd(int sd, Shared_pointer &io_channel) + { + _sds[sd].io_channel = io_channel; + _sds[sd].allocated = true; + } + + bool _is_valid_sd(int sd) const + { + return (sd >= 0) && (sd < MAX_SOCKET_DESCRIPTORS); + } + + void _reset_sd(int sd) + { + _sds[sd].io_channel = Shared_pointer(); + _sds[sd].allocated = false; + } + + public: + Socket_descriptor_registry() + { + for (unsigned i = 0; i < MAX_SOCKET_DESCRIPTORS; i++) + _reset_sd(i); + } + + int add_io_channel(Shared_pointer io_channel, int sd = -1) + { + if (sd == -1) { + PERR("Could not allocate socket descriptor"); + return -1; + } + + if (!_is_valid_sd(sd)) { + PERR("Socket descriptor %d is out of range", sd); + return -2; + } + + _assign_sd(sd, io_channel); + return sd; + } + + void remove_io_channel(int sd) + { + if (!_is_valid_sd(sd)) + PERR("Socket descriptor %d is out of range", sd); + else + _reset_sd(sd); + } + + bool sd_in_use(int sd) const + { + return (_is_valid_sd(sd) && _sds[sd].allocated); + } + + Shared_pointer io_channel_by_sd(int sd) const + { + if (!sd_in_use(sd)) { + PWRN("Socket descriptor %d is not open", sd); + return Shared_pointer(); + } + + return _sds[sd].io_channel; + } + + static Socket_descriptor_registry *instance() + { + static Socket_descriptor_registry _sdr; + return &_sdr; + } + }; + +}; +#endif /* _NOUX__SOCKET_DESCRIPTOR_REGISTRY_H_ */ diff --git a/ports/src/noux/net/socket_io_channel.h b/ports/src/noux/net/socket_io_channel.h new file mode 100644 index 000000000..af91e796b --- /dev/null +++ b/ports/src/noux/net/socket_io_channel.h @@ -0,0 +1,268 @@ +/* + * \brief I/O channel for sockets + * \author Josef Söntgen + * \date 2012-04-12 + */ + +/* + * 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. + */ + + +#ifndef _NOUX__SOCKET_IO_CHANNEL_H_ +#define _NOUX__SOCKET_IO_CHANNEL_H_ + +/* Genode includes */ +#include + +/* Noux includes */ +#include +#include + +/* Libc includes */ +#include +#include +#include +#include + + +namespace Noux { + + class Socket_io_channel : public Io_channel + { + private: + + int _socket; + + enum { UNBLOCK_READ = 0x1, + UNBLOCK_WRITE = 0x2, + UNBLOCK_EXCEPT = 0x4 }; + + int _unblock; + + public: + + Socket_io_channel() + : + _socket(-1), + _unblock(0) + { } + + Socket_io_channel(int s) + : + _socket(s), + _unblock(0) + { } + + ~Socket_io_channel() + { + if (_socket != -1) + ::shutdown(_socket, SHUT_RDWR); + } + + int get_socket() const + { + return _socket; + } + + void set_unblock(bool rd, bool wr, bool ex) + { + if (rd) + _unblock |= UNBLOCK_READ; + if (wr) + _unblock |= UNBLOCK_WRITE; + if (ex) + _unblock |= UNBLOCK_EXCEPT; + } + + bool fstat(Sysio *sysio) { return false; } + + bool fcntl(Sysio *sysio) + { + /* + * For now this fcntl() only contains stubs to keep programs + * happy. lwip-1.3.2 which is currently used does not provide + * its own lwip_fcntl(). We could only use lwip_ioctl() to set + * the socket O_NONBLOCKING if we get this kind of request. + */ + switch (sysio->fcntl_in.cmd) { + + case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS: + { + PWRN("FCNTL_CMD_GET_FILE_STATUS_FLAGS currently only returns 0"); + sysio->fcntl_out.result = 0; + return true; + } + + case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS: + { + PWRN("FCNTL_CMD_SET_FILE_STATUS_FLAGS currently only returns 0"); + sysio->fcntl_out.result = 0; + return true; + } + default: + PWRN("invalid fcntl command: %d", sysio->fcntl_in.cmd); + sysio->error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID; + + return false; + } + } + + bool fchdir(Sysio *sysio, Pwd *pwd) { return false; } + bool dirent(Sysio *sysio) { return false; } + + bool check_unblock(bool rd, bool wr, bool ex) const + { + if (_unblock & UNBLOCK_READ) + return true; + if (_unblock & UNBLOCK_WRITE) + return true; + if (_unblock & UNBLOCK_EXCEPT) + return true; + + return false; + } + + size_t write(Sysio *sysio) + { + size_t written = ::write(_socket, sysio->write_in.chunk, + sysio->write_in.count); + + return written; + } + + bool read(Sysio *sysio) + { + size_t const max_count = + Genode::min(sysio->read_in.count, + sizeof(sysio->read_out.chunk)); + + sysio->read_out.count = ::read(_socket, sysio->read_out.chunk, + max_count); + + return true; + } + + bool socket(Sysio *sysio) + { + _socket = ::socket(sysio->socket_in.domain, + sysio->socket_in.type, + sysio->socket_in.protocol); + + return (_socket == -1) ? false : true; + } + + bool getsockopt(Sysio *sysio) + { + int result = ::getsockopt(_socket, sysio->getsockopt_in.level, + sysio->getsockopt_in.optname, + sysio->getsockopt_in.optval, + &sysio->getsockopt_in.optlen); + + return (result == -1) ? false : true; + } + + bool setsockopt(Sysio *sysio) + { + /* + * Filter options out because lwip only supports several socket + * options. Therefore for now we silently return 0 and notify + * the user via debug message. + */ + switch (sysio->setsockopt_in.optname) { + case SO_DEBUG: + case SO_LINGER: + case SO_REUSEADDR: + + PWRN("SOL_SOCKET option '%d' is currently not supported, however we report success", + sysio->setsockopt_in.optname); + return true; + } + + int result = ::setsockopt(_socket, sysio->setsockopt_in.level, + sysio->setsockopt_in.optname, + sysio->setsockopt_in.optval, + sysio->setsockopt_in.optlen); + + return (result == -1) ? false : true; + } + + int accept(Sysio *sysio) + { + int result; + + if (sysio->accept_in.addrlen == 0) { + result = ::accept(_socket, NULL, NULL); + } + else { + result = ::accept(_socket, (sockaddr *)&sysio->accept_in.addr, + &sysio->accept_in.addrlen); + } + + return result; + } + + int bind(Sysio *sysio) + { + return ::bind(_socket, (const struct sockaddr *)&sysio->bind_in.addr, + sysio->bind_in.addrlen); + } + + int connect(Sysio *sysio) + { + return ::connect(_socket, (struct sockaddr *)&sysio->connect_in.addr, + sysio->connect_in.addrlen); + } + + int getpeername(Sysio *sysio) + { + return ::getpeername(_socket, (struct sockaddr *)&sysio->getpeername_in.addr, + (socklen_t *)&sysio->getpeername_in.addrlen); + } + + bool ioctl(Sysio *sysio) + { + int result = ::ioctl(_socket, sysio->ioctl_in.request, NULL); + + return result ? false : true; + } + + int listen(Sysio *sysio) + { + return ::listen(_socket, sysio->listen_in.backlog); + } + + ssize_t recv(Sysio *sysio) + { + return ::recv(_socket, sysio->recv_in.buf, sysio->recv_in.len, + sysio->recv_in.flags); + } + + ssize_t send(Sysio *sysio) + { + return ::send(_socket, sysio->send_in.buf, sysio->send_in.len, + sysio->send_in.flags); + } + + ssize_t sendto(Sysio *sysio) + { + ssize_t result = ::sendto(_socket, sysio->sendto_in.buf, sysio->sendto_in.len, + sysio->sendto_in.flags, + (const struct sockaddr *) &sysio->sendto_in.dest_addr, + sysio->sendto_in.addrlen); + + return result; + } + + int shutdown(Sysio *sysio) + { + return ::shutdown(_socket, sysio->shutdown_in.how); + } + }; +} + +#endif /* _NOUX__SOCKET_IO_CHANNEL_H_ */ + diff --git a/ports/src/noux/net/target.mk b/ports/src/noux/net/target.mk new file mode 100644 index 000000000..2cc35c51e --- /dev/null +++ b/ports/src/noux/net/target.mk @@ -0,0 +1,12 @@ +TARGET = noux_net +LIBS = cxx env server process signal lwip + +LIBS += libc libc_lwip + +SRC_CC = main.cc net.cc + +INC_DIR += $(PRG_DIR) +INC_DIR += $(PRG_DIR)/../ + +vpath main.cc $(PRG_DIR)/.. +vpath net.cc $(PRG_DIR) diff --git a/ports/src/noux/target.mk b/ports/src/noux/target.mk deleted file mode 100644 index 5ab6ee955..000000000 --- a/ports/src/noux/target.mk +++ /dev/null @@ -1,4 +0,0 @@ -TARGET = noux -LIBS = cxx env server process signal -SRC_CC = main.cc -INC_DIR += $(PRG_DIR)