diff --git a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc index 3360cb4472..c8b905b0be 100644 --- a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc +++ b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc @@ -635,7 +635,24 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file ** File interface ** ********************/ - bool poll(bool, Vfs::Vfs_handle::Context *) { return true; } + bool poll(bool trigger_io_response, Vfs::Vfs_handle::Context *context) + { + /* + * The connect file is considered readable when the socket is + * writeable (connected or error). + */ + + using namespace Linux; + + file f; + f.f_flags = 0; + if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLOUT_SET)) { + if (trigger_io_response) + _parent.trigger_io_response(context); + return true; + } + return false; + } Lxip::ssize_t write(Lxip_vfs_file_handle &handle, char const *src, Genode::size_t len, @@ -657,12 +674,12 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file addr->sin_addr.s_addr = get_addr(handle.content_buffer); addr->sin_family = AF_INET; - _write_err = _sock.ops->connect(&_sock, (sockaddr *)addr, sizeof(addr_storage), 0); + _write_err = _sock.ops->connect(&_sock, (sockaddr *)addr, sizeof(addr_storage), O_NONBLOCK); switch (_write_err) { case Lxip::Io_result::LINUX_EINPROGRESS: _connecting = true; - return -1; + return len; case Lxip::Io_result::LINUX_EALREADY: return -1; @@ -679,6 +696,7 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file default: if (_write_err != 0) return -1; + _is_connected = true; break; } @@ -691,6 +709,29 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file return len; } + + Lxip::ssize_t read(Lxip_vfs_file_handle &handle, + char *dst, Genode::size_t len, + file_size /* ignored */) override + { + int so_error = 0; + int opt_len = sizeof(so_error); + int res = sock_getsockopt(&_sock, SOL_SOCKET, SO_ERROR, (char*)&so_error, &opt_len); + + if (res != 0) { + Genode::error("Vfs::Lxip_connect_file::read(): getsockopt() failed"); + return -1; + } + + switch (so_error) { + case 0: + return Genode::snprintf(dst, len, "connected"); + case Linux::ECONNREFUSED: + return Genode::snprintf(dst, len, "connection refused"); + default: + return Genode::snprintf(dst, len, "unknown error"); + } + } }; diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.cc b/repos/libports/src/lib/libc/socket_fs_plugin.cc index ce3eb6c74b..fa0366ed9a 100644 --- a/repos/libports/src/lib/libc/socket_fs_plugin.cc +++ b/repos/libports/src/lib/libc/socket_fs_plugin.cc @@ -114,6 +114,8 @@ struct Socket_fs::Context : Libc::Plugin_context enum Proto { TCP, UDP }; + enum State { UNCONNECTED, ACCEPT_ONLY, CONNECTING, CONNECTED, CONNECT_ABORTED }; + struct Inaccessible { }; /* exception */ Absolute_path const path { @@ -141,7 +143,7 @@ struct Socket_fs::Context : Libc::Plugin_context Proto const _proto; - bool _accept_only = false; + State _state { UNCONNECTED }; template void _fd_apply(FUNC const &fn) @@ -196,7 +198,7 @@ struct Socket_fs::Context : Libc::Plugin_context } int data_fd() { return _fd_for_type(Fd::DATA, O_RDWR); } - int connect_fd() { return _fd_for_type(Fd::CONNECT, O_WRONLY); } + int connect_fd() { return _fd_for_type(Fd::CONNECT, O_RDWR); } int bind_fd() { return _fd_for_type(Fd::BIND, O_WRONLY); } int listen_fd() { return _fd_for_type(Fd::LISTEN, O_WRONLY); } int accept_fd() { return _fd_for_type(Fd::ACCEPT, O_RDONLY); } @@ -204,16 +206,57 @@ struct Socket_fs::Context : Libc::Plugin_context int remote_fd() { return _fd_for_type(Fd::REMOTE, O_RDWR); } /* request the appropriate fd to ensure the file is open */ - bool data_read_ready() { data_fd(); return _fd_read_ready(Fd::DATA); } - bool accept_read_ready() { accept_fd(); return _fd_read_ready(Fd::ACCEPT); } - bool local_read_ready() { local_fd(); return _fd_read_ready(Fd::LOCAL); } - bool remote_read_ready() { remote_fd(); return _fd_read_ready(Fd::REMOTE); } + bool connect_read_ready() { connect_fd(); return _fd_read_ready(Fd::CONNECT); } + bool data_read_ready() { data_fd(); return _fd_read_ready(Fd::DATA); } + bool accept_read_ready() { accept_fd(); return _fd_read_ready(Fd::ACCEPT); } + bool local_read_ready() { local_fd(); return _fd_read_ready(Fd::LOCAL); } + bool remote_read_ready() { remote_fd(); return _fd_read_ready(Fd::REMOTE); } - void accept_only() { _accept_only = true; } + void state(State state) { _state = state; } + State state() const { return _state; } bool read_ready() { - return _accept_only ? accept_read_ready() : data_read_ready(); + return (_state == ACCEPT_ONLY) ? accept_read_ready() : data_read_ready(); + } + + bool write_ready() + { + if (_state == CONNECTING) + return connect_read_ready(); + + /* XXX ask if "data" is writeable */ + return true; + } + + /* + * Read the connect status from the connect file and return 0 if connected + * or -1 with errno set to the error code. + */ + int read_connect_status() + { + char connect_status[32] = { 0 }; + ssize_t connect_status_len; + + connect_status_len = read(connect_fd(), connect_status, + sizeof(connect_status)); + + if (connect_status_len <= 0) { + Genode::error("socket_fs: reading from the connect file failed"); + return -1; + } + + if (strcmp(connect_status, "connected") == 0) + return 0; + + if (strcmp(connect_status, "connection refused") == 0) + return Errno(ECONNREFUSED); + + if (strcmp(connect_status, "not connected") == 0) + return Errno(ENOTCONN); + + Genode::error("socket_fs: unhandled connection state"); + return Errno(ECONNREFUSED); } }; @@ -566,23 +609,81 @@ extern "C" int socket_fs_connect(int libc_fd, sockaddr const *addr, socklen_t ad return Errno(EAFNOSUPPORT); } - /* TODO EISCONN */ - /* TODO ECONNREFUSED */ - /* TODO maybe EALREADY, EINPROGRESS, ETIMEDOUT */ + switch (context->state()) { + case Context::UNCONNECTED: + { + Sockaddr_string addr_string; + try { + addr_string = Sockaddr_string(host_string(*(sockaddr_in const *)addr), + port_string(*(sockaddr_in const *)addr)); + } + catch (Address_conversion_failed) { return Errno(EINVAL); } - Sockaddr_string addr_string; - try { - addr_string = Sockaddr_string(host_string(*(sockaddr_in const *)addr), - port_string(*(sockaddr_in const *)addr)); + context->state(Context::CONNECTING); + + int const len = strlen(addr_string.base()); + int const n = write(context->connect_fd(), addr_string.base(), len); + + if (n != len) return Errno(ECONNREFUSED); + + if (context->fd_flags() & O_NONBLOCK) + return Errno(EINPROGRESS); + + /* block until socket is ready for writing */ + + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(libc_fd, &writefds); + + enum { CONNECT_TIMEOUT_S = 10 }; + struct timeval timeout {CONNECT_TIMEOUT_S, 0}; + int res = select(libc_fd + 1, NULL, &writefds, NULL, &timeout); + + if (res < 0) { + /* errno has been set by select() */ + return res; + } + + if (res == 0) { + context->state(Context::CONNECT_ABORTED); + return Errno(ETIMEDOUT); + } + + int connect_status = context->read_connect_status(); + + if (connect_status == 0) + context->state(Context::CONNECTED); + else + context->state(Context::CONNECT_ABORTED); + + /* errno has been set by context->read_connect_status() */ + return connect_status; + } + break; + case Context::ACCEPT_ONLY: + return Errno(EINVAL); + case Context::CONNECTING: + { + if (!context->connect_read_ready()) + return Errno(EALREADY); + + int connect_status = context->read_connect_status(); + + if (connect_status == 0) + context->state(Context::CONNECTED); + else + context->state(Context::CONNECT_ABORTED); + + /* errno was set by context->read_connect_status() */ + return connect_status; + } + case Context::CONNECTED: + return Errno(EISCONN); + case Context::CONNECT_ABORTED: + return Errno(ECONNABORTED); } - catch (Address_conversion_failed) { return Errno(EINVAL); } - int const len = strlen(addr_string.base()); - int const n = write(context->connect_fd(), addr_string.base(), len); - if (n != len) return Errno(ECONNREFUSED); - - /* sync to block for write completion */ - return fsync(context->connect_fd()); + return Errno(ECONNREFUSED); } @@ -599,7 +700,7 @@ extern "C" int socket_fs_listen(int libc_fd, int backlog) int const n = write(context->listen_fd(), buf, len); if (n != len) return Errno(EOPNOTSUPP); - context->accept_only(); + context->state(Context::ACCEPT_ONLY); return 0; } @@ -732,6 +833,21 @@ extern "C" int socket_fs_getsockopt(int libc_fd, int level, int optname, *(int *)optval = 1; return 0; case SO_ERROR: + if (context->state() == Context::CONNECTING) { + + int connect_status = context->read_connect_status(); + + if (connect_status == 0) { + *(int*)optval = 0; + context->state(Context::CONNECTED); + } else { + *(int*)optval = errno; + context->state(Context::CONNECT_ABORTED); + } + + return 0; + } + /* not yet implemented - but return true */ *(int *)optval = 0; return 0; @@ -948,10 +1064,14 @@ int Socket_fs::Plugin::select(int nfds, } if (FD_ISSET(fd, &in_writefds)) { - if (true /* XXX ask if "data" is writeable */) { - FD_SET(fd, writefds); - ++nready; - } + try { + Socket_fs::Context *context = dynamic_cast(fdo->context); + + if (context->write_ready()) { + FD_SET(fd, writefds); + ++nready; + } + } catch (Socket_fs::Context::Inaccessible) { } } /* XXX exceptfds not supported */ diff --git a/repos/libports/src/lib/vfs/lwip/vfs.cc b/repos/libports/src/lib/vfs/lwip/vfs.cc index 5f9eea2ffa..bccb4d14ef 100644 --- a/repos/libports/src/lib/vfs/lwip/vfs.cc +++ b/repos/libports/src/lib/vfs/lwip/vfs.cc @@ -827,9 +827,11 @@ class Lwip::Udp_socket_dir final : case Lwip_file_handle::CONNECT: { /* check if the PCB was connected */ - if (ip_addr_isany(&_pcb->remote_ip)) - return Read_result::READ_OK; - /* otherwise fallthru to REMOTE*/ + if (!ip_addr_isany(&_pcb->remote_ip)) + out_count = Genode::snprintf(dst, count, "connected"); + else + out_count = Genode::snprintf(dst, count, "not connected"); + return Read_result::READ_OK; } case Lwip_file_handle::REMOTE: { @@ -1162,7 +1164,11 @@ class Lwip::Tcp_socket_dir final : break; case Lwip_file_handle::CONNECT: - return !ip_addr_isany(&_pcb->remote_ip); + /* + * The connect file is considered readable when the socket is + * writeable (connected or error). + */ + return ((state == READY) || (state == CLOSED)); case Lwip_file_handle::LOCATION: case Lwip_file_handle::LOCAL: @@ -1303,6 +1309,15 @@ class Lwip::Tcp_socket_dir final : break; case Lwip_file_handle::CONNECT: + switch (state) { + case READY: + out_count = Genode::snprintf(dst, count, "connected"); + break; + default: + out_count = Genode::snprintf(dst, count, "connection refused"); + break; + } + return Read_result::READ_OK; case Lwip_file_handle::LISTEN: case Lwip_file_handle::INVALID: break; }