mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
parent
12c10dbcd1
commit
e8ea28d897
@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 <typename FUNC>
|
||||
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<Socket_fs::Context *>(fdo->context);
|
||||
|
||||
if (context->write_ready()) {
|
||||
FD_SET(fd, writefds);
|
||||
++nready;
|
||||
}
|
||||
} catch (Socket_fs::Context::Inaccessible) { }
|
||||
}
|
||||
|
||||
/* XXX exceptfds not supported */
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user