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 <base/env.h> +#include <base/log.h> +#include <vfs/types.h> +#include <util/string.h> + +/* libc includes */ +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdio.h> + +/* 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 <int CAPACITY> 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<NI_MAXHOST> Host_string; + typedef String<NI_MAXSERV> 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<NI_MAXHOST + NI_MAXSERV> + { + 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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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<Socket_context *>(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 <sys/types.h> +#include <sys/socket.h> + +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 <util/token.h> /* libc includes */ -#include <sys/types.h> -#include <sys/socket.h> #include <errno.h> /* 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); + } + +}