From fdad5116dd604b0b5d6c40ed54b56f4cad27deff Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Sun, 12 Feb 2017 10:59:37 +0100 Subject: [PATCH] libc: support for socket file system The socket file system can be configured in the "socket" attribute of the libc config node like follows. This configures the socket file system libc backend to access files in "/socket" for socket operations. --- repos/libports/lib/mk/libc.mk | 2 +- .../libports/src/lib/libc/socket_fs_plugin.cc | 788 ++++++++++++++++++ .../libports/src/lib/libc/socket_fs_plugin.h | 38 + .../src/lib/libc/socket_operations.cc | 242 ++++-- repos/libports/src/lib/libc/vfs_plugin.cc | 21 + 5 files changed, 1013 insertions(+), 78 deletions(-) create mode 100644 repos/libports/src/lib/libc/socket_fs_plugin.cc create mode 100644 repos/libports/src/lib/libc/socket_fs_plugin.h diff --git a/repos/libports/lib/mk/libc.mk b/repos/libports/lib/mk/libc.mk index 1f7aa89eae..7eb2cc2236 100644 --- a/repos/libports/lib/mk/libc.mk +++ b/repos/libports/lib/mk/libc.mk @@ -15,7 +15,7 @@ SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc \ plugin.cc plugin_registry.cc select.cc exit.cc environ.cc nanosleep.cc \ pread_pwrite.cc readv_writev.cc poll.cc \ libc_pdbg.cc vfs_plugin.cc rtc.cc dynamic_linker.cc signal.cc \ - socket_operations.cc task.cc addrinfo.cc + socket_operations.cc task.cc addrinfo.cc socket_fs_plugin.cc CC_OPT_sysctl += -Wno-write-strings diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.cc b/repos/libports/src/lib/libc/socket_fs_plugin.cc new file mode 100644 index 0000000000..8ce3f0e260 --- /dev/null +++ b/repos/libports/src/lib/libc/socket_fs_plugin.cc @@ -0,0 +1,788 @@ +/* + * \brief Libc pseudo plugin for socket fs + * \author Christian Helmuth + * \author Christian Prochaska + * \author Norman Feske + * \author Emery Hemingway + * \date 2015-06-23 + */ + +/* + * Copyright (C) 2015-2017 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 +#include + +/* libc includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* libc-internal includes */ +#include "socket_fs_plugin.h" +#include "libc_file.h" +#include "libc_errno.h" +#include "task.h" + + +namespace Libc { + extern char const *config_socket(); + bool read_ready(Libc::File_descriptor *); +} + + +/*************** + ** Utilities ** + ***************/ + +namespace { + + using Libc::Errno; + + struct Absolute_path : Vfs::Absolute_path + { + Absolute_path() { } + + Absolute_path(char const *path, char const *pwd = 0) + : + Vfs::Absolute_path(path, pwd) + { + remove_trailing('\n'); + } + }; + + struct Exception { }; + struct New_socket_failed : Exception { }; + struct Address_conversion_failed : Exception { }; + + struct Socket_context : Libc::Plugin_context + { + struct Inaccessible { }; /* exception */ + + Absolute_path path; + + int _data_fd = -1; + int _remote_fd = -1; + int _local_fd = -1; + + Socket_context(Absolute_path const &path) + : path(path.base()) { } + + ~Socket_context() + { + if (_data_fd != -1) close(_data_fd); + } + + int _open_file(char const *file_name) + { + Absolute_path file(file_name, path.base()); + int const fd = open(file.base(), O_RDWR); + if (fd == -1) { + Genode::error(__func__, ": ", file_name, " file not accessible"); + throw Inaccessible(); + } + return fd; + } + + int data_fd() + { + if (_data_fd == -1) + _data_fd = _open_file("data"); + + return _data_fd; + } + + bool data_read_ready() + { + return Libc::read_ready( + Libc::file_descriptor_allocator()->find_by_libc_fd(data_fd())); + } + + int local_fd() + { + if (_local_fd == -1) + _local_fd = _open_file("local"); + + return _local_fd; + } + + int remote_fd() + { + if (_remote_fd == -1) + _remote_fd = _open_file("remote"); + + return _remote_fd; + } + + bool remote_read_ready() + { + return Libc::read_ready( + Libc::file_descriptor_allocator()->find_by_libc_fd(remote_fd())); + } + }; + + struct Socket_plugin : Libc::Plugin + { + bool supports_select(int, fd_set *, fd_set *, fd_set *, timeval *) override; + + ssize_t read(Libc::File_descriptor *, void *, ::size_t) override; + ssize_t write(Libc::File_descriptor *, const void *, ::size_t) override; + int close(Libc::File_descriptor *) override; + int select(int, fd_set *, fd_set *, fd_set *, timeval *) override; + }; + + Socket_plugin & socket_plugin() + { + static Socket_plugin inst; + return inst; + } + + template class String + { + private: + + char _buf[CAPACITY] { 0 }; + + public: + + String() { } + + constexpr size_t capacity() { return CAPACITY; } + + char const * base() const { return _buf; } + char * base() { return _buf; } + + void terminate(size_t at) { _buf[at] = 0; } + + void remove_trailing_newline() + { + int i = 0; + while (_buf[i] && _buf[i + 1]) i++; + + if (i > 0 && _buf[i] == '\n') + _buf[i] = 0; + } + }; + + typedef String Host_string; + typedef String Port_string; + + /* + * Both NI_MAXHOST and NI_MAXSERV include the terminating 0, which allows + * use to put ':' between host and port on concatenation. + */ + struct Sockaddr_string : String + { + Sockaddr_string() { } + + Sockaddr_string(Host_string const &host, Port_string const &port) + { + char *b = base(); + b = stpcpy(b, host.base()); + b = stpcpy(b, ":"); + b = stpcpy(b, port.base()); + } + + Host_string host() const + { + Host_string host; + + strncpy(host.base(), base(), host.capacity()); + char *at = strstr(host.base(), ":"); + if (!at) + throw Address_conversion_failed(); + *at = 0; + + return host; + } + + Port_string port() const + { + Port_string port; + + char *at = strstr(base(), ":"); + if (!at) + throw Address_conversion_failed(); + + strncpy(port.base(), ++at, port.capacity()); + + return port; + } + }; +} + + +/* TODO move to C++ structs or something */ + +static Port_string port_string(sockaddr_in const &addr) +{ + Port_string port; + + if (getnameinfo((sockaddr *)&addr, sizeof(addr), + nullptr, 0, /* no host conversion */ + port.base(), port.capacity(), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw Address_conversion_failed(); + + return port; +} + + +static Host_string host_string(sockaddr_in const &addr) +{ + Host_string host; + + if (getnameinfo((sockaddr *)&addr, sizeof(addr), + host.base(), host.capacity(), + nullptr, 0, /* no port conversion */ + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw Address_conversion_failed(); + + return host; +} + + +static sockaddr_in sockaddr_in_struct(Host_string const &host, Port_string const &port) +{ + addrinfo hints; + addrinfo *info = nullptr; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + if (getaddrinfo(host.base(), port.base(), &hints, &info)) + throw Address_conversion_failed(); + + sockaddr_in addr = *(sockaddr_in*)info->ai_addr; + + freeaddrinfo(info); + + return addr; +} + + +static int read_sockaddr_in(int read_fd, Socket_context *context, + struct sockaddr_in *addr, socklen_t *addrlen, + bool block_for_read = false) +{ + if (!addr) return Errno(EFAULT); + if (!addrlen || *addrlen <= 0) return Errno(EINVAL); + if (read_fd == -1) return Errno(ENOTCONN); + + while (block_for_read && !context->remote_read_ready()) + Libc::suspend(); + + Sockaddr_string addr_string; + int const n = read(read_fd, addr_string.base(), addr_string.capacity() - 1); + + if (!n) /* probably a UDP socket */ + return Errno(ENOTCONN); + if (n == -1 || !n || n >= (int)addr_string.capacity() - 1) + return Errno(EINVAL); + + addr_string.terminate(n); + addr_string.remove_trailing_newline(); + + try { + /* convert the address but do not exceed the caller's buffer */ + sockaddr_in saddr = sockaddr_in_struct(addr_string.host(), addr_string.port()); + memcpy(addr, &saddr, *addrlen); + *addrlen = sizeof(saddr); + + return 0; + } catch (Address_conversion_failed) { + Genode::warning("IP address conversion failed"); + return Errno(ENOBUFS); + } +} + + +/*********************** + ** Address functions ** + ***********************/ + +extern "C" int socket_fs_getpeername(int libc_fd, sockaddr *addr, socklen_t *addrlen) +{ + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) return Errno(EBADF); + + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + return read_sockaddr_in(context->remote_fd(), context, (sockaddr_in *)addr, addrlen); +} + + +extern "C" int socket_fs_getsockname(int libc_fd, sockaddr *addr, socklen_t *addrlen) +{ + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) return Errno(EBADF); + + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + return read_sockaddr_in(context->local_fd(), context, (sockaddr_in *)addr, addrlen); +} + + +/************************** + ** Socket transport API ** + **************************/ + +extern "C" int socket_fs_accept(int libc_fd, sockaddr *addr, socklen_t *addrlen) +{ + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) + return Errno(EBADF); + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + char accept_socket[10]; + { + Absolute_path file("accept", context->path.base()); + int const fd = open(file.base(), O_RDONLY); + if (fd == -1) { + Genode::error(__func__, ": accept file not accessible"); + return Errno(EINVAL); + } + int n = 0; + /* XXX currently reading accept may return without new connection */ + do { n = read(fd, accept_socket, sizeof(accept_socket)); } while (n == 0); + close(fd); + if (n == -1 || n >= (int)sizeof(accept_socket) - 1) + return Errno(EINVAL); + + accept_socket[n] = 0; + } + + Absolute_path accept_path(accept_socket, Libc::config_socket()); + Socket_context *accept_context = new (Genode::env()->heap()) + Socket_context(accept_path); + Libc::File_descriptor *accept_fd = + Libc::file_descriptor_allocator()->alloc(&socket_plugin(), accept_context); + + if (addr && addrlen) { + Absolute_path file("remote", accept_path.base()); + int const fd = open(file.base(), O_RDONLY); + if (fd == -1) { + Genode::error(__func__, ": remote file not accessible"); + return Errno(EINVAL); + } + Sockaddr_string remote; + int const n = read(fd, remote.base(), remote.capacity() - 1); + close(fd); + if (n == -1 || !n || n >= (int)remote.capacity() - 1) + return Errno(EINVAL); + + remote.terminate(n); + remote.remove_trailing_newline(); + + sockaddr_in remote_addr = sockaddr_in_struct(remote.host(), remote.port()); + memcpy(addr, &remote_addr, *addrlen); + *addrlen = sizeof(remote_addr); + } + + return accept_fd->libc_fd; +} + + +extern "C" int socket_fs_bind(int libc_fd, sockaddr const *addr, socklen_t addrlen) +{ + if (addr->sa_family != AF_INET) { + Genode::error(__func__, ": family not supported"); + return Errno(EAFNOSUPPORT); + } + + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) + return Errno(EBADF); + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + { + Sockaddr_string addr_string(host_string(*(sockaddr_in *)addr), + port_string(*(sockaddr_in *)addr)); + + Absolute_path file("bind", context->path.base()); + int const fd = open(file.base(), O_WRONLY); + if (fd == -1) { + Genode::error(__func__, ": bind file not accessible"); + return Errno(EINVAL); + } + int const len = strlen(addr_string.base()); + int const n = write(fd, addr_string.base(), len); + close(fd); + if (n != len) return Errno(EIO); + } + + return 0; +} + + +extern "C" int socket_fs_connect(int libc_fd, sockaddr const *addr, socklen_t addrlen) +{ + if (addr->sa_family != AF_INET) { + Genode::error(__func__, ": family not supported"); + return Errno(EAFNOSUPPORT); + } + + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) + return Errno(EBADF); + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + { + Sockaddr_string addr_string(host_string(*(sockaddr_in const *)addr), + port_string(*(sockaddr_in const *)addr)); + + Absolute_path file("connect", context->path.base()); + int const fd = open(file.base(), O_WRONLY); + if (fd == -1) { + Genode::error(__func__, ": connect file not accessible"); + return Errno(EINVAL); + } + int const len = strlen(addr_string.base()); + int const n = write(fd, addr_string.base(), len); + close(fd); + if (n != len) return Errno(EIO); + } + + return 0; +} + + +extern "C" int socket_fs_listen(int libc_fd, int backlog) +{ + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) + return Errno(EBADF); + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + { + Absolute_path file("listen", context->path.base()); + int const fd = open(file.base(), O_WRONLY); + if (fd == -1) { + Genode::error(__func__, ": listen file not accessible"); + return Errno(EINVAL); + } + char buf[10]; + snprintf(buf, sizeof(buf), "%d", backlog); + int const n = write(fd, buf, strlen(buf)); + close(fd); + if ((unsigned)n != strlen(buf)) return Errno(EIO); + } + + return 0; +} + + +static ssize_t do_recvfrom(Libc::File_descriptor *fd, + void *buf, ::size_t len, int flags, + struct sockaddr *src_addr, socklen_t *src_addrlen) +{ + Socket_context *context = dynamic_cast(fd->context); + if (!context) return Errno(ENOTSOCK); + if (!buf || !len) return Errno(EINVAL); + + /* TODO if "remote" is empty we have to block for the next packet */ + if (src_addr) { + int const res = read_sockaddr_in(context->remote_fd(), context, + (sockaddr_in *)src_addr, src_addrlen, + true); + if (res < 0) return res; + } + + try { + lseek(context->data_fd(), 0, 0); + ssize_t out_len = read(context->data_fd(), buf, len); + return out_len; + } catch (Socket_context::Inaccessible) { + return Errno(EINVAL); + } +} + + +extern "C" ssize_t socket_fs_recvfrom(int libc_fd, void *buf, ::size_t len, int flags, + sockaddr *src_addr, socklen_t *src_addrlen) +{ + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) return Errno(EBADF); + + return do_recvfrom(fd, buf, len, flags, src_addr, src_addrlen); +} + + +extern "C" ssize_t socket_fs_recv(int libc_fd, void *buf, ::size_t len, int flags) +{ + /* identical to recvfrom() with a NULL src_addr argument */ + return socket_fs_recvfrom(libc_fd, buf, len, flags, nullptr, nullptr); +} + + +extern "C" ssize_t socket_fs_recvmsg(int libc_fd, msghdr *msg, int flags) +{ + Genode::warning("########## TODO ########## ", __func__); + return 0; +} + + +static ssize_t do_sendto(Libc::File_descriptor *fd, + void const *buf, ::size_t len, int flags, + sockaddr const *dest_addr, socklen_t dest_addrlen) +{ + Socket_context *context = dynamic_cast(fd->context); + if (!context) return Errno(ENOTSOCK); + if (!buf || !len) return Errno(EINVAL); + + if (dest_addr) { + Sockaddr_string addr_string(host_string(*(sockaddr_in const *)dest_addr), + port_string(*(sockaddr_in const *)dest_addr)); + +#warning TODO use remote_fd() + Absolute_path file("remote", context->path.base()); + int const fd = open(file.base(), O_WRONLY); + if (fd == -1) { + Genode::error(__func__, ": remote file not accessible"); + return Errno(EINVAL); + } + int const len = strlen(addr_string.base()); + int const n = write(fd, addr_string.base(), len); + close(fd); + if (n != len) return Errno(EIO); + } + + try { + lseek(context->data_fd(), 0, 0); + ssize_t out_len = write(context->data_fd(), buf, len); + return out_len; + } catch (Socket_context::Inaccessible) { + return Errno(EINVAL); + } +} + + +extern "C" ssize_t socket_fs_sendto(int libc_fd, void const *buf, ::size_t len, int flags, + sockaddr const *dest_addr, socklen_t dest_addrlen) +{ + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) return Errno(EBADF); + + return do_sendto(fd, buf, len, flags, dest_addr, dest_addrlen); +} + + +extern "C" ssize_t socket_fs_send(int libc_fd, void const *buf, ::size_t len, int flags) +{ + /* identical to sendto() with a NULL dest_addr argument */ + return socket_fs_sendto(libc_fd, buf, len, flags, nullptr, 0); +} + + +extern "C" int socket_fs_getsockopt(int libc_fd, int level, int optname, + void *optval, socklen_t *optlen) +{ + Genode::warning("########## TODO ########## ", __func__); + return 0; +} + + +extern "C" int socket_fs_setsockopt(int libc_fd, int level, int optname, + void const *optval, socklen_t optlen) +{ + Genode::warning("########## TODO ########## ", __func__); + return 0; +} + + +extern "C" int socket_fs_shutdown(int libc_fd, int how) +{ + /* TODO ENOTCONN */ + + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd); + if (!fd) + return Errno(EBADF); + Socket_context *context; + if (!(context = dynamic_cast(fd->context))) + return Errno(ENOTSOCK); + + unlink(context->path.base()); + + return 0; +} + + +static Genode::String<16> new_socket(Absolute_path const &path) +{ + Absolute_path new_socket("new_socket", path.base()); + + int const fd = open(new_socket.base(), O_RDONLY); + if (fd == -1) { + Genode::error(__func__, ": new_socket file not accessible - socket fs not mounted?"); + throw New_socket_failed(); + } + char buf[10]; + int const n = read(fd, buf, sizeof(buf)); + close(fd); + if (n == -1 || !n || n >= (int)sizeof(buf) - 1) + throw New_socket_failed(); + buf[n] = 0; + + return Genode::String<16>(buf); +} + + +extern "C" int socket_fs_socket(int domain, int type, int protocol) +{ + Absolute_path path(Libc::config_socket()); + + if (path == "") { + Genode::error(__func__, ": socket fs not mounted"); + return Errno(EACCES); + } + + if ((type != SOCK_STREAM || (protocol != 0 && protocol != IPPROTO_TCP)) + && (type != SOCK_DGRAM || (protocol != 0 && protocol != IPPROTO_UDP))) { + Genode::error(__func__, + ": socket with type=", type, + " protocol=", protocol, " not supported"); + return Errno(EAFNOSUPPORT); + } + + /* socket is ensured to be TCP or UDP */ + try { + Absolute_path proto_path(path); + if (type == SOCK_STREAM) + proto_path.append("/tcp"); + else + proto_path.append("/udp"); + + Genode::String<16> socket_path = new_socket(proto_path); + path.append("/"); + path.append(socket_path.string()); + } catch (New_socket_failed) { return Errno(EINVAL); } + + Socket_context *context = new (Genode::env()->heap()) + Socket_context(path); + Libc::File_descriptor *fd = + Libc::file_descriptor_allocator()->alloc(&socket_plugin(), context); + + return fd->libc_fd; +} + + +/**************************** + ** File-plugin operations ** + ****************************/ + +//int Socket_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg) override; + +ssize_t Socket_plugin::read(Libc::File_descriptor *fd, void *buf, ::size_t count) +{ + return do_recvfrom(fd, buf, count, 0, nullptr, nullptr); +} + + +ssize_t Socket_plugin::write(Libc::File_descriptor *fd, const void *buf, ::size_t count) +{ + return do_sendto(fd, buf, count, 0, nullptr, 0); +} + + +bool Socket_plugin::supports_select(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + /* return true if any file descriptor (which is set) belongs to the VFS */ + for (int fd = 0; fd < nfds; ++fd) { + + if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) { + Libc::File_descriptor *fdo = + Libc::file_descriptor_allocator()->find_by_libc_fd(fd); + + if (fdo && (fdo->plugin == this)) + return true; + } + } + + return false; +} + + +int Socket_plugin::select(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout) +{ + int nready = 0; + + fd_set const in_readfds = *readfds; + fd_set const in_writefds = *writefds; + /* XXX exceptfds not supported */ + + /* clear fd sets */ + FD_ZERO(readfds); + FD_ZERO(writefds); + FD_ZERO(exceptfds); + + for (int fd = 0; fd < nfds; ++fd) { + + Libc::File_descriptor *fdo = + Libc::file_descriptor_allocator()->find_by_libc_fd(fd); + + /* handle only fds that belong to this plugin */ + if (!fdo || (fdo->plugin != this)) + continue; + + if (FD_ISSET(fd, &in_readfds)) { + try { + Socket_context *context = dynamic_cast(fdo->context); + if (context->data_read_ready()) { + FD_SET(fd, readfds); + ++nready; + } + } catch (Socket_context::Inaccessible) { } + } + + if (FD_ISSET(fd, &in_writefds)) { + if (false /* XXX ask if "data" is writeable */) { + FD_SET(fd, writefds); + ++nready; + } + } + + /* XXX exceptfds not supported */ + } + + return nready; +} + + +int Socket_plugin::close(Libc::File_descriptor *fd) +{ + Socket_context *context = dynamic_cast(fd->context); + if (!context) return Errno(EBADF); + + Genode::destroy(Genode::env()->heap(), context); + Libc::file_descriptor_allocator()->free(fd); + + return 0; +} diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.h b/repos/libports/src/lib/libc/socket_fs_plugin.h new file mode 100644 index 0000000000..223b04de5f --- /dev/null +++ b/repos/libports/src/lib/libc/socket_fs_plugin.h @@ -0,0 +1,38 @@ +/* + * \brief Libc pseudo plugin for socket fs + * \author Emery Hemingway + * \author Christian Helmuth + * \date 2017-02-11 + */ + +/* + * Copyright (C) 2017 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 _LIBC__SOCKET_FS_PLUGIN_H_ +#define _LIBC__SOCKET_FS_PLUGIN_H_ + +/* Libc includes */ +#include +#include + +extern "C" int socket_fs_getpeername(int, sockaddr *, socklen_t *); +extern "C" int socket_fs_getsockname(int, sockaddr *, socklen_t *); +extern "C" int socket_fs_accept(int, sockaddr *, socklen_t *); +extern "C" int socket_fs_bind(int, sockaddr const *, socklen_t); +extern "C" int socket_fs_connect(int, sockaddr const *, socklen_t); +extern "C" int socket_fs_listen(int, int); +extern "C" ssize_t socket_fs_recvfrom(int, void *, ::size_t, int, sockaddr *, socklen_t *); +extern "C" ssize_t socket_fs_recv(int, void *, ::size_t, int); +extern "C" ssize_t socket_fs_recvmsg(int, msghdr *, int); +extern "C" ssize_t socket_fs_sendto(int, void const *, ::size_t, int, sockaddr const *, socklen_t); +extern "C" ssize_t socket_fs_send(int, void const *, ::size_t, int); +extern "C" int socket_fs_getsockopt(int, int, int, void *, socklen_t *); +extern "C" int socket_fs_setsockopt(int, int, int, void const *, socklen_t); +extern "C" int socket_fs_shutdown(int, int); +extern "C" int socket_fs_socket(int, int, int); + +#endif /* _LIBC__SOCKET_FS_PLUGIN_H_ */ diff --git a/repos/libports/src/lib/libc/socket_operations.cc b/repos/libports/src/lib/libc/socket_operations.cc index b215a44003..dcc9689831 100644 --- a/repos/libports/src/lib/libc/socket_operations.cc +++ b/repos/libports/src/lib/libc/socket_operations.cc @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2015 Genode Labs GmbH + * Copyright (C) 2015-2017 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. @@ -19,150 +19,238 @@ #include /* libc includes */ -#include -#include #include /* libc-internal includes */ #include "libc_file.h" +#include "socket_fs_plugin.h" using namespace Libc; +namespace Libc { extern char const *config_socket(); } -extern "C" int _accept(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) + +/*********************** + ** Address functions ** + ***********************/ + +extern "C" int _getpeername(int libc_fd, sockaddr *addr, socklen_t *addrlen) { - return accept(libc_fd, addr, addrlen); + if (*Libc::config_socket()) + return socket_fs_getpeername(libc_fd, addr, addrlen); + + FD_FUNC_WRAPPER(getpeername, libc_fd, addr, addrlen); } -extern "C" int accept(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) +extern "C" int getpeername(int libc_fd, sockaddr *addr, socklen_t *addrlen) { + return _getpeername(libc_fd, addr, addrlen); +} + + +extern "C" int _getsockname(int libc_fd, sockaddr *addr, socklen_t *addrlen) +{ + if (*Libc::config_socket()) + return socket_fs_getsockname(libc_fd, addr, addrlen); + + FD_FUNC_WRAPPER(getsockname, libc_fd, addr, addrlen); +} + + +extern "C" int getsockname(int libc_fd, sockaddr *addr, socklen_t *addrlen) +{ + return _getsockname(libc_fd, addr, addrlen); +} + + +/************************** + ** Socket transport API ** + **************************/ + +extern "C" int _accept(int libc_fd, sockaddr *addr, socklen_t *addrlen) +{ + if (*Libc::config_socket()) + return socket_fs_accept(libc_fd, addr, addrlen); + File_descriptor *ret_fd; FD_FUNC_WRAPPER_GENERIC(ret_fd =, 0, accept, libc_fd, addr, addrlen); return ret_fd ? ret_fd->libc_fd : INVALID_FD; } -extern "C" int _bind(int libc_fd, const struct sockaddr *addr, - socklen_t addrlen) +extern "C" int accept(int libc_fd, sockaddr *addr, socklen_t *addrlen) { - return bind(libc_fd, addr, addrlen); + return _accept(libc_fd, addr, addrlen); } -extern "C" int bind(int libc_fd, const struct sockaddr *addr, - socklen_t addrlen) { - FD_FUNC_WRAPPER(bind, libc_fd, addr, addrlen); } - - -extern "C" int connect(int libc_fd, const struct sockaddr *addr, - socklen_t addrlen) { - FD_FUNC_WRAPPER(connect, libc_fd, addr, addrlen); } - - -extern "C" int _connect(int libc_fd, const struct sockaddr *addr, - socklen_t addrlen) +extern "C" int _bind(int libc_fd, sockaddr const *addr, socklen_t addrlen) { - return connect(libc_fd, addr, addrlen); + if (*Libc::config_socket()) + return socket_fs_bind(libc_fd, addr, addrlen); + + FD_FUNC_WRAPPER(bind, libc_fd, addr, addrlen); } -extern "C" int _getpeername(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) { - FD_FUNC_WRAPPER(getpeername, libc_fd, addr, addrlen); } - - -extern "C" int getpeername(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) +extern "C" int bind(int libc_fd, sockaddr const *addr, socklen_t addrlen) { - return _getpeername(libc_fd, addr, addrlen); + return _bind(libc_fd, addr, addrlen); } -extern "C" int _getsockname(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) { - FD_FUNC_WRAPPER(getsockname, libc_fd, addr, addrlen); } - - -extern "C" int getsockname(int libc_fd, struct sockaddr *addr, socklen_t *addrlen) +extern "C" int _connect(int libc_fd, sockaddr const *addr, socklen_t addrlen) { - return _getsockname(libc_fd, addr, addrlen); + if (*Libc::config_socket()) + return socket_fs_connect(libc_fd, addr, addrlen); + + FD_FUNC_WRAPPER(connect, libc_fd, addr, addrlen); +} + + +extern "C" int connect(int libc_fd, sockaddr const *addr, socklen_t addrlen) +{ + return _connect(libc_fd, addr, addrlen); } extern "C" int _listen(int libc_fd, int backlog) { - return listen(libc_fd, backlog); + if (*Libc::config_socket()) + return socket_fs_listen(libc_fd, backlog); + + FD_FUNC_WRAPPER(listen, libc_fd, backlog); } -extern "C" int listen(int libc_fd, int backlog) { - FD_FUNC_WRAPPER(listen, libc_fd, backlog); } - - -extern "C" ssize_t recv(int libc_fd, void *buf, ::size_t len, int flags) { - FD_FUNC_WRAPPER(recv, libc_fd, buf, len, flags); } +extern "C" int listen(int libc_fd, int backlog) +{ + return _listen(libc_fd, backlog); +} extern "C" ssize_t _recvfrom(int libc_fd, void *buf, ::size_t len, int flags, - struct sockaddr *src_addr, socklen_t *addrlen) { - FD_FUNC_WRAPPER(recvfrom, libc_fd, buf, len, flags, src_addr, addrlen); } - - -extern "C" ssize_t recvfrom(int libc_fd, void *buf, ::size_t len, int flags, - struct sockaddr *src_addr, socklen_t *addrlen) + sockaddr *src_addr, socklen_t *src_addrlen) { - return _recvfrom(libc_fd, buf, len, flags, src_addr, addrlen); + if (*Libc::config_socket()) + return socket_fs_recvfrom(libc_fd, buf, len, flags, src_addr, src_addrlen); + + FD_FUNC_WRAPPER(recvfrom, libc_fd, buf, len, flags, src_addr, src_addrlen); } -extern "C" ssize_t recvmsg(int libc_fd, struct msghdr *msg, int flags) { - FD_FUNC_WRAPPER(recvmsg, libc_fd, msg, flags); } - - -extern "C" ssize_t send(int libc_fd, const void *buf, ::size_t len, int flags) { - FD_FUNC_WRAPPER(send, libc_fd, buf, len, flags); } - - -extern "C" ssize_t _sendto(int libc_fd, const void *buf, ::size_t len, int flags, - const struct sockaddr *dest_addr, socklen_t addrlen) { - FD_FUNC_WRAPPER(sendto, libc_fd, buf, len, flags, dest_addr, addrlen); } - - -extern "C" ssize_t sendto(int libc_fd, const void *buf, ::size_t len, int flags, - const struct sockaddr *dest_addr, socklen_t addrlen) +extern "C" ssize_t recvfrom(int libc_fd, void *buf, ::size_t len, int flags, + sockaddr *src_addr, socklen_t *src_addrlen) { - return _sendto(libc_fd, buf, len, flags, dest_addr, addrlen); + return _recvfrom(libc_fd, buf, len, flags, src_addr, src_addrlen); +} + + +extern "C" ssize_t recv(int libc_fd, void *buf, ::size_t len, int flags) +{ + if (*Libc::config_socket()) + return socket_fs_recv(libc_fd, buf, len, flags); + + FD_FUNC_WRAPPER(recv, libc_fd, buf, len, flags); +} + + +extern "C" ssize_t _recvmsg(int libc_fd, msghdr *msg, int flags) +{ + if (*Libc::config_socket()) + return socket_fs_recvmsg(libc_fd, msg, flags); + + FD_FUNC_WRAPPER(recvmsg, libc_fd, msg, flags); +} + + +extern "C" ssize_t recvmsg(int libc_fd, msghdr *msg, int flags) +{ + return _recvmsg(libc_fd, msg, flags); +} + + +extern "C" ssize_t _sendto(int libc_fd, void const *buf, ::size_t len, int flags, + sockaddr const *dest_addr, socklen_t dest_addrlen) +{ + if (*Libc::config_socket()) + return socket_fs_sendto(libc_fd, buf, len, flags, dest_addr, dest_addrlen); + + FD_FUNC_WRAPPER(sendto, libc_fd, buf, len, flags, dest_addr, dest_addrlen); +} + + +extern "C" ssize_t sendto(int libc_fd, void const *buf, ::size_t len, int flags, + sockaddr const *dest_addr, socklen_t dest_addrlen) +{ + return _sendto(libc_fd, buf, len, flags, dest_addr, dest_addrlen); +} + + +extern "C" ssize_t send(int libc_fd, void const *buf, ::size_t len, int flags) +{ + if (*Libc::config_socket()) + return socket_fs_send(libc_fd, buf, len, flags); + + FD_FUNC_WRAPPER(send, libc_fd, buf, len, flags); } extern "C" int _getsockopt(int libc_fd, int level, int optname, void *optval, socklen_t *optlen) { - return getsockopt(libc_fd, level, optname, optval, optlen); + if (*Libc::config_socket()) + return socket_fs_getsockopt(libc_fd, level, optname, optval, optlen); + + FD_FUNC_WRAPPER(getsockopt, libc_fd, level, optname, optval, optlen); } extern "C" int getsockopt(int libc_fd, int level, int optname, - void *optval, socklen_t *optlen) { - FD_FUNC_WRAPPER(getsockopt, libc_fd, level, optname, optval, optlen); } + void *optval, socklen_t *optlen) +{ + return _getsockopt(libc_fd, level, optname, optval, optlen); +} extern "C" int _setsockopt(int libc_fd, int level, int optname, - const void *optval, socklen_t optlen) { - FD_FUNC_WRAPPER(setsockopt, libc_fd, level, optname, optval, optlen); } + void const *optval, socklen_t optlen) +{ + if (*Libc::config_socket()) + return socket_fs_setsockopt(libc_fd, level, optname, optval, optlen); + + FD_FUNC_WRAPPER(setsockopt, libc_fd, level, optname, optval, optlen); +} extern "C" int setsockopt(int libc_fd, int level, int optname, - const void *optval, socklen_t optlen) + void const *optval, socklen_t optlen) { return _setsockopt(libc_fd, level, optname, optval, optlen); } -extern "C" int shutdown(int libc_fd, int how) { - FD_FUNC_WRAPPER(shutdown, libc_fd, how); } - - -extern "C" int socket(int domain, int type, int protocol) +extern "C" int _shutdown(int libc_fd, int how) { + if (*Libc::config_socket()) + return socket_fs_shutdown(libc_fd, how); + + FD_FUNC_WRAPPER(shutdown, libc_fd, how); +} + + +extern "C" int shutdown(int libc_fd, int how) +{ + return _shutdown(libc_fd, how); +} + + +extern "C" int _socket(int domain, int type, int protocol) +{ + if (*Libc::config_socket()) + return socket_fs_socket(domain, type, protocol); + Plugin *plugin; File_descriptor *new_fdo; @@ -183,7 +271,7 @@ extern "C" int socket(int domain, int type, int protocol) } -extern "C" int _socket(int domain, int type, int protocol) +extern "C" int socket(int domain, int type, int protocol) { - return socket(domain, type, protocol); + return _socket(domain, type, protocol); } diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index b46fb6a04b..6aaf77853b 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -111,6 +111,13 @@ namespace Libc { static Config_attr rtc("rtc", ""); return rtc.string(); } + + char const *config_socket() __attribute__((weak)); + char const *config_socket() + { + static Config_attr socket("socket", ""); + return socket.string(); + } } int Libc::Vfs_plugin::access(const char *path, int amode) @@ -797,3 +804,17 @@ int Libc::Vfs_plugin::select(int nfds, } return nready; } + +namespace Libc { + + bool read_ready(Libc::File_descriptor *fd) + { + Vfs::Vfs_handle *handle = vfs_handle(fd); + if (!handle) return false; + + handle->fs().notify_read_ready(handle); + + return handle->fs().read_ready(handle); + } + +}