Noux: add proper errno handling to network funcs

Programs use the errno value to check which error exactly occured.
It is mandatory for non-blocking I/O, e.g. connect() gets the current
state of the connection by looking at the returned errno values.

Fixes #337.
This commit is contained in:
Josef Söntgen 2012-08-24 17:04:02 +02:00 committed by Norman Feske
parent aae3ce348e
commit 9cff243f42
3 changed files with 285 additions and 24 deletions

View File

@ -299,6 +299,42 @@ namespace Noux {
MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM,
MKDIR_ERR_NAME_TOO_LONG};
enum Read_error { READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK,
READ_ERR_INVALID, READ_ERR_IO };
enum Write_error { WRITE_ERR_AGAIN, WRITE_ERR_WOULD_BLOCK,
WRITE_ERR_INVALID, WRITE_ERR_IO };
/**
* Socket related errors
*/
enum Accept_error { ACCEPT_ERR_AGAIN, ACCEPT_ERR_WOULD_BLOCK,
ACCEPT_ERR_INVALID, ACCEPT_ERR_NO_MEMORY,
ACCEPT_ERR_NOT_SUPPORTED };
enum Bind_error { BIND_ERR_ACCESS, BIND_ERR_ADDR_IN_USE,
BIND_ERR_INVALID, BIND_ERR_NO_MEMORY };
enum Connect_error { CONNECT_ERR_ACCESS, CONNECT_ERR_AGAIN,
CONNECT_ERR_ALREADY, CONNECT_ERR_CONN_REFUSED,
CONNECT_ERR_NO_PERM, CONNECT_ERR_ADDR_IN_USE,
CONNECT_ERR_IN_PROGRESS, CONNECT_ERR_IS_CONNECTED };
enum Listen_error { LISTEN_ERR_ADDR_IN_USE, LISTEN_ERR_NOT_SUPPORTED };
enum Recv_error { RECV_ERR_AGAIN, RECV_ERR_WOULD_BLOCK,
RECV_ERR_CONN_REFUSED, RECV_ERR_INVALID,
RECV_ERR_NOT_CONNECTED, RECV_ERR_NO_MEMORY };
enum Send_error { SEND_ERR_AGAIN, SEND_ERR_WOULD_BLOCK,
SEND_ERR_CONNECTION_RESET, SEND_ERR_INVALID,
SEND_ERR_IS_CONNECTED, SEND_ERR_NO_MEMORY };
enum Shutdown_error { SHUTDOWN_ERR_NOT_CONNECTED };
enum Socket_error { SOCKET_ERR_ACCESS, SOCKET_ERR_NO_AF_SUPPORT,
SOCKET_ERR_INVALID, SOCKET_ERR_NO_MEMORY };
union {
General_error general;
Stat_error stat;
@ -310,6 +346,16 @@ namespace Noux {
Unlink_error unlink;
Rename_error rename;
Mkdir_error mkdir;
Read_error read;
Write_error write;
Accept_error accept;
Bind_error bind;
Connect_error connect;
Listen_error listen;
Recv_error recv;
Send_error send;
Shutdown_error shutdown;
Socket_error socket;
} error;
union {

View File

@ -774,7 +774,18 @@ namespace {
Genode::memcpy(sysio()->write_in.chunk, src, curr_count);
if (!noux()->syscall(Noux::Session::SYSCALL_WRITE)) {
PERR("write error %d (fd %d)", sysio()->error.general, noux_fd(fd->context));
switch (sysio()->error.write) {
case Noux::Sysio::WRITE_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::WRITE_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::WRITE_ERR_IO: errno = EIO; break;
default:
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
errno = EBADF;
else
errno = 0;
break;
}
}
count -= curr_count;
@ -797,8 +808,18 @@ namespace {
sysio()->read_in.count = curr_count;
if (!noux()->syscall(Noux::Session::SYSCALL_READ)) {
PERR("read error");
/* XXX set errno */
switch (sysio()->error.read) {
case Noux::Sysio::READ_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::READ_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::READ_ERR_IO: errno = EIO; break;
default:
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
errno = EBADF;
else
errno = 0;
break;
}
return -1;
}
@ -1321,6 +1342,15 @@ namespace {
}
if (!noux()->syscall(Noux::Session::SYSCALL_ACCEPT)) {
switch (sysio()->error.accept) {
case Noux::Sysio::ACCEPT_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::ACCEPT_ERR_NO_MEMORY: errno = ENOMEM; break;
case Noux::Sysio::ACCEPT_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::ACCEPT_ERR_NOT_SUPPORTED: errno = EOPNOTSUPP; break;
case Noux::Sysio::ACCEPT_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
default: errno = 0; break;
}
return 0;
}
@ -1330,7 +1360,6 @@ namespace {
Libc::Plugin_context *context = noux_context(sysio()->accept_out.fd);
return Libc::file_descriptor_allocator()->alloc(this, context,
sysio()->accept_out.fd);
}
@ -1343,7 +1372,13 @@ namespace {
sysio()->bind_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_BIND)) {
errno = EACCES;
switch (sysio()->error.bind) {
case Noux::Sysio::BIND_ERR_ACCESS: errno = EACCES; break;
case Noux::Sysio::BIND_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
case Noux::Sysio::BIND_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::BIND_ERR_NO_MEMORY: errno = ENOMEM; break;
default: errno = 0; break;
}
return -1;
}
@ -1360,7 +1395,14 @@ namespace {
sysio()->connect_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_CONNECT)) {
/* XXX errno */
switch (sysio()->error.connect) {
case Noux::Sysio::CONNECT_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::CONNECT_ERR_ALREADY: errno = EALREADY; break;
case Noux::Sysio::CONNECT_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
case Noux::Sysio::CONNECT_ERR_IN_PROGRESS: errno = EINPROGRESS; break;
case Noux::Sysio::CONNECT_ERR_IS_CONNECTED: errno = EISCONN; break;
default: errno = 0; break;
}
return -1;
}
@ -1393,7 +1435,11 @@ namespace {
sysio()->listen_in.backlog = backlog;
if (!noux()->syscall(Noux::Session::SYSCALL_LISTEN)) {
/* errno = EACCES; */
switch (sysio()->error.listen) {
case Noux::Sysio::LISTEN_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
case Noux::Sysio::LISTEN_ERR_NOT_SUPPORTED: errno = EOPNOTSUPP; break;
default: errno = 0; break;
}
return -1;
}
@ -1413,7 +1459,13 @@ namespace {
sysio()->recv_in.len = curr_len;
if (!noux()->syscall(Noux::Session::SYSCALL_RECV)) {
/* XXX set errno */
switch (sysio()->error.recv) {
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::RECV_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::RECV_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
default: errno = 0; break;
}
return -1;
}
@ -1452,7 +1504,13 @@ namespace {
sysio()->recvfrom_in.addrlen = *addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_RECVFROM)) {
/* XXX set errno */
switch (sysio()->error.recv) {
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::RECV_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::RECV_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
default: errno = 0; break;
}
return -1;
}
@ -1495,6 +1553,16 @@ namespace {
if (!noux()->syscall(Noux::Session::SYSCALL_SEND)) {
PERR("write error %d", sysio()->error.general);
switch (sysio()->error.send) {
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::SEND_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::SEND_ERR_CONNECTION_RESET: errno = ECONNRESET; break;
case Noux::Sysio::SEND_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::SEND_ERR_IS_CONNECTED: errno = EISCONN; break;
case Noux::Sysio::SEND_ERR_NO_MEMORY: errno = ENOMEM; break;
default: errno = 0; break;
}
/* return foo */
}
len -= curr_len;
@ -1510,7 +1578,7 @@ namespace {
int const orig_count = len;
if (addrlen > sizeof (sysio()->sendto_in.dest_addr)) {
/* XXX errno */
errno = 0; /* XXX */
return -1;
}
@ -1535,6 +1603,15 @@ namespace {
}
if (!noux()->syscall(Noux::Session::SYSCALL_SENDTO)) {
switch (sysio()->error.send) {
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::SEND_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::SEND_ERR_CONNECTION_RESET: errno = ECONNRESET; break;
case Noux::Sysio::SEND_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::SEND_ERR_IS_CONNECTED: errno = EISCONN; break;
case Noux::Sysio::SEND_ERR_NO_MEMORY: errno = ENOMEM; break;
default: errno = 0; break;
}
return -1;
}
@ -1552,6 +1629,10 @@ namespace {
sysio()->shutdown_in.how = how;
if (!noux()->syscall(Noux::Session::SYSCALL_SHUTDOWN)) {
switch (sysio()->error.shutdown) {
case Noux::Sysio::SHUTDOWN_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
default: errno = 0; break;
}
return -1;
}

View File

@ -28,6 +28,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
namespace Noux {
@ -119,7 +120,7 @@ namespace Noux {
bool write(Sysio *sysio, size_t &count)
{
size_t result = ::write(_socket, sysio->write_in.chunk,
ssize_t result = ::write(_socket, sysio->write_in.chunk,
sysio->write_in.count);
if (result > -1) {
@ -129,6 +130,15 @@ namespace Noux {
return true;
}
switch (errno) {
/* case EAGAIN: sysio->error.read = Sysio::READ_ERR_AGAIN; break; */
case EWOULDBLOCK: sysio->error.read = Sysio::READ_ERR_WOULD_BLOCK; break;
case EINVAL: sysio->error.read = Sysio::READ_ERR_INVALID; break;
case EIO: sysio->error.read = Sysio::READ_ERR_IO; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
return false;
}
@ -138,10 +148,23 @@ namespace Noux {
Genode::min(sysio->read_in.count,
sizeof(sysio->read_out.chunk));
sysio->read_out.count = ::read(_socket, sysio->read_out.chunk,
max_count);
ssize_t result = ::read(_socket, sysio->read_out.chunk, max_count);
return true;
if (result > -1) {
sysio->read_out.count = result;
return true;
}
switch (errno) {
/* case EAGAIN: sysio->error.read = Sysio::READ_ERR_AGAIN; break; */
case EWOULDBLOCK: sysio->error.read = Sysio::READ_ERR_WOULD_BLOCK; break;
case EINVAL: sysio->error.read = Sysio::READ_ERR_INVALID; break;
case EIO: sysio->error.read = Sysio::READ_ERR_IO; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
return false;
}
bool socket(Sysio *sysio)
@ -200,19 +223,58 @@ namespace Noux {
&sysio->accept_in.addrlen);
}
if (result == -1) {
switch (errno) {
/* case EAGAIN: sysio->error.accept = Sysio::ACCEPT_ERR_AGAIN; break; */
case ENOMEM: sysio->error.accept = Sysio::ACCEPT_ERR_NO_MEMORY; break;
case EINVAL: sysio->error.accept = Sysio::ACCEPT_ERR_INVALID; break;
case EOPNOTSUPP: sysio->error.accept = Sysio::ACCEPT_ERR_NOT_SUPPORTED; break;
case EWOULDBLOCK: sysio->error.accept = Sysio::ACCEPT_ERR_WOULD_BLOCK; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
int bind(Sysio *sysio)
{
return ::bind(_socket, (const struct sockaddr *)&sysio->bind_in.addr,
sysio->bind_in.addrlen);
int result = ::bind(_socket, (const struct sockaddr *)&sysio->bind_in.addr,
sysio->bind_in.addrlen);
if (result == -1) {
switch (errno) {
case EACCES: sysio->error.bind = Sysio::BIND_ERR_ACCESS; break;
case EADDRINUSE: sysio->error.bind = Sysio::BIND_ERR_ADDR_IN_USE; break;
case EINVAL: sysio->error.bind = Sysio::BIND_ERR_INVALID; break;
case ENOMEM: sysio->error.bind = Sysio::BIND_ERR_NO_MEMORY; break;
default:
PERR("unhandled errno: %d", errno); break;
}
}
return result;
}
int connect(Sysio *sysio)
{
return ::connect(_socket, (struct sockaddr *)&sysio->connect_in.addr,
sysio->connect_in.addrlen);
int result = ::connect(_socket, (struct sockaddr *)&sysio->connect_in.addr,
sysio->connect_in.addrlen);
if (result == -1) {
switch (errno) {
case EAGAIN: sysio->error.connect = Sysio::CONNECT_ERR_AGAIN; break;
case EALREADY: sysio->error.connect = Sysio::CONNECT_ERR_ALREADY; break;
case EADDRINUSE: sysio->error.connect = Sysio::CONNECT_ERR_ADDR_IN_USE; break;
case EINPROGRESS: sysio->error.connect = Sysio::CONNECT_ERR_IN_PROGRESS; break;
case EISCONN: sysio->error.connect = Sysio::CONNECT_ERR_IS_CONNECTED; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
int getpeername(Sysio *sysio)
@ -230,13 +292,36 @@ namespace Noux {
int listen(Sysio *sysio)
{
return ::listen(_socket, sysio->listen_in.backlog);
int result = ::listen(_socket, sysio->listen_in.backlog);
if (result == -1) {
switch (errno) {
case EADDRINUSE: sysio->error.listen = Sysio::LISTEN_ERR_ADDR_IN_USE; break;
case EOPNOTSUPP: sysio->error.listen = Sysio::LISTEN_ERR_NOT_SUPPORTED; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
ssize_t recv(Sysio *sysio)
{
return ::recv(_socket, sysio->recv_in.buf, sysio->recv_in.len,
sysio->recv_in.flags);
ssize_t result = ::recv(_socket, sysio->recv_in.buf, sysio->recv_in.len, sysio->recv_in.flags);
if (result == -1) {
switch (errno) {
/*case EAGAIN: sysio->error.recv = Sysio::RECV_ERR_AGAIN; break; */
case EWOULDBLOCK: sysio->error.recv = Sysio::RECV_ERR_WOULD_BLOCK; break;
case EINVAL: sysio->error.recv = Sysio::RECV_ERR_INVALID; break;
case ENOTCONN: sysio->error.recv = Sysio::RECV_ERR_NOT_CONNECTED; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
ssize_t recvfrom(Sysio *sysio)
@ -245,13 +330,39 @@ namespace Noux {
sysio->recv_in.flags, (struct sockaddr *)&sysio->recvfrom_in.src_addr,
&sysio->recvfrom_in.addrlen);
if (result == -1) {
switch (errno) {
/*case EAGAIN: sysio->error.recv = Sysio::RECV_ERR_AGAIN; break; */
case EWOULDBLOCK: sysio->error.recv = Sysio::RECV_ERR_WOULD_BLOCK; break;
case EINVAL: sysio->error.recv = Sysio::RECV_ERR_INVALID; break;
case ENOTCONN: sysio->error.recv = Sysio::RECV_ERR_NOT_CONNECTED; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
ssize_t send(Sysio *sysio)
{
return ::send(_socket, sysio->send_in.buf, sysio->send_in.len,
sysio->send_in.flags);
ssize_t result = ::send(_socket, sysio->send_in.buf, sysio->send_in.len,
sysio->send_in.flags);
if (result == -1) {
switch (errno) {
/*case EAGAIN: sysio->error.send = Sysio::SEND_ERR_AGAIN; break; */
case EWOULDBLOCK: sysio->error.send = Sysio::SEND_ERR_WOULD_BLOCK; break;
case ECONNRESET: sysio->error.send = Sysio::SEND_ERR_CONNECTION_RESET; break;
case EINVAL: sysio->error.send = Sysio::SEND_ERR_INVALID; break;
case EISCONN: sysio->error.send = Sysio::SEND_ERR_IS_CONNECTED; break;
case ENOMEM: sysio->error.send = Sysio::SEND_ERR_NO_MEMORY; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
ssize_t sendto(Sysio *sysio)
@ -261,12 +372,35 @@ namespace Noux {
(const struct sockaddr *) &sysio->sendto_in.dest_addr,
sysio->sendto_in.addrlen);
if (result == -1) {
switch (errno) {
/*case EAGAIN: sysio->error.send = Sysio::SEND_ERR_AGAIN; break; */
case EWOULDBLOCK: sysio->error.send = Sysio::SEND_ERR_WOULD_BLOCK; break;
case ECONNRESET: sysio->error.send = Sysio::SEND_ERR_CONNECTION_RESET; break;
case EINVAL: sysio->error.send = Sysio::SEND_ERR_INVALID; break;
case EISCONN: sysio->error.send = Sysio::SEND_ERR_IS_CONNECTED; break;
case ENOMEM: sysio->error.send = Sysio::SEND_ERR_NO_MEMORY; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
int shutdown(Sysio *sysio)
{
return ::shutdown(_socket, sysio->shutdown_in.how);
int result = ::shutdown(_socket, sysio->shutdown_in.how);
if (result == -1) {
switch (errno) {
case ENOTCONN: sysio->error.shutdown = Sysio::SHUTDOWN_ERR_NOT_CONNECTED; break;
default:
PDBG("unhandled errno: %d", errno); break;
}
}
return result;
}
};
}