mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 22:47:50 +00:00
noux: API transition
This patch removes the dependency of the deprecated Genode API, fixes the coding style, and removes the "random" file system (superseded by the VFS plugin mechanism). Ref #1987
This commit is contained in:
parent
f957fcdd98
commit
82e6d7cf52
@ -30,479 +30,480 @@
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
using namespace Genode;
|
||||
struct Sysio;
|
||||
}
|
||||
|
||||
struct Sysio
|
||||
|
||||
struct Noux::Sysio
|
||||
{
|
||||
/* signal numbers must match with libc signal numbers */
|
||||
enum Signal {
|
||||
SIG_INT = 2,
|
||||
SIG_CHLD = 20,
|
||||
};
|
||||
|
||||
enum { SIGNAL_QUEUE_SIZE = 32 };
|
||||
Ring_buffer<enum Signal, SIGNAL_QUEUE_SIZE,
|
||||
Ring_buffer_unsynchronized> pending_signals;
|
||||
|
||||
enum { MAX_PATH_LEN = 512 };
|
||||
typedef char Path[MAX_PATH_LEN];
|
||||
|
||||
enum { CHUNK_SIZE = 11*1024 };
|
||||
typedef char Chunk[CHUNK_SIZE];
|
||||
|
||||
enum { ARGS_MAX_LEN = 5*1024 };
|
||||
typedef char Args[ARGS_MAX_LEN];
|
||||
|
||||
enum { ENV_MAX_LEN = 6*1024 };
|
||||
typedef char Env[ENV_MAX_LEN];
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef long int ssize_t;
|
||||
|
||||
/**
|
||||
* Flags of 'mode' argument of open syscall
|
||||
*/
|
||||
enum {
|
||||
OPEN_MODE_RDONLY = 0,
|
||||
OPEN_MODE_WRONLY = 1,
|
||||
OPEN_MODE_RDWR = 2,
|
||||
OPEN_MODE_ACCMODE = 3,
|
||||
OPEN_MODE_CREATE = 0x0800, /* libc O_EXCL */
|
||||
};
|
||||
|
||||
/**
|
||||
* These values are the same as in the FreeBSD libc
|
||||
*/
|
||||
enum {
|
||||
STAT_MODE_SYMLINK = 0120000,
|
||||
STAT_MODE_FILE = 0100000,
|
||||
STAT_MODE_DIRECTORY = 0040000,
|
||||
STAT_MODE_CHARDEV = 0020000,
|
||||
STAT_MODE_BLOCKDEV = 0060000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Must be POD (in contrast to the VFS type) because it's used in a union
|
||||
*/
|
||||
struct Stat
|
||||
{
|
||||
/* signal numbers must match with libc signal numbers */
|
||||
enum Signal {
|
||||
SIG_INT = 2,
|
||||
SIG_CHLD = 20,
|
||||
};
|
||||
Vfs::file_size size;
|
||||
unsigned mode;
|
||||
unsigned uid;
|
||||
unsigned gid;
|
||||
unsigned long inode;
|
||||
unsigned long device;
|
||||
|
||||
enum { SIGNAL_QUEUE_SIZE = 32 };
|
||||
Ring_buffer<enum Signal, SIGNAL_QUEUE_SIZE,
|
||||
Ring_buffer_unsynchronized> pending_signals;
|
||||
|
||||
enum { MAX_PATH_LEN = 512 };
|
||||
typedef char Path[MAX_PATH_LEN];
|
||||
|
||||
enum { CHUNK_SIZE = 11*1024 };
|
||||
typedef char Chunk[CHUNK_SIZE];
|
||||
|
||||
enum { ARGS_MAX_LEN = 5*1024 };
|
||||
typedef char Args[ARGS_MAX_LEN];
|
||||
|
||||
enum { ENV_MAX_LEN = 6*1024 };
|
||||
typedef char Env[ENV_MAX_LEN];
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef long int ssize_t;
|
||||
|
||||
/**
|
||||
* Flags of 'mode' argument of open syscall
|
||||
*/
|
||||
enum {
|
||||
OPEN_MODE_RDONLY = 0,
|
||||
OPEN_MODE_WRONLY = 1,
|
||||
OPEN_MODE_RDWR = 2,
|
||||
OPEN_MODE_ACCMODE = 3,
|
||||
OPEN_MODE_CREATE = 0x0800, /* libc O_EXCL */
|
||||
};
|
||||
|
||||
/**
|
||||
* These values are the same as in the FreeBSD libc
|
||||
*/
|
||||
enum {
|
||||
STAT_MODE_SYMLINK = 0120000,
|
||||
STAT_MODE_FILE = 0100000,
|
||||
STAT_MODE_DIRECTORY = 0040000,
|
||||
STAT_MODE_CHARDEV = 0020000,
|
||||
STAT_MODE_BLOCKDEV = 0060000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Must be POD (in contrast to the VFS type) because it's used in a union
|
||||
*/
|
||||
struct Stat
|
||||
Stat & operator= (Vfs::Directory_service::Stat const &stat)
|
||||
{
|
||||
Vfs::file_size size;
|
||||
unsigned mode;
|
||||
unsigned uid;
|
||||
unsigned gid;
|
||||
unsigned long inode;
|
||||
unsigned long device;
|
||||
size = stat.size;
|
||||
mode = stat.mode;
|
||||
uid = stat.uid;
|
||||
gid = stat.gid;
|
||||
inode = stat.inode;
|
||||
device = stat.device;
|
||||
|
||||
Stat & operator= (Vfs::Directory_service::Stat const &stat)
|
||||
{
|
||||
size = stat.size;
|
||||
mode = stat.mode;
|
||||
uid = stat.uid;
|
||||
gid = stat.gid;
|
||||
inode = stat.inode;
|
||||
device = stat.device;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Argument structure used for ioctl syscall
|
||||
*/
|
||||
struct Ioctl_in
|
||||
{
|
||||
typedef Vfs::File_io_service::Ioctl_opcode Opcode;
|
||||
|
||||
/**
|
||||
* Argument structure used for ioctl syscall
|
||||
*/
|
||||
struct Ioctl_in
|
||||
typedef Vfs::File_io_service::Ioctl_value Value;
|
||||
|
||||
Opcode request;
|
||||
int argp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure carrying the result values of 'ioctl' syscalls
|
||||
*/
|
||||
typedef Vfs::File_io_service::Ioctl_out Ioctl_out;
|
||||
|
||||
enum Lseek_whence { LSEEK_SET, LSEEK_CUR, LSEEK_END };
|
||||
|
||||
enum { DIRENT_MAX_NAME_LEN = Vfs::Directory_service::DIRENT_MAX_NAME_LEN };
|
||||
|
||||
typedef Vfs::Directory_service::Dirent_type Dirent_type;
|
||||
|
||||
/*
|
||||
* Must be POD (in contrast to the VFS type) because it's used in a union
|
||||
*/
|
||||
struct Dirent
|
||||
{
|
||||
unsigned long fileno;
|
||||
Dirent_type type;
|
||||
char name[DIRENT_MAX_NAME_LEN];
|
||||
|
||||
Dirent & operator= (Vfs::Directory_service::Dirent const &dirent)
|
||||
{
|
||||
typedef Vfs::File_io_service::Ioctl_opcode Opcode;
|
||||
fileno = dirent.fileno;
|
||||
type = dirent.type;
|
||||
memcpy(name, dirent.name, DIRENT_MAX_NAME_LEN);
|
||||
|
||||
typedef Vfs::File_io_service::Ioctl_value Value;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
Opcode request;
|
||||
int argp;
|
||||
};
|
||||
enum Fcntl_cmd {
|
||||
FCNTL_CMD_GET_FILE_STATUS_FLAGS,
|
||||
FCNTL_CMD_SET_FILE_STATUS_FLAGS,
|
||||
FCNTL_CMD_SET_FD_FLAGS
|
||||
};
|
||||
|
||||
/**
|
||||
* Input and output argument type of select syscall
|
||||
*/
|
||||
struct Select_fds
|
||||
{
|
||||
/**
|
||||
* Maximum number of file descriptors supported
|
||||
*/
|
||||
enum { MAX_FDS = 32U };
|
||||
|
||||
/**
|
||||
* Structure carrying the result values of 'ioctl' syscalls
|
||||
* Number of file descriptors to watch for read operations (rd),
|
||||
* write operations (wr), or exceptions (ex).
|
||||
*/
|
||||
typedef Vfs::File_io_service::Ioctl_out Ioctl_out;
|
||||
size_t num_rd,
|
||||
num_wr,
|
||||
num_ex;
|
||||
|
||||
enum Lseek_whence { LSEEK_SET, LSEEK_CUR, LSEEK_END };
|
||||
|
||||
enum { DIRENT_MAX_NAME_LEN = Vfs::Directory_service::DIRENT_MAX_NAME_LEN };
|
||||
|
||||
typedef Vfs::Directory_service::Dirent_type Dirent_type;
|
||||
|
||||
/*
|
||||
* Must be POD (in contrast to the VFS type) because it's used in a union
|
||||
/**
|
||||
* Array containing the file descriptors, starting with those
|
||||
* referring to rd, followed by wr, and finally ex
|
||||
*/
|
||||
struct Dirent
|
||||
int array[MAX_FDS];
|
||||
|
||||
/**
|
||||
* Return total number of file descriptors contained in the array
|
||||
*/
|
||||
size_t total_fds() const {
|
||||
return min(num_rd + num_wr + num_ex, (size_t)MAX_FDS); }
|
||||
|
||||
/**
|
||||
* Check for maximum population of fds array
|
||||
*/
|
||||
bool max_fds_exceeded() const
|
||||
{
|
||||
unsigned long fileno;
|
||||
Dirent_type type;
|
||||
char name[DIRENT_MAX_NAME_LEN];
|
||||
|
||||
Dirent & operator= (Vfs::Directory_service::Dirent const &dirent)
|
||||
{
|
||||
fileno = dirent.fileno;
|
||||
type = dirent.type;
|
||||
memcpy(name, dirent.name, DIRENT_MAX_NAME_LEN);
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
enum Fcntl_cmd {
|
||||
FCNTL_CMD_GET_FILE_STATUS_FLAGS,
|
||||
FCNTL_CMD_SET_FILE_STATUS_FLAGS,
|
||||
FCNTL_CMD_SET_FD_FLAGS
|
||||
};
|
||||
/*
|
||||
* Note that even though the corner case of num_rd + num_wr +
|
||||
* num_ex == MAX_FDS is technically valid, this condition hints
|
||||
* at a possible attempt to over popupate the array (see the
|
||||
* implementation of 'select' in the Noux libc plugin). Hence,
|
||||
* we regard this case as an error, too.
|
||||
*/
|
||||
return total_fds() >= MAX_FDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input and output argument type of select syscall
|
||||
* Return true of fd set index should be watched for reading
|
||||
*/
|
||||
struct Select_fds
|
||||
{
|
||||
/**
|
||||
* Maximum number of file descriptors supported
|
||||
*/
|
||||
enum { MAX_FDS = 32U };
|
||||
|
||||
/**
|
||||
* Number of file descriptors to watch for read operations (rd),
|
||||
* write operations (wr), or exceptions (ex).
|
||||
*/
|
||||
size_t num_rd,
|
||||
num_wr,
|
||||
num_ex;
|
||||
|
||||
/**
|
||||
* Array containing the file descriptors, starting with those
|
||||
* referring to rd, followed by wr, and finally ex
|
||||
*/
|
||||
int array[MAX_FDS];
|
||||
|
||||
/**
|
||||
* Return total number of file descriptors contained in the array
|
||||
*/
|
||||
size_t total_fds() const {
|
||||
return min(num_rd + num_wr + num_ex, (size_t)MAX_FDS); }
|
||||
|
||||
/**
|
||||
* Check for maximum population of fds array
|
||||
*/
|
||||
bool max_fds_exceeded() const
|
||||
{
|
||||
/*
|
||||
* Note that even though the corner case of num_rd + num_wr +
|
||||
* num_ex == MAX_FDS is technically valid, this condition hints
|
||||
* at a possible attempt to over popupate the array (see the
|
||||
* implementation of 'select' in the Noux libc plugin). Hence,
|
||||
* we regard this case as an error, too.
|
||||
*/
|
||||
return total_fds() >= MAX_FDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true of fd set index should be watched for reading
|
||||
*/
|
||||
bool watch_for_rd(unsigned i) const { return i < num_rd; }
|
||||
|
||||
/**
|
||||
* Return true if fd set index should be watched for writing
|
||||
*/
|
||||
bool watch_for_wr(unsigned i) const {
|
||||
return (i >= num_rd) && (i < num_rd + num_wr); }
|
||||
|
||||
/**
|
||||
* Return true if fd set index should be watched for exceptions
|
||||
*/
|
||||
bool watch_for_ex(unsigned i) const {
|
||||
return (i >= num_rd + num_wr) && (i < total_fds()); }
|
||||
};
|
||||
|
||||
struct Select_timeout
|
||||
{
|
||||
long sec, usec;
|
||||
|
||||
/**
|
||||
* Set timeout to infinity
|
||||
*/
|
||||
void set_infinite() { sec = -1; usec = -1; }
|
||||
|
||||
/**
|
||||
* Return true if the timeout is infinite
|
||||
*/
|
||||
bool infinite() const { return (sec == -1) && (usec == -1); }
|
||||
|
||||
/**
|
||||
* Return true if the timeout is zero
|
||||
*/
|
||||
bool zero() const { return (sec == 0) && (usec == 0); }
|
||||
};
|
||||
bool watch_for_rd(unsigned i) const { return i < num_rd; }
|
||||
|
||||
/**
|
||||
* Socket related structures
|
||||
* Return true if fd set index should be watched for writing
|
||||
*/
|
||||
enum { MAX_HOSTNAME_LEN = 255 };
|
||||
typedef char Hostname[MAX_HOSTNAME_LEN];
|
||||
|
||||
enum { MAX_SERVNAME_LEN = 255 };
|
||||
typedef char Servname[MAX_SERVNAME_LEN];
|
||||
|
||||
enum { MAX_ADDRINFO_RESULTS = 4 };
|
||||
|
||||
struct in_addr
|
||||
{
|
||||
unsigned int s_addr;
|
||||
};
|
||||
|
||||
struct sockaddr
|
||||
{
|
||||
unsigned char sa_len;
|
||||
unsigned char sa_family;
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
struct sockaddr_in {
|
||||
unsigned char sin_len;
|
||||
unsigned char sin_family;
|
||||
unsigned short sin_port;
|
||||
struct in_addr sin_addr;
|
||||
char sin_zero[8];
|
||||
};
|
||||
|
||||
typedef unsigned socklen_t;
|
||||
|
||||
struct addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
socklen_t ai_addrlen;
|
||||
struct sockaddr *ai_addr;
|
||||
char *ai_canonname;
|
||||
struct addrinfo *ai_next;
|
||||
};
|
||||
|
||||
struct Addrinfo {
|
||||
struct addrinfo addrinfo;
|
||||
struct sockaddr ai_addr;
|
||||
char ai_canonname[255];
|
||||
};
|
||||
bool watch_for_wr(unsigned i) const {
|
||||
return (i >= num_rd) && (i < num_rd + num_wr); }
|
||||
|
||||
/**
|
||||
* user info defintions
|
||||
* Return true if fd set index should be watched for exceptions
|
||||
*/
|
||||
enum { USERINFO_GET_ALL = 0, USERINFO_GET_UID, USERINFO_GET_GID };
|
||||
enum { MAX_USERNAME_LEN = 32 };
|
||||
typedef char User[MAX_USERNAME_LEN];
|
||||
enum { MAX_SHELL_LEN = 16 };
|
||||
typedef char Shell[MAX_SHELL_LEN];
|
||||
enum { MAX_HOME_LEN = 128 };
|
||||
typedef char Home[MAX_HOME_LEN];
|
||||
typedef unsigned int Uid;
|
||||
bool watch_for_ex(unsigned i) const {
|
||||
return (i >= num_rd + num_wr) && (i < total_fds()); }
|
||||
};
|
||||
|
||||
struct Select_timeout
|
||||
{
|
||||
long sec, usec;
|
||||
|
||||
/**
|
||||
* time/clock definitions
|
||||
* Set timeout to infinity
|
||||
*/
|
||||
enum Clock_Id { CLOCK_ID_SECOND };
|
||||
|
||||
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
||||
enum Execve_error { EXECVE_NONEXISTENT = Vfs::Directory_service::NUM_GENERAL_ERRORS, EXECVE_NOMEM };
|
||||
enum Fork_error { FORK_NOMEM = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
||||
enum Select_error { SELECT_ERR_INTERRUPT };
|
||||
void set_infinite() { sec = -1; usec = -1; }
|
||||
|
||||
/**
|
||||
* Socket related errors
|
||||
* Return true if the timeout is infinite
|
||||
*/
|
||||
enum Accept_error { ACCEPT_ERR_AGAIN, ACCEPT_ERR_WOULD_BLOCK,
|
||||
ACCEPT_ERR_INVALID, ACCEPT_ERR_NO_MEMORY,
|
||||
ACCEPT_ERR_NOT_SUPPORTED };
|
||||
bool infinite() const { return (sec == -1) && (usec == -1); }
|
||||
|
||||
enum Bind_error { BIND_ERR_ACCESS, BIND_ERR_ADDR_IN_USE,
|
||||
BIND_ERR_INVALID, BIND_ERR_NO_MEMORY };
|
||||
/**
|
||||
* Return true if the timeout is zero
|
||||
*/
|
||||
bool zero() const { return (sec == 0) && (usec == 0); }
|
||||
};
|
||||
|
||||
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,
|
||||
CONNECT_ERR_RESET, CONNECT_ERR_ABORTED,
|
||||
CONNECT_ERR_NO_ROUTE };
|
||||
/**
|
||||
* Socket related structures
|
||||
*/
|
||||
enum { MAX_HOSTNAME_LEN = 255 };
|
||||
typedef char Hostname[MAX_HOSTNAME_LEN];
|
||||
|
||||
enum Listen_error { LISTEN_ERR_ADDR_IN_USE, LISTEN_ERR_NOT_SUPPORTED };
|
||||
enum { MAX_SERVNAME_LEN = 255 };
|
||||
typedef char Servname[MAX_SERVNAME_LEN];
|
||||
|
||||
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 { MAX_ADDRINFO_RESULTS = 4 };
|
||||
|
||||
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 };
|
||||
struct in_addr
|
||||
{
|
||||
unsigned int s_addr;
|
||||
};
|
||||
|
||||
enum Shutdown_error { SHUTDOWN_ERR_NOT_CONNECTED };
|
||||
struct sockaddr
|
||||
{
|
||||
unsigned char sa_len;
|
||||
unsigned char sa_family;
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
enum Socket_error { SOCKET_ERR_ACCESS, SOCKET_ERR_NO_AF_SUPPORT,
|
||||
SOCKET_ERR_INVALID, SOCKET_ERR_NO_MEMORY };
|
||||
struct sockaddr_in {
|
||||
unsigned char sin_len;
|
||||
unsigned char sin_family;
|
||||
unsigned short sin_port;
|
||||
struct in_addr sin_addr;
|
||||
char sin_zero[8];
|
||||
};
|
||||
|
||||
enum Clock_error { CLOCK_ERR_INVALID, CLOCK_ERR_FAULT, CLOCK_ERR_NO_PERM };
|
||||
typedef unsigned socklen_t;
|
||||
|
||||
enum Utimes_error { UTIMES_ERR_ACCESS, UTIMES_ERR_FAUL, UTIMES_ERR_EIO,
|
||||
UTIMES_ERR_NAME_TOO_LONG, UTIMES_ERR_NO_ENTRY,
|
||||
UTIMES_ERR_NOT_DIRECTORY, UTIMES_ERR_NO_PERM,
|
||||
UTIMES_ERR_READ_ONLY };
|
||||
struct addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
socklen_t ai_addrlen;
|
||||
struct sockaddr *ai_addr;
|
||||
char *ai_canonname;
|
||||
struct addrinfo *ai_next;
|
||||
};
|
||||
|
||||
enum Wait4_error { WAIT4_ERR_INTERRUPT };
|
||||
struct Addrinfo {
|
||||
struct addrinfo addrinfo;
|
||||
struct sockaddr ai_addr;
|
||||
char ai_canonname[255];
|
||||
};
|
||||
|
||||
enum Kill_error { KILL_ERR_SRCH };
|
||||
/**
|
||||
* user info defintions
|
||||
*/
|
||||
enum { USERINFO_GET_ALL = 0, USERINFO_GET_UID, USERINFO_GET_GID };
|
||||
enum { MAX_USERNAME_LEN = 32 };
|
||||
typedef char User[MAX_USERNAME_LEN];
|
||||
enum { MAX_SHELL_LEN = 16 };
|
||||
typedef char Shell[MAX_SHELL_LEN];
|
||||
enum { MAX_HOME_LEN = 128 };
|
||||
typedef char Home[MAX_HOME_LEN];
|
||||
typedef unsigned int Uid;
|
||||
|
||||
union {
|
||||
Vfs::Directory_service::General_error general;
|
||||
Vfs::Directory_service::Stat_result stat;
|
||||
Vfs::File_io_service::Ftruncate_result ftruncate;
|
||||
Vfs::Directory_service::Open_result open;
|
||||
Vfs::Directory_service::Unlink_result unlink;
|
||||
Vfs::Directory_service::Readlink_result readlink;
|
||||
Vfs::Directory_service::Rename_result rename;
|
||||
Vfs::Directory_service::Mkdir_result mkdir;
|
||||
Vfs::Directory_service::Symlink_result symlink;
|
||||
Vfs::File_io_service::Read_result read;
|
||||
Vfs::File_io_service::Write_result write;
|
||||
Vfs::File_io_service::Ioctl_result ioctl;
|
||||
/**
|
||||
* time/clock definitions
|
||||
*/
|
||||
enum Clock_Id { CLOCK_ID_SECOND };
|
||||
|
||||
Fcntl_error fcntl;
|
||||
Execve_error execve;
|
||||
Select_error select;
|
||||
Accept_error accept;
|
||||
Bind_error bind;
|
||||
Connect_error connect;
|
||||
Listen_error listen;
|
||||
Recv_error recv;
|
||||
Send_error send;
|
||||
Shutdown_error shutdown;
|
||||
Socket_error socket;
|
||||
Clock_error clock;
|
||||
Utimes_error utimes;
|
||||
Wait4_error wait4;
|
||||
Kill_error kill;
|
||||
Fork_error fork;
|
||||
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
||||
enum Execve_error { EXECVE_NONEXISTENT = Vfs::Directory_service::NUM_GENERAL_ERRORS, EXECVE_NOMEM };
|
||||
enum Fork_error { FORK_NOMEM = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
||||
enum Select_error { SELECT_ERR_INTERRUPT };
|
||||
|
||||
} error;
|
||||
/**
|
||||
* Socket related errors
|
||||
*/
|
||||
enum Accept_error { ACCEPT_ERR_AGAIN, ACCEPT_ERR_WOULD_BLOCK,
|
||||
ACCEPT_ERR_INVALID, ACCEPT_ERR_NO_MEMORY,
|
||||
ACCEPT_ERR_NOT_SUPPORTED };
|
||||
|
||||
union {
|
||||
enum Bind_error { BIND_ERR_ACCESS, BIND_ERR_ADDR_IN_USE,
|
||||
BIND_ERR_INVALID, BIND_ERR_NO_MEMORY };
|
||||
|
||||
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; },
|
||||
{ size_t count; });
|
||||
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,
|
||||
CONNECT_ERR_RESET, CONNECT_ERR_ABORTED,
|
||||
CONNECT_ERR_NO_ROUTE };
|
||||
|
||||
SYSIO_DECL(stat, { Path path; }, { Stat st; });
|
||||
enum Listen_error { LISTEN_ERR_ADDR_IN_USE, LISTEN_ERR_NOT_SUPPORTED };
|
||||
|
||||
SYSIO_DECL(symlink, { Path oldpath; Path newpath; }, { });
|
||||
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 };
|
||||
|
||||
SYSIO_DECL(fstat, { int fd; }, { Stat st; });
|
||||
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 };
|
||||
|
||||
SYSIO_DECL(ftruncate, { int fd; off_t length; }, { });
|
||||
enum Shutdown_error { SHUTDOWN_ERR_NOT_CONNECTED };
|
||||
|
||||
SYSIO_DECL(fcntl, { int fd; long long_arg; Fcntl_cmd cmd; },
|
||||
{ int result; });
|
||||
enum Socket_error { SOCKET_ERR_ACCESS, SOCKET_ERR_NO_AF_SUPPORT,
|
||||
SOCKET_ERR_INVALID, SOCKET_ERR_NO_MEMORY };
|
||||
|
||||
SYSIO_DECL(open, { Path path; int mode; }, { int fd; });
|
||||
enum Clock_error { CLOCK_ERR_INVALID, CLOCK_ERR_FAULT, CLOCK_ERR_NO_PERM };
|
||||
|
||||
SYSIO_DECL(close, { int fd; }, { });
|
||||
enum Utimes_error { UTIMES_ERR_ACCESS, UTIMES_ERR_FAUL, UTIMES_ERR_EIO,
|
||||
UTIMES_ERR_NAME_TOO_LONG, UTIMES_ERR_NO_ENTRY,
|
||||
UTIMES_ERR_NOT_DIRECTORY, UTIMES_ERR_NO_PERM,
|
||||
UTIMES_ERR_READ_ONLY };
|
||||
|
||||
SYSIO_DECL(ioctl, : Ioctl_in { int fd; }, : Ioctl_out { });
|
||||
enum Wait4_error { WAIT4_ERR_INTERRUPT };
|
||||
|
||||
SYSIO_DECL(lseek, { int fd; off_t offset; Lseek_whence whence; },
|
||||
{ off_t offset; });
|
||||
enum Kill_error { KILL_ERR_SRCH };
|
||||
|
||||
SYSIO_DECL(dirent, { int fd; }, { Dirent entry; });
|
||||
union {
|
||||
Vfs::Directory_service::General_error general;
|
||||
Vfs::Directory_service::Stat_result stat;
|
||||
Vfs::File_io_service::Ftruncate_result ftruncate;
|
||||
Vfs::Directory_service::Open_result open;
|
||||
Vfs::Directory_service::Unlink_result unlink;
|
||||
Vfs::Directory_service::Readlink_result readlink;
|
||||
Vfs::Directory_service::Rename_result rename;
|
||||
Vfs::Directory_service::Mkdir_result mkdir;
|
||||
Vfs::Directory_service::Symlink_result symlink;
|
||||
Vfs::File_io_service::Read_result read;
|
||||
Vfs::File_io_service::Write_result write;
|
||||
Vfs::File_io_service::Ioctl_result ioctl;
|
||||
|
||||
SYSIO_DECL(read, { int fd; size_t count; },
|
||||
{ Chunk chunk; size_t count; });
|
||||
Fcntl_error fcntl;
|
||||
Execve_error execve;
|
||||
Select_error select;
|
||||
Accept_error accept;
|
||||
Bind_error bind;
|
||||
Connect_error connect;
|
||||
Listen_error listen;
|
||||
Recv_error recv;
|
||||
Send_error send;
|
||||
Shutdown_error shutdown;
|
||||
Socket_error socket;
|
||||
Clock_error clock;
|
||||
Utimes_error utimes;
|
||||
Wait4_error wait4;
|
||||
Kill_error kill;
|
||||
Fork_error fork;
|
||||
|
||||
SYSIO_DECL(readlink, { Path path; size_t bufsiz; },
|
||||
{ Chunk chunk; size_t count; });
|
||||
} error;
|
||||
|
||||
SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { });
|
||||
union {
|
||||
|
||||
SYSIO_DECL(select, { Select_fds fds; Select_timeout timeout; },
|
||||
{ Select_fds fds; });
|
||||
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; },
|
||||
{ size_t count; });
|
||||
|
||||
SYSIO_DECL(fork, { addr_t ip; addr_t sp;
|
||||
addr_t parent_cap_addr; },
|
||||
{ int pid; });
|
||||
SYSIO_DECL(stat, { Path path; }, { Stat st; });
|
||||
|
||||
SYSIO_DECL(getpid, { }, { int pid; });
|
||||
SYSIO_DECL(symlink, { Path oldpath; Path newpath; }, { });
|
||||
|
||||
SYSIO_DECL(wait4, { int pid; bool nohang; },
|
||||
{ int pid; int status; });
|
||||
SYSIO_DECL(pipe, { }, { int fd[2]; });
|
||||
SYSIO_DECL(fstat, { int fd; }, { Stat st; });
|
||||
|
||||
SYSIO_DECL(dup2, { int fd; int to_fd; }, { int fd; });
|
||||
SYSIO_DECL(ftruncate, { int fd; off_t length; }, { });
|
||||
|
||||
SYSIO_DECL(unlink, { Path path; }, { });
|
||||
SYSIO_DECL(fcntl, { int fd; long long_arg; Fcntl_cmd cmd; },
|
||||
{ int result; });
|
||||
|
||||
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
|
||||
SYSIO_DECL(open, { Path path; int mode; }, { int fd; });
|
||||
|
||||
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
|
||||
SYSIO_DECL(close, { int fd; }, { });
|
||||
|
||||
SYSIO_DECL(socket, { int domain; int type; int protocol; },
|
||||
{ int fd; });
|
||||
SYSIO_DECL(ioctl, : Ioctl_in { int fd; }, : Ioctl_out { });
|
||||
|
||||
/* XXX for now abuse Chunk for passing optval */
|
||||
SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval;
|
||||
socklen_t optlen; }, { int result; });
|
||||
SYSIO_DECL(lseek, { int fd; off_t offset; Lseek_whence whence; },
|
||||
{ off_t offset; });
|
||||
|
||||
SYSIO_DECL(setsockopt, { int fd; int level;
|
||||
int optname; Chunk optval;
|
||||
socklen_t optlen; }, { });
|
||||
SYSIO_DECL(dirent, { int fd; }, { Dirent entry; });
|
||||
|
||||
SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; },
|
||||
{ int fd; });
|
||||
SYSIO_DECL(read, { int fd; size_t count; },
|
||||
{ Chunk chunk; size_t count; });
|
||||
|
||||
SYSIO_DECL(bind, { int fd; struct sockaddr addr;
|
||||
socklen_t addrlen; }, { int result; });
|
||||
SYSIO_DECL(readlink, { Path path; size_t bufsiz; },
|
||||
{ Chunk chunk; size_t count; });
|
||||
|
||||
SYSIO_DECL(getpeername, { int fd; struct sockaddr addr;
|
||||
socklen_t addrlen; }, { });
|
||||
SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { });
|
||||
|
||||
SYSIO_DECL(listen, { int fd; int type; int backlog; },
|
||||
{ int result; });
|
||||
SYSIO_DECL(select, { Select_fds fds; Select_timeout timeout; },
|
||||
{ Select_fds fds; });
|
||||
|
||||
SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; },
|
||||
{ ssize_t len; });
|
||||
SYSIO_DECL(fork, { addr_t ip; addr_t sp;
|
||||
addr_t parent_cap_addr; },
|
||||
{ int pid; });
|
||||
|
||||
SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags;
|
||||
struct sockaddr dest_addr; socklen_t addrlen; },
|
||||
{ ssize_t len; });
|
||||
SYSIO_DECL(getpid, { }, { int pid; });
|
||||
|
||||
SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; },
|
||||
{ size_t len; });
|
||||
SYSIO_DECL(wait4, { int pid; bool nohang; },
|
||||
{ int pid; int status; });
|
||||
SYSIO_DECL(pipe, { }, { int fd[2]; });
|
||||
|
||||
SYSIO_DECL(recvfrom, { int fd; Chunk buf; size_t len; int flags;
|
||||
struct sockaddr src_addr; socklen_t addrlen; },
|
||||
{ size_t len; });
|
||||
SYSIO_DECL(dup2, { int fd; int to_fd; }, { int fd; });
|
||||
|
||||
SYSIO_DECL(shutdown, { int fd; int how; }, { });
|
||||
SYSIO_DECL(unlink, { Path path; }, { });
|
||||
|
||||
SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; },
|
||||
{ int result; });
|
||||
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
|
||||
|
||||
SYSIO_DECL(userinfo, { int request; Uid uid; },
|
||||
{ User name; Uid uid; Uid gid; Shell shell;
|
||||
Home home; });
|
||||
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
|
||||
|
||||
SYSIO_DECL(gettimeofday, { }, { unsigned long sec; unsigned int usec; });
|
||||
SYSIO_DECL(socket, { int domain; int type; int protocol; },
|
||||
{ int fd; });
|
||||
|
||||
SYSIO_DECL(clock_gettime, { Clock_Id clock_id; },
|
||||
{ unsigned long sec; unsigned long nsec; });
|
||||
/* XXX for now abuse Chunk for passing optval */
|
||||
SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval;
|
||||
socklen_t optlen; }, { int result; });
|
||||
|
||||
SYSIO_DECL(utimes, { Path path; unsigned long sec; unsigned long usec; },
|
||||
{ });
|
||||
SYSIO_DECL(setsockopt, { int fd; int level;
|
||||
int optname; Chunk optval;
|
||||
socklen_t optlen; }, { });
|
||||
|
||||
SYSIO_DECL(sync, { }, { });
|
||||
SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; },
|
||||
{ int fd; });
|
||||
|
||||
SYSIO_DECL(kill, { int pid; Signal sig; }, { });
|
||||
SYSIO_DECL(bind, { int fd; struct sockaddr addr;
|
||||
socklen_t addrlen; }, { int result; });
|
||||
|
||||
SYSIO_DECL(getdtablesize, { }, { int n; });
|
||||
};
|
||||
SYSIO_DECL(getpeername, { int fd; struct sockaddr addr;
|
||||
socklen_t addrlen; }, { });
|
||||
|
||||
SYSIO_DECL(listen, { int fd; int type; int backlog; },
|
||||
{ int result; });
|
||||
|
||||
SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; },
|
||||
{ ssize_t len; });
|
||||
|
||||
SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags;
|
||||
struct sockaddr dest_addr; socklen_t addrlen; },
|
||||
{ ssize_t len; });
|
||||
|
||||
SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; },
|
||||
{ size_t len; });
|
||||
|
||||
SYSIO_DECL(recvfrom, { int fd; Chunk buf; size_t len; int flags;
|
||||
struct sockaddr src_addr; socklen_t addrlen; },
|
||||
{ size_t len; });
|
||||
|
||||
SYSIO_DECL(shutdown, { int fd; int how; }, { });
|
||||
|
||||
SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; },
|
||||
{ int result; });
|
||||
|
||||
SYSIO_DECL(userinfo, { int request; Uid uid; },
|
||||
{ User name; Uid uid; Uid gid; Shell shell;
|
||||
Home home; });
|
||||
|
||||
SYSIO_DECL(gettimeofday, { }, { unsigned long sec; unsigned int usec; });
|
||||
|
||||
SYSIO_DECL(clock_gettime, { Clock_Id clock_id; },
|
||||
{ unsigned long sec; unsigned long nsec; });
|
||||
|
||||
SYSIO_DECL(utimes, { Path path; unsigned long sec; unsigned long usec; },
|
||||
{ });
|
||||
|
||||
SYSIO_DECL(sync, { }, { });
|
||||
|
||||
SYSIO_DECL(kill, { int pid; Signal sig; }, { });
|
||||
|
||||
SYSIO_DECL(getdtablesize, { }, { int n; });
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -15,102 +15,105 @@
|
||||
#define _NOUX__ARGS_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <util/string.h>
|
||||
#include <base/log.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Args
|
||||
{
|
||||
protected:
|
||||
|
||||
char * const _buf;
|
||||
size_t const _buf_size;
|
||||
size_t _len;
|
||||
|
||||
size_t _free_size() const
|
||||
{
|
||||
/*
|
||||
* Keep space for two trailing zeros, indicating the end of the
|
||||
* stream of strings.
|
||||
*/
|
||||
return _buf_size - _len - 2;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
class Overrun { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param buf argument buffer
|
||||
* \param buf_size size of argument buffer in character,
|
||||
* must be at least 2
|
||||
*/
|
||||
Args(char *buf, size_t buf_size)
|
||||
: _buf(buf), _buf_size(buf_size), _len(0)
|
||||
{
|
||||
if (buf_size <= 2)
|
||||
throw Overrun();
|
||||
|
||||
/* search end of string stream (two subsequent zeros) */
|
||||
for (; (_buf[_len] || _buf[_len + 1]) && (_len < _buf_size - 2); _len++);
|
||||
|
||||
/* ensure termination of argument buffer */
|
||||
_buf[_buf_size - 1] = 0;
|
||||
_buf[_buf_size - 2] = 0;
|
||||
}
|
||||
|
||||
Args() : _buf(0), _buf_size(0), _len(0) { }
|
||||
|
||||
bool valid() const { return _buf_size > 0; }
|
||||
|
||||
size_t len() const { return _len; }
|
||||
|
||||
char const * const base() const { return _buf; }
|
||||
|
||||
void append(char const *arg)
|
||||
{
|
||||
size_t const arg_len = strlen(arg);
|
||||
|
||||
if (arg_len > _free_size())
|
||||
throw Overrun();
|
||||
|
||||
strncpy(_buf + _len, arg, _buf_size - _len);
|
||||
|
||||
_len += arg_len + 1; /* keep null termination between strings */
|
||||
|
||||
/* mark end of stream of strings */
|
||||
_buf[_len] = 0;
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
for (unsigned i = 0, j = 0; _buf[i] && (i < _buf_size - 2);
|
||||
i += strlen(&_buf[i]) + 1, j++)
|
||||
log("arg(", j, "): \"", Cstring(&_buf[i]), "\"");
|
||||
}
|
||||
};
|
||||
|
||||
class Args_dataspace : private Attached_ram_dataspace, public Args
|
||||
{
|
||||
public:
|
||||
|
||||
Args_dataspace(size_t size, Args const &from = Args())
|
||||
:
|
||||
Attached_ram_dataspace(env()->ram_session(), size),
|
||||
Args(local_addr<char>(), size)
|
||||
{
|
||||
if (from.len() > size - 2)
|
||||
throw Overrun();
|
||||
|
||||
memcpy(_buf, from.base(), from.len() + 1);
|
||||
}
|
||||
|
||||
using Attached_ram_dataspace::cap;
|
||||
};
|
||||
class Args;
|
||||
class Args_dataspace;
|
||||
}
|
||||
|
||||
|
||||
class Noux::Args
|
||||
{
|
||||
protected:
|
||||
|
||||
char * const _buf;
|
||||
size_t const _buf_size;
|
||||
size_t _len;
|
||||
|
||||
size_t _free_size() const
|
||||
{
|
||||
/*
|
||||
* Keep space for two trailing zeros, indicating the end of the
|
||||
* stream of strings.
|
||||
*/
|
||||
return _buf_size - _len - 2;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
class Overrun { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param buf argument buffer
|
||||
* \param buf_size size of argument buffer in character,
|
||||
* must be at least 2
|
||||
*/
|
||||
Args(char *buf, size_t buf_size)
|
||||
: _buf(buf), _buf_size(buf_size), _len(0)
|
||||
{
|
||||
if (buf_size <= 2)
|
||||
throw Overrun();
|
||||
|
||||
/* search end of string stream (two subsequent zeros) */
|
||||
for (; (_buf[_len] || _buf[_len + 1]) && (_len < _buf_size - 2); _len++);
|
||||
|
||||
/* ensure termination of argument buffer */
|
||||
_buf[_buf_size - 1] = 0;
|
||||
_buf[_buf_size - 2] = 0;
|
||||
}
|
||||
|
||||
Args() : _buf(0), _buf_size(0), _len(0) { }
|
||||
|
||||
bool valid() const { return _buf_size > 0; }
|
||||
|
||||
size_t len() const { return _len; }
|
||||
|
||||
char const * const base() const { return _buf; }
|
||||
|
||||
void append(char const *arg)
|
||||
{
|
||||
size_t const arg_len = strlen(arg);
|
||||
|
||||
if (arg_len > _free_size())
|
||||
throw Overrun();
|
||||
|
||||
strncpy(_buf + _len, arg, _buf_size - _len);
|
||||
|
||||
_len += arg_len + 1; /* keep null termination between strings */
|
||||
|
||||
/* mark end of stream of strings */
|
||||
_buf[_len] = 0;
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
for (unsigned i = 0, j = 0; _buf[i] && (i < _buf_size - 2);
|
||||
i += strlen(&_buf[i]) + 1, j++)
|
||||
log("arg(", j, "): \"", Cstring(&_buf[i]), "\"");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Noux::Args_dataspace : private Attached_ram_dataspace, Args
|
||||
{
|
||||
Args_dataspace(Ram_session &ram, Region_map &rm,
|
||||
size_t size, Args const &from = Args())
|
||||
:
|
||||
Attached_ram_dataspace(ram, rm, size),
|
||||
Args(local_addr<char>(), size)
|
||||
{
|
||||
if (from.len() > size - 2)
|
||||
throw Overrun();
|
||||
|
||||
memcpy(_buf, from.base(), from.len() + 1);
|
||||
}
|
||||
|
||||
using Attached_ram_dataspace::cap;
|
||||
};
|
||||
|
||||
#endif /* _NOUX__ARGS_H_ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,140 +23,142 @@
|
||||
#include <range_checked_index.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
template <size_t> class Child_env;
|
||||
using namespace Genode;
|
||||
|
||||
/**
|
||||
* \param ARGS_SIZE size of the argument buffer given
|
||||
* to the constructor
|
||||
*/
|
||||
template <size_t ARGS_SIZE>
|
||||
class Child_env
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MAX_LEN_INTERPRETER_LINE = 128 };
|
||||
|
||||
char const *_binary_name;
|
||||
char _args[ARGS_SIZE + MAX_LEN_INTERPRETER_LINE];
|
||||
Sysio::Env _env;
|
||||
|
||||
void _process_env(Sysio::Env env)
|
||||
{
|
||||
memcpy(_env, env, sizeof(Sysio::Env));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case that the given binary needs an interpreter
|
||||
*/
|
||||
void _process_binary_name_and_args(const char *binary_name,
|
||||
Dataspace_capability binary_ds,
|
||||
const char *args)
|
||||
{
|
||||
bool interpretable = true;
|
||||
|
||||
const size_t binary_size = Dataspace_client(binary_ds).size();
|
||||
|
||||
if (binary_size < 4)
|
||||
interpretable = false;
|
||||
|
||||
const char *binary_addr = 0;
|
||||
if (interpretable)
|
||||
try {
|
||||
binary_addr = Genode::env()->rm_session()->attach(binary_ds);
|
||||
} catch(...) {
|
||||
PWRN("could not attach dataspace");
|
||||
interpretable = false;
|
||||
}
|
||||
|
||||
if (interpretable &&
|
||||
((binary_addr[0] != '#') || (binary_addr[1] != '!')))
|
||||
interpretable = false;
|
||||
|
||||
if (!interpretable) {
|
||||
Genode::env()->rm_session()->detach(binary_addr);
|
||||
_binary_name = binary_name;
|
||||
Genode::memcpy(_args, args, ARGS_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* find end of line */
|
||||
Range_checked_index<unsigned int>
|
||||
eol(2, min(binary_size, MAX_LEN_INTERPRETER_LINE));
|
||||
|
||||
try {
|
||||
while (binary_addr[eol] != '\n') eol++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
/* skip leading spaces */
|
||||
Range_checked_index<unsigned int>
|
||||
interpreter_line_cursor(2, eol);
|
||||
|
||||
try {
|
||||
while (binary_addr[interpreter_line_cursor] == ' ')
|
||||
interpreter_line_cursor++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
/* no interpreter name found */
|
||||
if (interpreter_line_cursor == eol)
|
||||
throw Child::Binary_does_not_exist();
|
||||
|
||||
int interpreter_name_start = interpreter_line_cursor;
|
||||
|
||||
/* find end of interpreter name */
|
||||
try {
|
||||
while (binary_addr[interpreter_line_cursor] != ' ')
|
||||
interpreter_line_cursor++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
size_t interpreter_name_len =
|
||||
interpreter_line_cursor - interpreter_name_start;
|
||||
|
||||
/* copy interpreter name into argument buffer */
|
||||
unsigned int args_buf_cursor = 0;
|
||||
Genode::strncpy(&_args[args_buf_cursor],
|
||||
&binary_addr[interpreter_name_start],
|
||||
interpreter_name_len + 1);
|
||||
_binary_name = &_args[args_buf_cursor];
|
||||
args_buf_cursor += interpreter_name_len + 1;
|
||||
|
||||
/* skip more spaces */
|
||||
try {
|
||||
while (binary_addr[interpreter_line_cursor] == ' ')
|
||||
interpreter_line_cursor++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
/* append interpreter arguments to argument buffer */
|
||||
size_t interpreter_args_len = eol - interpreter_line_cursor;
|
||||
if (interpreter_args_len > 0) {
|
||||
Genode::strncpy(&_args[args_buf_cursor],
|
||||
&binary_addr[interpreter_line_cursor],
|
||||
interpreter_args_len + 1);
|
||||
args_buf_cursor += interpreter_args_len + 1;
|
||||
}
|
||||
|
||||
/* append script arguments to argument buffer */
|
||||
Genode::memcpy(&_args[args_buf_cursor],
|
||||
args, ARGS_SIZE);
|
||||
|
||||
Genode::env()->rm_session()->detach(binary_addr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Child_env(const char *binary_name, Dataspace_capability binary_ds,
|
||||
const char *args, Sysio::Env env)
|
||||
{
|
||||
_process_env(env);
|
||||
_process_binary_name_and_args(binary_name, binary_ds, args);
|
||||
}
|
||||
|
||||
char const *binary_name() const { return _binary_name; }
|
||||
|
||||
Args args() { return Args(_args, sizeof(_args)); }
|
||||
|
||||
Sysio::Env const &env() const { return _env; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \param ARGS_SIZE size of the argument buffer given
|
||||
* to the constructor
|
||||
*/
|
||||
template <Noux::size_t ARGS_SIZE>
|
||||
class Noux::Child_env
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MAX_LEN_INTERPRETER_LINE = 128 };
|
||||
|
||||
char const *_binary_name;
|
||||
char _args[ARGS_SIZE + MAX_LEN_INTERPRETER_LINE];
|
||||
Sysio::Env _env;
|
||||
|
||||
void _process_env(Sysio::Env env)
|
||||
{
|
||||
memcpy(_env, env, sizeof(Sysio::Env));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case that the given binary needs an interpreter
|
||||
*/
|
||||
void _process_binary_name_and_args(Region_map &local_rm,
|
||||
const char *binary_name,
|
||||
Dataspace_capability binary_ds,
|
||||
const char *args)
|
||||
{
|
||||
bool interpretable = true;
|
||||
|
||||
const size_t binary_size = Dataspace_client(binary_ds).size();
|
||||
|
||||
if (binary_size < 4)
|
||||
interpretable = false;
|
||||
|
||||
const char *binary_addr = 0;
|
||||
if (interpretable)
|
||||
try {
|
||||
binary_addr = local_rm.attach(binary_ds);
|
||||
} catch(...) {
|
||||
PWRN("could not attach dataspace");
|
||||
interpretable = false;
|
||||
}
|
||||
|
||||
if (interpretable &&
|
||||
((binary_addr[0] != '#') || (binary_addr[1] != '!')))
|
||||
interpretable = false;
|
||||
|
||||
if (!interpretable) {
|
||||
local_rm.detach(binary_addr);
|
||||
_binary_name = binary_name;
|
||||
Genode::memcpy(_args, args, ARGS_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* find end of line */
|
||||
Range_checked_index<unsigned int>
|
||||
eol(2, min(binary_size, MAX_LEN_INTERPRETER_LINE));
|
||||
|
||||
try {
|
||||
while (binary_addr[eol] != '\n') eol++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
/* skip leading spaces */
|
||||
Range_checked_index<unsigned int>
|
||||
interpreter_line_cursor(2, eol);
|
||||
|
||||
try {
|
||||
while (binary_addr[interpreter_line_cursor] == ' ')
|
||||
interpreter_line_cursor++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
/* no interpreter name found */
|
||||
if (interpreter_line_cursor == eol)
|
||||
throw Child::Binary_does_not_exist();
|
||||
|
||||
int interpreter_name_start = interpreter_line_cursor;
|
||||
|
||||
/* find end of interpreter name */
|
||||
try {
|
||||
while (binary_addr[interpreter_line_cursor] != ' ')
|
||||
interpreter_line_cursor++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
size_t interpreter_name_len =
|
||||
interpreter_line_cursor - interpreter_name_start;
|
||||
|
||||
/* copy interpreter name into argument buffer */
|
||||
unsigned int args_buf_cursor = 0;
|
||||
Genode::strncpy(&_args[args_buf_cursor],
|
||||
&binary_addr[interpreter_name_start],
|
||||
interpreter_name_len + 1);
|
||||
_binary_name = &_args[args_buf_cursor];
|
||||
args_buf_cursor += interpreter_name_len + 1;
|
||||
|
||||
/* skip more spaces */
|
||||
try {
|
||||
while (binary_addr[interpreter_line_cursor] == ' ')
|
||||
interpreter_line_cursor++;
|
||||
} catch (Index_out_of_range) { }
|
||||
|
||||
/* append interpreter arguments to argument buffer */
|
||||
size_t interpreter_args_len = eol - interpreter_line_cursor;
|
||||
if (interpreter_args_len > 0) {
|
||||
Genode::strncpy(&_args[args_buf_cursor],
|
||||
&binary_addr[interpreter_line_cursor],
|
||||
interpreter_args_len + 1);
|
||||
args_buf_cursor += interpreter_args_len + 1;
|
||||
}
|
||||
|
||||
/* append script arguments to argument buffer */
|
||||
Genode::memcpy(&_args[args_buf_cursor],
|
||||
args, ARGS_SIZE);
|
||||
|
||||
local_rm.detach(binary_addr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Child_env(Region_map &local_rm, const char *binary_name,
|
||||
Dataspace_capability binary_ds, const char *args, Sysio::Env env)
|
||||
{
|
||||
_process_env(env);
|
||||
_process_binary_name_and_args(local_rm, binary_name, binary_ds, args);
|
||||
}
|
||||
|
||||
char const *binary_name() const { return _binary_name; }
|
||||
|
||||
Args args() { return Args(_args, sizeof(_args)); }
|
||||
|
||||
Sysio::Env const &env() const { return _env; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__CHILD_ENV_H_ */
|
||||
|
@ -33,161 +33,164 @@ namespace Noux {
|
||||
typedef Local_service<Cpu_session_component> Cpu_service;
|
||||
typedef Local_service<Rpc_object<Session> > Noux_service;
|
||||
|
||||
class Child_policy : public Genode::Child_policy
|
||||
{
|
||||
private:
|
||||
|
||||
Name const _name;
|
||||
Binary_name const _binary_name;
|
||||
Init::Child_policy_provide_rom_file _args_policy;
|
||||
Init::Child_policy_provide_rom_file _env_policy;
|
||||
Init::Child_policy_provide_rom_file _config_policy;
|
||||
Pd_service &_pd_service;
|
||||
Ram_service &_ram_service;
|
||||
Cpu_service &_cpu_service;
|
||||
Noux_service &_noux_service;
|
||||
Local_rom_service &_rom_service;
|
||||
Parent_services &_parent_services;
|
||||
Family_member &_family_member;
|
||||
Parent_exit *_parent_exit;
|
||||
File_descriptor_registry &_file_descriptor_registry;
|
||||
Signal_context_capability _destruct_context_cap;
|
||||
Ram_session &_ref_ram;
|
||||
Ram_session_capability _ref_ram_cap;
|
||||
int _exit_value;
|
||||
bool _verbose;
|
||||
|
||||
template <typename T>
|
||||
static Genode::Service *_find_service(Genode::Registry<T> &services,
|
||||
Genode::Service::Name const &name)
|
||||
{
|
||||
Genode::Service *service = nullptr;
|
||||
services.for_each([&] (T &s) {
|
||||
if (!service && (s.name() == name))
|
||||
service = &s; });
|
||||
return service;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Child_policy(Name const &name,
|
||||
Binary_name const &binary_name,
|
||||
Dataspace_capability args_ds,
|
||||
Dataspace_capability env_ds,
|
||||
Dataspace_capability config_ds,
|
||||
Rpc_entrypoint &entrypoint,
|
||||
Pd_service &pd_service,
|
||||
Ram_service &ram_service,
|
||||
Cpu_service &cpu_service,
|
||||
Noux_service &noux_service,
|
||||
Local_rom_service &rom_service,
|
||||
Parent_services &parent_services,
|
||||
Family_member &family_member,
|
||||
Parent_exit *parent_exit,
|
||||
File_descriptor_registry &file_descriptor_registry,
|
||||
Signal_context_capability destruct_context_cap,
|
||||
Ram_session &ref_ram,
|
||||
Ram_session_capability ref_ram_cap,
|
||||
bool verbose)
|
||||
:
|
||||
_name(name),
|
||||
_binary_name(binary_name),
|
||||
_args_policy( "args", args_ds, &entrypoint),
|
||||
_env_policy( "env", env_ds, &entrypoint),
|
||||
_config_policy("config", config_ds, &entrypoint),
|
||||
_pd_service(pd_service), _ram_service(ram_service),
|
||||
_cpu_service(cpu_service), _noux_service(noux_service),
|
||||
_rom_service(rom_service), _parent_services(parent_services),
|
||||
_family_member(family_member),
|
||||
_parent_exit(parent_exit),
|
||||
_file_descriptor_registry(file_descriptor_registry),
|
||||
_destruct_context_cap(destruct_context_cap),
|
||||
_ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap),
|
||||
_exit_value(~0),
|
||||
_verbose(verbose)
|
||||
{ }
|
||||
|
||||
int exit_value() const { return _exit_value; }
|
||||
|
||||
/****************************
|
||||
** Child policy interface **
|
||||
****************************/
|
||||
|
||||
Name name() const override { return _name; }
|
||||
Binary_name binary_name() const override { return _binary_name; }
|
||||
|
||||
Ram_session &ref_ram() override { return _ref_ram; }
|
||||
|
||||
Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; }
|
||||
|
||||
void init(Ram_session &session, Ram_session_capability cap) override
|
||||
{
|
||||
session.ref_account(_ref_ram_cap);
|
||||
}
|
||||
|
||||
Service &resolve_session_request(Service::Name const &service_name,
|
||||
Session_state::Args const &args) override
|
||||
{
|
||||
Session_label const label(Genode::label_from_args(args.string()));
|
||||
|
||||
/* route initial ROM requests (binary and linker) to the parent */
|
||||
if (service_name == Genode::Rom_session::service_name()) {
|
||||
if (label.last_element() == binary_name()) return _rom_service;
|
||||
if (label.last_element() == linker_name()) return _rom_service;
|
||||
}
|
||||
|
||||
Genode::Service *service = nullptr;
|
||||
|
||||
/* check for local ROM requests */
|
||||
if ((service = _args_policy .resolve_session_request(service_name.string(), args.string()))
|
||||
|| (service = _env_policy .resolve_session_request(service_name.string(), args.string()))
|
||||
|| (service = _config_policy.resolve_session_request(service_name.string(), args.string())))
|
||||
return *service;
|
||||
|
||||
/* check for local services */
|
||||
if (service_name == Genode::Ram_session::service_name()) return _ram_service;
|
||||
if (service_name == Genode::Cpu_session::service_name()) return _cpu_service;
|
||||
if (service_name == Genode::Rom_session::service_name()) return _rom_service;
|
||||
if (service_name == Genode::Pd_session::service_name()) return _pd_service;
|
||||
if (service_name == Noux::Session::service_name()) return _noux_service;
|
||||
|
||||
/* check for parent services */
|
||||
if ((service = _find_service(_parent_services, service_name)))
|
||||
return *service;
|
||||
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
||||
void exit(int exit_value) override
|
||||
{
|
||||
_exit_value = exit_value;
|
||||
|
||||
if (_verbose || (exit_value != 0))
|
||||
log("child ", _name, " exited with exit value ", exit_value);
|
||||
|
||||
/*
|
||||
* Close all open file descriptors. This is necessary to unblock
|
||||
* the parent if it is trying to read from a pipe (connected to
|
||||
* the child) before calling 'wait4()'.
|
||||
*/
|
||||
_file_descriptor_registry.flush();
|
||||
|
||||
_family_member.exit(exit_value);
|
||||
|
||||
/* notify the parent */
|
||||
if (_parent_exit)
|
||||
_parent_exit->exit_child();
|
||||
else {
|
||||
/* handle exit of the init process */
|
||||
Signal_transmitter(_destruct_context_cap).submit();
|
||||
}
|
||||
}
|
||||
|
||||
Region_map *address_space(Pd_session &pd) override
|
||||
{
|
||||
return &static_cast<Pd_session_component &>(pd).address_space_region_map();
|
||||
}
|
||||
};
|
||||
class Child_policy;
|
||||
}
|
||||
|
||||
|
||||
class Noux::Child_policy : public Genode::Child_policy
|
||||
{
|
||||
private:
|
||||
|
||||
Name const _name;
|
||||
Binary_name const _binary_name;
|
||||
Init::Child_policy_provide_rom_file _args_policy;
|
||||
Init::Child_policy_provide_rom_file _env_policy;
|
||||
Init::Child_policy_provide_rom_file _config_policy;
|
||||
Pd_service &_pd_service;
|
||||
Ram_service &_ram_service;
|
||||
Cpu_service &_cpu_service;
|
||||
Noux_service &_noux_service;
|
||||
Local_rom_service &_rom_service;
|
||||
Parent_services &_parent_services;
|
||||
Family_member &_family_member;
|
||||
Parent_exit *_parent_exit;
|
||||
File_descriptor_registry &_file_descriptor_registry;
|
||||
Signal_context_capability _destruct_context_cap;
|
||||
Ram_session &_ref_ram;
|
||||
Ram_session_capability _ref_ram_cap;
|
||||
int _exit_value;
|
||||
bool _verbose;
|
||||
|
||||
template <typename T>
|
||||
static Genode::Service *_find_service(Genode::Registry<T> &services,
|
||||
Genode::Service::Name const &name)
|
||||
{
|
||||
Genode::Service *service = nullptr;
|
||||
services.for_each([&] (T &s) {
|
||||
if (!service && (s.name() == name))
|
||||
service = &s; });
|
||||
return service;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Child_policy(Name const &name,
|
||||
Binary_name const &binary_name,
|
||||
Dataspace_capability args_ds,
|
||||
Dataspace_capability env_ds,
|
||||
Dataspace_capability config_ds,
|
||||
Rpc_entrypoint &entrypoint,
|
||||
Pd_service &pd_service,
|
||||
Ram_service &ram_service,
|
||||
Cpu_service &cpu_service,
|
||||
Noux_service &noux_service,
|
||||
Local_rom_service &rom_service,
|
||||
Parent_services &parent_services,
|
||||
Family_member &family_member,
|
||||
Parent_exit *parent_exit,
|
||||
File_descriptor_registry &file_descriptor_registry,
|
||||
Signal_context_capability destruct_context_cap,
|
||||
Ram_session &ref_ram,
|
||||
Ram_session_capability ref_ram_cap,
|
||||
bool verbose)
|
||||
:
|
||||
_name(name),
|
||||
_binary_name(binary_name),
|
||||
_args_policy( "args", args_ds, &entrypoint),
|
||||
_env_policy( "env", env_ds, &entrypoint),
|
||||
_config_policy("config", config_ds, &entrypoint),
|
||||
_pd_service(pd_service), _ram_service(ram_service),
|
||||
_cpu_service(cpu_service), _noux_service(noux_service),
|
||||
_rom_service(rom_service), _parent_services(parent_services),
|
||||
_family_member(family_member),
|
||||
_parent_exit(parent_exit),
|
||||
_file_descriptor_registry(file_descriptor_registry),
|
||||
_destruct_context_cap(destruct_context_cap),
|
||||
_ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap),
|
||||
_exit_value(~0),
|
||||
_verbose(verbose)
|
||||
{ }
|
||||
|
||||
int exit_value() const { return _exit_value; }
|
||||
|
||||
/****************************
|
||||
** Child policy interface **
|
||||
****************************/
|
||||
|
||||
Name name() const override { return _name; }
|
||||
Binary_name binary_name() const override { return _binary_name; }
|
||||
|
||||
Ram_session &ref_ram() override { return _ref_ram; }
|
||||
|
||||
Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; }
|
||||
|
||||
void init(Ram_session &session, Ram_session_capability cap) override
|
||||
{
|
||||
session.ref_account(_ref_ram_cap);
|
||||
}
|
||||
|
||||
Service &resolve_session_request(Service::Name const &service_name,
|
||||
Session_state::Args const &args) override
|
||||
{
|
||||
Session_label const label(Genode::label_from_args(args.string()));
|
||||
|
||||
/* route initial ROM requests (binary and linker) to the parent */
|
||||
if (service_name == Genode::Rom_session::service_name()) {
|
||||
if (label.last_element() == binary_name()) return _rom_service;
|
||||
if (label.last_element() == linker_name()) return _rom_service;
|
||||
}
|
||||
|
||||
Genode::Service *service = nullptr;
|
||||
|
||||
/* check for local ROM requests */
|
||||
if ((service = _args_policy .resolve_session_request(service_name.string(), args.string()))
|
||||
|| (service = _env_policy .resolve_session_request(service_name.string(), args.string()))
|
||||
|| (service = _config_policy.resolve_session_request(service_name.string(), args.string())))
|
||||
return *service;
|
||||
|
||||
/* check for local services */
|
||||
if (service_name == Genode::Ram_session::service_name()) return _ram_service;
|
||||
if (service_name == Genode::Cpu_session::service_name()) return _cpu_service;
|
||||
if (service_name == Genode::Rom_session::service_name()) return _rom_service;
|
||||
if (service_name == Genode::Pd_session::service_name()) return _pd_service;
|
||||
if (service_name == Noux::Session::service_name()) return _noux_service;
|
||||
|
||||
/* check for parent services */
|
||||
if ((service = _find_service(_parent_services, service_name)))
|
||||
return *service;
|
||||
|
||||
throw Parent::Service_denied();
|
||||
}
|
||||
|
||||
void exit(int exit_value) override
|
||||
{
|
||||
_exit_value = exit_value;
|
||||
|
||||
if (_verbose || (exit_value != 0))
|
||||
log("child ", _name, " exited with exit value ", exit_value);
|
||||
|
||||
/*
|
||||
* Close all open file descriptors. This is necessary to unblock
|
||||
* the parent if it is trying to read from a pipe (connected to
|
||||
* the child) before calling 'wait4()'.
|
||||
*/
|
||||
_file_descriptor_registry.flush();
|
||||
|
||||
_family_member.exit(exit_value);
|
||||
|
||||
/* notify the parent */
|
||||
if (_parent_exit)
|
||||
_parent_exit->exit_child();
|
||||
else {
|
||||
/* handle exit of the init process */
|
||||
Signal_transmitter(_destruct_context_cap).submit();
|
||||
}
|
||||
}
|
||||
|
||||
Region_map *address_space(Pd_session &pd) override
|
||||
{
|
||||
return &static_cast<Pd_session_component &>(pd).address_space_region_map();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__CHILD_POLICY_H_ */
|
||||
|
@ -34,137 +34,140 @@
|
||||
#include <pd_session_component.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Cpu_session_component;
|
||||
using namespace Genode;
|
||||
|
||||
class Cpu_session_component : public Rpc_object<Cpu_session>
|
||||
{
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
bool const _forked;
|
||||
Cpu_connection _cpu;
|
||||
|
||||
enum { MAX_THREADS = 8, MAIN_THREAD_IDX = 0 };
|
||||
|
||||
Thread_capability _threads[MAX_THREADS];
|
||||
Dataspace_capability _trace_control;
|
||||
Dataspace_registry &_registry;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param forked false if the CPU session belongs to a child
|
||||
* created via execve or to the init process, or
|
||||
* true if the CPU session belongs to a newly
|
||||
* forked process.
|
||||
*
|
||||
* The 'forked' parameter controls the policy applied to the
|
||||
* startup of the main thread.
|
||||
*/
|
||||
Cpu_session_component(Rpc_entrypoint &ep, Child_policy::Name const &label,
|
||||
bool forked, Dataspace_registry ®istry)
|
||||
: _ep(ep), _forked(forked), _cpu(label.string()), _registry(registry)
|
||||
{ _ep.manage(this); }
|
||||
|
||||
~Cpu_session_component()
|
||||
{
|
||||
_ep.dissolve(this);
|
||||
|
||||
if (!_trace_control.valid())
|
||||
return;
|
||||
|
||||
auto lambda = [&] (Static_dataspace_info *rdi) { return rdi; };
|
||||
Static_dataspace_info *ds_info = _registry.apply(_trace_control, lambda);
|
||||
if (ds_info)
|
||||
destroy(env()->heap(), ds_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly start main thread, only meaningful when
|
||||
* 'forked' is true
|
||||
*/
|
||||
void start_main_thread(addr_t ip, addr_t sp)
|
||||
{
|
||||
Capability<Cpu_thread> main_thread = _threads[MAIN_THREAD_IDX];
|
||||
Cpu_thread_client(main_thread).start(ip, sp);
|
||||
}
|
||||
|
||||
Cpu_session_capability cpu_cap() { return _cpu.cap(); }
|
||||
|
||||
|
||||
/***************************
|
||||
** Cpu_session interface **
|
||||
***************************/
|
||||
|
||||
Thread_capability create_thread(Capability<Pd_session> pd_cap,
|
||||
Name const &name,
|
||||
Affinity::Location affinity,
|
||||
Weight weight,
|
||||
addr_t utcb) override
|
||||
{
|
||||
/* create thread at core, keep local copy (needed on NOVA) */
|
||||
for (unsigned i = 0; i < MAX_THREADS; i++) {
|
||||
if (_threads[i].valid())
|
||||
continue;
|
||||
|
||||
auto lambda = [&] (Pd_session_component *pd)
|
||||
{
|
||||
if (!pd)
|
||||
throw Thread_creation_failed();
|
||||
|
||||
return _cpu.create_thread(pd->core_pd_cap(), name,
|
||||
affinity, weight, utcb);
|
||||
};
|
||||
|
||||
Thread_capability cap = _ep.apply(pd_cap, lambda);
|
||||
_threads[i] = cap;
|
||||
return cap;
|
||||
}
|
||||
|
||||
error("maximum number of threads per session reached");
|
||||
throw Thread_creation_failed();
|
||||
}
|
||||
|
||||
void kill_thread(Thread_capability thread) override
|
||||
{
|
||||
/* purge local copy of thread capability */
|
||||
for (unsigned i = 0; i < MAX_THREADS; i++)
|
||||
if (_threads[i].local_name() == thread.local_name())
|
||||
_threads[i] = Thread_capability();
|
||||
|
||||
_cpu.kill_thread(thread);
|
||||
}
|
||||
|
||||
void exception_sigh(Signal_context_capability handler) override {
|
||||
_cpu.exception_sigh(handler); }
|
||||
|
||||
Affinity::Space affinity_space() const override {
|
||||
return _cpu.affinity_space(); }
|
||||
|
||||
Dataspace_capability trace_control() override
|
||||
{
|
||||
if (!_trace_control.valid()) {
|
||||
_trace_control = _cpu.trace_control();
|
||||
new (env()->heap()) Static_dataspace_info(_registry,
|
||||
_trace_control);
|
||||
}
|
||||
return _trace_control;
|
||||
}
|
||||
|
||||
Quota quota() override { return _cpu.quota(); }
|
||||
|
||||
int ref_account(Cpu_session_capability c) override {
|
||||
return _cpu.ref_account(c); }
|
||||
|
||||
int transfer_quota(Cpu_session_capability c, size_t q) override {
|
||||
return _cpu.transfer_quota(c, q); }
|
||||
|
||||
Capability<Native_cpu> native_cpu() override {
|
||||
return _cpu.native_cpu(); }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class Noux::Cpu_session_component : public Rpc_object<Cpu_session>
|
||||
{
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
bool const _forked;
|
||||
Cpu_connection _cpu;
|
||||
|
||||
enum { MAX_THREADS = 8, MAIN_THREAD_IDX = 0 };
|
||||
|
||||
Thread_capability _threads[MAX_THREADS];
|
||||
Dataspace_capability _trace_control;
|
||||
Dataspace_registry &_registry;
|
||||
|
||||
Constructible<Static_dataspace_info> _ds_info;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param forked false if the CPU session belongs to a child
|
||||
* created via execve or to the init process, or
|
||||
* true if the CPU session belongs to a newly
|
||||
* forked process.
|
||||
*
|
||||
* The 'forked' parameter controls the policy applied to the
|
||||
* startup of the main thread.
|
||||
*/
|
||||
Cpu_session_component(Env &env,
|
||||
Rpc_entrypoint &ep,
|
||||
Child_policy::Name const &label,
|
||||
bool forked,
|
||||
Dataspace_registry ®istry)
|
||||
:
|
||||
_ep(ep), _forked(forked), _cpu(env, label.string()), _registry(registry)
|
||||
{
|
||||
_ep.manage(this);
|
||||
}
|
||||
|
||||
~Cpu_session_component()
|
||||
{
|
||||
_ep.dissolve(this);
|
||||
|
||||
if (!_trace_control.valid())
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly start main thread, only meaningful when
|
||||
* 'forked' is true
|
||||
*/
|
||||
void start_main_thread(addr_t ip, addr_t sp)
|
||||
{
|
||||
Capability<Cpu_thread> main_thread = _threads[MAIN_THREAD_IDX];
|
||||
Cpu_thread_client(main_thread).start(ip, sp);
|
||||
}
|
||||
|
||||
Cpu_session_capability cpu_cap() { return _cpu.cap(); }
|
||||
|
||||
|
||||
/***************************
|
||||
** Cpu_session interface **
|
||||
***************************/
|
||||
|
||||
Thread_capability create_thread(Capability<Pd_session> pd_cap,
|
||||
Name const &name,
|
||||
Affinity::Location affinity,
|
||||
Weight weight,
|
||||
addr_t utcb) override
|
||||
{
|
||||
/* create thread at core, keep local copy (needed on NOVA) */
|
||||
for (unsigned i = 0; i < MAX_THREADS; i++) {
|
||||
if (_threads[i].valid())
|
||||
continue;
|
||||
|
||||
auto lambda = [&] (Pd_session_component *pd)
|
||||
{
|
||||
if (!pd)
|
||||
throw Thread_creation_failed();
|
||||
|
||||
return _cpu.create_thread(pd->core_pd_cap(), name,
|
||||
affinity, weight, utcb);
|
||||
};
|
||||
|
||||
Thread_capability cap = _ep.apply(pd_cap, lambda);
|
||||
_threads[i] = cap;
|
||||
return cap;
|
||||
}
|
||||
|
||||
error("maximum number of threads per session reached");
|
||||
throw Thread_creation_failed();
|
||||
}
|
||||
|
||||
void kill_thread(Thread_capability thread) override
|
||||
{
|
||||
/* purge local copy of thread capability */
|
||||
for (unsigned i = 0; i < MAX_THREADS; i++)
|
||||
if (_threads[i].local_name() == thread.local_name())
|
||||
_threads[i] = Thread_capability();
|
||||
|
||||
_cpu.kill_thread(thread);
|
||||
}
|
||||
|
||||
void exception_sigh(Signal_context_capability handler) override {
|
||||
_cpu.exception_sigh(handler); }
|
||||
|
||||
Affinity::Space affinity_space() const override {
|
||||
return _cpu.affinity_space(); }
|
||||
|
||||
Dataspace_capability trace_control() override
|
||||
{
|
||||
if (!_trace_control.valid()) {
|
||||
_trace_control = _cpu.trace_control();
|
||||
_ds_info.construct(_registry, _trace_control);
|
||||
}
|
||||
return _trace_control;
|
||||
}
|
||||
|
||||
Quota quota() override { return _cpu.quota(); }
|
||||
|
||||
int ref_account(Cpu_session_capability c) override {
|
||||
return _cpu.ref_account(c); }
|
||||
|
||||
int transfer_quota(Cpu_session_capability c, size_t q) override {
|
||||
return _cpu.transfer_quota(c, q); }
|
||||
|
||||
Capability<Native_cpu> native_cpu() override {
|
||||
return _cpu.native_cpu(); }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__CPU_SESSION_COMPONENT_H_ */
|
||||
|
@ -19,163 +19,186 @@
|
||||
#include <dataspace/client.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Dataspace_registry;
|
||||
class Dataspace_user;
|
||||
class Dataspace_info;
|
||||
class Dataspace_registry;
|
||||
|
||||
struct Static_dataspace_info;
|
||||
|
||||
struct Dataspace_user : List<Dataspace_user>::Element
|
||||
{
|
||||
virtual void dissolve(Dataspace_info &ds) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Dataspace_info : public Object_pool<Dataspace_info>::Entry
|
||||
{
|
||||
private:
|
||||
|
||||
size_t _size;
|
||||
Dataspace_capability _ds_cap;
|
||||
Lock _users_lock;
|
||||
List<Dataspace_user> _users;
|
||||
|
||||
public:
|
||||
|
||||
Dataspace_info(Dataspace_capability ds_cap)
|
||||
:
|
||||
Object_pool<Dataspace_info>::Entry(ds_cap),
|
||||
_size(ds_cap.valid() ? Dataspace_client(ds_cap).size() : 0),
|
||||
_ds_cap(ds_cap)
|
||||
{ }
|
||||
|
||||
virtual ~Dataspace_info() { }
|
||||
|
||||
size_t size() const { return _size; }
|
||||
Dataspace_capability ds_cap() const { return _ds_cap; }
|
||||
|
||||
void register_user(Dataspace_user &user)
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
_users.insert(&user);
|
||||
}
|
||||
|
||||
void unregister_user(Dataspace_user &user)
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
_users.remove(&user);
|
||||
}
|
||||
|
||||
void dissolve_users()
|
||||
{
|
||||
for (;;) {
|
||||
Dataspace_user *user = 0;
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
user = _users.first();
|
||||
if (!user)
|
||||
break;
|
||||
|
||||
_users.remove(user);
|
||||
}
|
||||
user->dissolve(*this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shadow copy of dataspace
|
||||
*
|
||||
* \param ds_registry registry for keeping track of
|
||||
* the new dataspace
|
||||
* \param ep entrypoint used to serve the RPC
|
||||
* interface of the new dataspace
|
||||
* (used if the dataspace is a sub
|
||||
* RM session)
|
||||
* \return capability for the new dataspace
|
||||
*/
|
||||
virtual Dataspace_capability fork(Ram_session &ram,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep) = 0;
|
||||
|
||||
/**
|
||||
* Write raw byte sequence into dataspace
|
||||
*
|
||||
* \param dst_offset destination offset within dataspace
|
||||
* \param src data source buffer
|
||||
* \param len length of source buffer in bytes
|
||||
*/
|
||||
virtual void poke(addr_t dst_offset, void const *src, size_t len) = 0;
|
||||
|
||||
/**
|
||||
* Return leaf region map that covers a given address
|
||||
*
|
||||
* \param addr address that is covered by the requested region map
|
||||
*/
|
||||
virtual Capability<Region_map> lookup_region_map(addr_t const addr)
|
||||
{
|
||||
/* by default a dataspace is no sub region map, so return invalid */
|
||||
return Capability<Region_map>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Dataspace_registry : public Object_pool<Dataspace_info>
|
||||
{
|
||||
public:
|
||||
|
||||
~Dataspace_registry()
|
||||
{
|
||||
/*
|
||||
* At the time the destructor is called, most 'Dataspace_info'
|
||||
* objects are expected to be gone already because
|
||||
* 'Child::_resources' and 'Child::_child' are destructed
|
||||
* before the 'Child::_ds_registry'. However, RM dataspaces
|
||||
* created via 'Rm_dataspace_info::fork', are not handled by
|
||||
* those destructors. So we have to clean them up here.
|
||||
*/
|
||||
remove_all([&] (Dataspace_info *info) {
|
||||
destroy(env()->heap(), info); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Static_dataspace_info : Dataspace_info
|
||||
{
|
||||
Dataspace_registry &_ds_registry;
|
||||
|
||||
Static_dataspace_info(Dataspace_registry &ds_registry,
|
||||
Dataspace_capability ds)
|
||||
: Dataspace_info(ds), _ds_registry(ds_registry)
|
||||
{
|
||||
_ds_registry.insert(this);
|
||||
}
|
||||
|
||||
~Static_dataspace_info()
|
||||
{
|
||||
auto lambda = [this] (Static_dataspace_info *info) {
|
||||
if (!info) {
|
||||
error("lookup of binary ds info failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_ds_registry.remove(info);
|
||||
|
||||
info->dissolve_users();
|
||||
};
|
||||
_ds_registry.apply(ds_cap(), lambda);
|
||||
}
|
||||
|
||||
Dataspace_capability fork(Ram_session &,
|
||||
Dataspace_registry &,
|
||||
Rpc_entrypoint &)
|
||||
{
|
||||
return ds_cap();
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
{
|
||||
error("attempt to poke onto a static dataspace");
|
||||
}
|
||||
};
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
struct Noux::Dataspace_user : List<Dataspace_user>::Element
|
||||
{
|
||||
virtual void dissolve(Dataspace_info &ds) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Noux::Dataspace_info : public Object_pool<Dataspace_info>::Entry
|
||||
{
|
||||
private:
|
||||
|
||||
size_t _size;
|
||||
Dataspace_capability _ds_cap;
|
||||
Lock _users_lock;
|
||||
List<Dataspace_user> _users;
|
||||
|
||||
public:
|
||||
|
||||
Dataspace_info(Dataspace_capability ds_cap)
|
||||
:
|
||||
Object_pool<Dataspace_info>::Entry(ds_cap),
|
||||
_size(ds_cap.valid() ? Dataspace_client(ds_cap).size() : 0),
|
||||
_ds_cap(ds_cap)
|
||||
{ }
|
||||
|
||||
virtual ~Dataspace_info() { }
|
||||
|
||||
size_t size() const { return _size; }
|
||||
Dataspace_capability ds_cap() const { return _ds_cap; }
|
||||
|
||||
void register_user(Dataspace_user &user)
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
_users.insert(&user);
|
||||
}
|
||||
|
||||
void unregister_user(Dataspace_user &user)
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
_users.remove(&user);
|
||||
}
|
||||
|
||||
void dissolve_users()
|
||||
{
|
||||
for (;;) {
|
||||
Dataspace_user *user = 0;
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
user = _users.first();
|
||||
if (!user)
|
||||
break;
|
||||
|
||||
_users.remove(user);
|
||||
}
|
||||
user->dissolve(*this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shadow copy of dataspace
|
||||
*
|
||||
* \param ram backing store used for copied dataspaces
|
||||
* \param local_rm region map used for temporarily attaching
|
||||
* dataspaces to the local address space
|
||||
* \param alloc allocator used for creatng new 'Dataspace_info'
|
||||
* objects
|
||||
* \param ds_registry registry for keeping track of
|
||||
* the new dataspace
|
||||
* \param ep entrypoint used to serve the RPC
|
||||
* interface of the new dataspace
|
||||
* (used if the dataspace is a sub
|
||||
* RM session)
|
||||
* \return capability for the new dataspace
|
||||
*/
|
||||
virtual Dataspace_capability fork(Ram_session &ram,
|
||||
Region_map &local_rm,
|
||||
Allocator &alloc,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep) = 0;
|
||||
|
||||
/**
|
||||
* Write raw byte sequence into dataspace
|
||||
*
|
||||
* \param local_rm region map used for temporarily attaching
|
||||
* the targeted dataspace to the local address
|
||||
* space
|
||||
* \param dst_offset destination offset within dataspace
|
||||
* \param src data source buffer
|
||||
* \param len length of source buffer in bytes
|
||||
*/
|
||||
virtual void poke(Region_map &local_rm, addr_t dst_offset,
|
||||
char const *src, size_t len) = 0;
|
||||
|
||||
/**
|
||||
* Return leaf region map that covers a given address
|
||||
*
|
||||
* \param addr address that is covered by the requested region map
|
||||
*/
|
||||
virtual Capability<Region_map> lookup_region_map(addr_t const addr)
|
||||
{
|
||||
/* by default a dataspace is no sub region map, so return invalid */
|
||||
return Capability<Region_map>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Noux::Dataspace_registry : public Object_pool<Dataspace_info>
|
||||
{
|
||||
private:
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
public:
|
||||
|
||||
Dataspace_registry(Allocator &alloc) : _alloc(alloc) { }
|
||||
|
||||
~Dataspace_registry()
|
||||
{
|
||||
/*
|
||||
* At the time the destructor is called, most 'Dataspace_info'
|
||||
* objects are expected to be gone already because
|
||||
* 'Child::_resources' and 'Child::_child' are destructed
|
||||
* before the 'Child::_ds_registry'. However, RM dataspaces
|
||||
* created via 'Rm_dataspace_info::fork', are not handled by
|
||||
* those destructors. So we have to clean them up here.
|
||||
*/
|
||||
remove_all([&] (Dataspace_info *info) { destroy(_alloc, info); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Noux::Static_dataspace_info : Dataspace_info
|
||||
{
|
||||
Dataspace_registry &_ds_registry;
|
||||
|
||||
Static_dataspace_info(Dataspace_registry &ds_registry,
|
||||
Dataspace_capability ds)
|
||||
: Dataspace_info(ds), _ds_registry(ds_registry)
|
||||
{
|
||||
_ds_registry.insert(this);
|
||||
}
|
||||
|
||||
~Static_dataspace_info()
|
||||
{
|
||||
auto lambda = [this] (Static_dataspace_info *info) {
|
||||
|
||||
if (!info) {
|
||||
error("lookup of binary ds info failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_ds_registry.remove(info);
|
||||
|
||||
info->dissolve_users();
|
||||
};
|
||||
_ds_registry.apply(ds_cap(), lambda);
|
||||
}
|
||||
|
||||
Dataspace_capability fork(Ram_session &,
|
||||
Region_map &,
|
||||
Allocator &,
|
||||
Dataspace_registry &,
|
||||
Rpc_entrypoint &) override
|
||||
{
|
||||
return ds_cap();
|
||||
}
|
||||
|
||||
void poke(Region_map &, addr_t, char const *, size_t) override
|
||||
{
|
||||
error("attempt to poke onto a static dataspace");
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__DATASPACE_REGISTRY_H_ */
|
||||
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* \brief Signal_dispatcher which adds a destruct queue element into a
|
||||
* destruct queue
|
||||
* \author Christian Prochaska
|
||||
* \date 2013-01-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2013 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 _NOUX__DESTRUCT_DISPATCHER_H_
|
||||
#define _NOUX__DESTRUCT_DISPATCHER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/signal.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <destruct_queue.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
class Destruct_dispatcher : public Signal_dispatcher_base
|
||||
{
|
||||
private:
|
||||
|
||||
Destruct_queue &_destruct_queue;
|
||||
Destruct_queue::Element_base *_element;
|
||||
|
||||
public:
|
||||
|
||||
Destruct_dispatcher(Destruct_queue &destruct_queue,
|
||||
Destruct_queue::Element_base *element)
|
||||
: _destruct_queue(destruct_queue), _element(element) { }
|
||||
|
||||
void dispatch(unsigned)
|
||||
{
|
||||
_destruct_queue.insert(_element);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__DESTRUCT_DISPATCHER_H_ */
|
@ -18,77 +18,81 @@
|
||||
#include <base/allocator.h>
|
||||
#include <util/list.h>
|
||||
|
||||
namespace Noux {
|
||||
namespace Noux { class Destruct_queue; }
|
||||
|
||||
class Destruct_queue
|
||||
{
|
||||
public:
|
||||
|
||||
struct Element_base : Genode::List<Element_base>::Element
|
||||
{
|
||||
virtual void destroy() = 0;
|
||||
};
|
||||
class Noux::Destruct_queue
|
||||
{
|
||||
public:
|
||||
|
||||
/*
|
||||
* When a pointer to an object which inherits 'Element' among other
|
||||
* base classes gets static-casted to a pointer to the 'Element'
|
||||
* base object, the resulting address can differ from the start
|
||||
* address of the inherited object. To be able to pass the start
|
||||
* address of the inherited object to the allocator, a static-cast
|
||||
* back to the inherited class needs to be performed. Therefore the
|
||||
* type of the class inheriting from 'Element' needs to be given as
|
||||
* template parameter.
|
||||
*/
|
||||
template <typename T>
|
||||
class Element : public Element_base
|
||||
{
|
||||
private:
|
||||
struct Element_base : Genode::List<Element_base>::Element
|
||||
{
|
||||
virtual void destroy() = 0;
|
||||
};
|
||||
|
||||
Genode::Allocator *_alloc;
|
||||
/*
|
||||
* When a pointer to an object which inherits 'Element' among other
|
||||
* base classes gets static-casted to a pointer to the 'Element'
|
||||
* base object, the resulting address can differ from the start
|
||||
* address of the inherited object. To be able to pass the start
|
||||
* address of the inherited object to the allocator, a static-cast
|
||||
* back to the inherited class needs to be performed. Therefore the
|
||||
* type of the class inheriting from 'Element' needs to be given as
|
||||
* template parameter.
|
||||
*/
|
||||
template <typename T>
|
||||
class Element : public Element_base
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
Genode::Allocator &_alloc;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc the allocator which was used to allocate
|
||||
* the element
|
||||
*/
|
||||
Element(Genode::Allocator *alloc) : _alloc(alloc) { }
|
||||
public:
|
||||
|
||||
virtual ~Element() { };
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc the allocator which was used to allocate
|
||||
* the element
|
||||
*/
|
||||
Element(Genode::Allocator &alloc) : _alloc(alloc) { }
|
||||
|
||||
void destroy()
|
||||
{
|
||||
Genode::destroy(_alloc, static_cast<T*>(this));
|
||||
}
|
||||
};
|
||||
virtual ~Element() { };
|
||||
|
||||
private:
|
||||
|
||||
Genode::List<Element_base> _destruct_list;
|
||||
Genode::Lock _destruct_list_lock;
|
||||
|
||||
public:
|
||||
|
||||
void insert(Element_base *element)
|
||||
{
|
||||
Genode::Lock::Guard guard(_destruct_list_lock);
|
||||
_destruct_list.insert(element);
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
Genode::Lock::Guard guard(_destruct_list_lock);
|
||||
|
||||
Element_base *element;
|
||||
while ((element = _destruct_list.first())) {
|
||||
_destruct_list.remove(element);
|
||||
element->destroy();
|
||||
void destroy()
|
||||
{
|
||||
Genode::destroy(_alloc, static_cast<T*>(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
private:
|
||||
|
||||
Genode::List<Element_base> _destruct_list;
|
||||
Genode::Lock _destruct_list_lock;
|
||||
Signal_context_capability _sigh;
|
||||
|
||||
public:
|
||||
|
||||
Destruct_queue(Signal_context_capability sigh) : _sigh(sigh) { }
|
||||
|
||||
void insert(Element_base *element)
|
||||
{
|
||||
Genode::Lock::Guard guard(_destruct_list_lock);
|
||||
_destruct_list.insert(element);
|
||||
|
||||
Signal_transmitter(_sigh).submit();
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
Genode::Lock::Guard guard(_destruct_list_lock);
|
||||
|
||||
Element_base *element;
|
||||
while ((element = _destruct_list.first())) {
|
||||
_destruct_list.remove(element);
|
||||
element->destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__DESTRUCT_QUEUE_H_ */
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* \brief Dummy input I/O channel to be used for non-interactive init
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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 _NOUX__DUMMY_INPUT_IO_CHANNEL_H_
|
||||
#define _NOUX__DUMMY_INPUT_IO_CHANNEL_H_
|
||||
|
||||
/* Noux includes */
|
||||
#include <io_channel.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Sysio;
|
||||
|
||||
struct Dummy_input_io_channel : public Io_channel
|
||||
{ };
|
||||
}
|
||||
|
||||
#endif /* _NOUX__DUMMY_INPUT_IO_CHANNEL_H_ */
|
@ -11,39 +11,49 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__ENVIRONMENT_H_
|
||||
#define _NOUX__ENVIRONMENT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/string.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <path.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Environment : private Attached_ram_dataspace
|
||||
{
|
||||
private:
|
||||
|
||||
Sysio::Env *_env;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \param env comma-separated list of environment variables
|
||||
*/
|
||||
Environment(Sysio::Env const &env) :
|
||||
Attached_ram_dataspace(Genode::env()->ram_session(), sizeof(Sysio::Env)),
|
||||
_env(local_addr<Sysio::Env>())
|
||||
{
|
||||
memcpy(_env, env, sizeof(Sysio::Env));
|
||||
}
|
||||
|
||||
using Attached_ram_dataspace::cap;
|
||||
|
||||
/**
|
||||
* Return list of environment variables as zero-separated list
|
||||
*/
|
||||
Sysio::Env const &env() { return *_env; }
|
||||
};
|
||||
class Environment;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
class Noux::Environment : private Attached_ram_dataspace
|
||||
{
|
||||
private:
|
||||
|
||||
Sysio::Env *_env;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param env comma-separated list of environment variables
|
||||
*/
|
||||
Environment(Ram_session &ram, Region_map &local_rm, Sysio::Env const &env)
|
||||
:
|
||||
Attached_ram_dataspace(ram, local_rm, sizeof(Sysio::Env)),
|
||||
_env(local_addr<Sysio::Env>())
|
||||
{
|
||||
memcpy(_env, env, sizeof(Sysio::Env));
|
||||
}
|
||||
|
||||
using Attached_ram_dataspace::cap;
|
||||
|
||||
/**
|
||||
* Return list of environment variables as zero-separated list
|
||||
*/
|
||||
Sysio::Env const &env() { return *_env; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__ENVIRONMENT_H_ */
|
||||
|
@ -22,159 +22,156 @@
|
||||
#include <parent_exit.h>
|
||||
#include <parent_execve.h>
|
||||
|
||||
namespace Noux {
|
||||
namespace Noux { class Family_member; }
|
||||
|
||||
class Family_member : public List<Family_member>::Element,
|
||||
public Parent_exit,
|
||||
public Parent_execve
|
||||
{
|
||||
private:
|
||||
|
||||
int const _pid;
|
||||
Lock _lock;
|
||||
List<Family_member> _list;
|
||||
bool _has_exited;
|
||||
int _exit_status;
|
||||
class Noux::Family_member : public List<Family_member>::Element,
|
||||
public Parent_exit,
|
||||
public Parent_execve
|
||||
{
|
||||
private:
|
||||
|
||||
protected:
|
||||
int const _pid;
|
||||
Lock _lock;
|
||||
List<Family_member> _list;
|
||||
bool _has_exited;
|
||||
int _exit_status;
|
||||
|
||||
/**
|
||||
* Lock used for implementing blocking syscalls,
|
||||
* i.e., select, wait4, ...
|
||||
*/
|
||||
Lock _blocker;
|
||||
protected:
|
||||
|
||||
public:
|
||||
/**
|
||||
* Lock used for implementing blocking syscalls,
|
||||
* i.e., select, wait4, ...
|
||||
*/
|
||||
Lock _blocker;
|
||||
|
||||
Family_member(int pid)
|
||||
: _pid(pid), _has_exited(false), _exit_status(0)
|
||||
{ }
|
||||
public:
|
||||
|
||||
virtual ~Family_member() { }
|
||||
Family_member(int pid)
|
||||
: _pid(pid), _has_exited(false), _exit_status(0)
|
||||
{ }
|
||||
|
||||
int pid() const { return _pid; }
|
||||
virtual ~Family_member() { }
|
||||
|
||||
int exit_status() const { return _exit_status; }
|
||||
int pid() const { return _pid; }
|
||||
|
||||
/**
|
||||
* Called by the parent at creation time of the process
|
||||
*/
|
||||
void insert(Family_member *member)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_list.insert(member);
|
||||
int exit_status() const { return _exit_status; }
|
||||
|
||||
/**
|
||||
* Called by the parent at creation time of the process
|
||||
*/
|
||||
void insert(Family_member *member)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_list.insert(member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the parent from the return path of the wait4 syscall
|
||||
*/
|
||||
void remove(Family_member *member)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_list.remove(member);
|
||||
}
|
||||
|
||||
virtual void submit_signal(Noux::Sysio::Signal sig) = 0;
|
||||
|
||||
/**
|
||||
* Called by the parent (originates from Kill_broadcaster)
|
||||
*/
|
||||
bool deliver_kill(int pid, Noux::Sysio::Signal sig)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (pid == _pid) {
|
||||
submit_signal(sig);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the parent from the return path of the wait4 syscall
|
||||
*/
|
||||
void remove(Family_member *member)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_list.remove(member);
|
||||
bool result = false;
|
||||
|
||||
for (Family_member *child = _list.first(); child; child = child->next())
|
||||
if (child->deliver_kill(pid, sig))
|
||||
result = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent_exit interface
|
||||
*/
|
||||
|
||||
/* Called by the child on the parent (via Parent_exit) */
|
||||
void exit_child()
|
||||
{
|
||||
submit_signal(Sysio::Signal::SIG_CHLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent_execve interface
|
||||
*/
|
||||
|
||||
/* Called by the parent from 'execve_child()' */
|
||||
virtual Family_member *do_execve(const char *filename,
|
||||
Args const &args,
|
||||
Sysio::Env const &env) = 0;
|
||||
|
||||
/* Called by the child on the parent (via Parent_execve) */
|
||||
void execve_child(Family_member &child,
|
||||
const char *filename,
|
||||
Args const &args,
|
||||
Sysio::Env const &env)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
Family_member *new_child = child.do_execve(filename,
|
||||
args,
|
||||
env);
|
||||
_list.insert(new_child);
|
||||
_list.remove(&child);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tell the parent that we exited
|
||||
*/
|
||||
void exit(int exit_status)
|
||||
{
|
||||
_exit_status = exit_status;
|
||||
_has_exited = true;
|
||||
}
|
||||
|
||||
Family_member *poll4()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
/* check if any of our children has exited */
|
||||
Family_member *curr = _list.first();
|
||||
for (; curr; curr = curr->next()) {
|
||||
if (curr->_has_exited)
|
||||
return curr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void submit_signal(Noux::Sysio::Signal sig) = 0;
|
||||
|
||||
/**
|
||||
* Called by the parent (originates from Kill_broadcaster)
|
||||
*/
|
||||
bool deliver_kill(int pid, Noux::Sysio::Signal sig)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (pid == _pid) {
|
||||
submit_signal(sig);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
for (Family_member *child = _list.first(); child; child = child->next())
|
||||
if (child->deliver_kill(pid, sig))
|
||||
result = true;
|
||||
/**
|
||||
* Wait for the exit of any of our children
|
||||
*/
|
||||
Family_member *wait4()
|
||||
{
|
||||
/* reset the blocker lock to the 'locked' state */
|
||||
_blocker.unlock();
|
||||
_blocker.lock();
|
||||
|
||||
Family_member *result = poll4();
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent_exit interface
|
||||
*/
|
||||
_blocker.lock();
|
||||
|
||||
/* Called by the child on the parent (via Parent_exit) */
|
||||
void exit_child()
|
||||
{
|
||||
submit_signal(Sysio::Signal::SIG_CHLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent_execve interface
|
||||
*/
|
||||
|
||||
/* Called by the parent from 'execve_child()' */
|
||||
virtual Family_member *do_execve(const char *filename,
|
||||
Args const &args,
|
||||
Sysio::Env const &env,
|
||||
bool verbose) = 0;
|
||||
|
||||
/* Called by the child on the parent (via Parent_execve) */
|
||||
void execve_child(Family_member &child,
|
||||
const char *filename,
|
||||
Args const &args,
|
||||
Sysio::Env const &env,
|
||||
bool verbose)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
Family_member *new_child = child.do_execve(filename,
|
||||
args,
|
||||
env,
|
||||
verbose);
|
||||
_list.insert(new_child);
|
||||
_list.remove(&child);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tell the parent that we exited
|
||||
*/
|
||||
void exit(int exit_status)
|
||||
{
|
||||
_exit_status = exit_status;
|
||||
_has_exited = true;
|
||||
}
|
||||
|
||||
Family_member *poll4()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
/* check if any of our children has exited */
|
||||
Family_member *curr = _list.first();
|
||||
for (; curr; curr = curr->next()) {
|
||||
if (curr->_has_exited)
|
||||
return curr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the exit of any of our children
|
||||
*/
|
||||
Family_member *wait4()
|
||||
{
|
||||
/* reset the blocker lock to the 'locked' state */
|
||||
_blocker.unlock();
|
||||
_blocker.lock();
|
||||
|
||||
Family_member *result = poll4();
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
_blocker.lock();
|
||||
|
||||
/* either a child exited or a signal occurred */
|
||||
return poll4();
|
||||
}
|
||||
};
|
||||
}
|
||||
/* either a child exited or a signal occurred */
|
||||
return poll4();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__FAMILY_MEMBER_H_ */
|
||||
|
@ -17,105 +17,105 @@
|
||||
/* Noux includes */
|
||||
#include <io_channel.h>
|
||||
|
||||
namespace Noux {
|
||||
namespace Noux { class File_descriptor_registry; }
|
||||
|
||||
class File_descriptor_registry
|
||||
{
|
||||
public:
|
||||
|
||||
enum { MAX_FILE_DESCRIPTORS = 64 };
|
||||
class Noux::File_descriptor_registry
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
enum { MAX_FILE_DESCRIPTORS = 64 };
|
||||
|
||||
struct {
|
||||
bool allocated;
|
||||
Shared_pointer<Io_channel> io_channel;
|
||||
} _fds[MAX_FILE_DESCRIPTORS];
|
||||
private:
|
||||
|
||||
bool _valid_fd(int fd) const
|
||||
{
|
||||
return (fd >= 0) && (fd < MAX_FILE_DESCRIPTORS);
|
||||
}
|
||||
struct {
|
||||
bool allocated;
|
||||
Shared_pointer<Io_channel> io_channel;
|
||||
} _fds[MAX_FILE_DESCRIPTORS];
|
||||
|
||||
bool _find_available_fd(int *fd) const
|
||||
{
|
||||
/* allocate the first free file descriptor */
|
||||
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)
|
||||
if (_fds[i].allocated == false) {
|
||||
*fd = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool _valid_fd(int fd) const
|
||||
{
|
||||
return (fd >= 0) && (fd < MAX_FILE_DESCRIPTORS);
|
||||
}
|
||||
|
||||
void _assign_fd(int fd, Shared_pointer<Io_channel> &io_channel)
|
||||
{
|
||||
_fds[fd].io_channel = io_channel;
|
||||
_fds[fd].allocated = true;
|
||||
}
|
||||
|
||||
void _reset_fd(int fd)
|
||||
{
|
||||
_fds[fd].io_channel = Shared_pointer<Io_channel>();
|
||||
_fds[fd].allocated = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
File_descriptor_registry()
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate I/O channel with file descriptor
|
||||
*
|
||||
* \return noux file descriptor used for the I/O channel
|
||||
*/
|
||||
virtual int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1)
|
||||
{
|
||||
if ((fd == -1) && !_find_available_fd(&fd)) {
|
||||
error("could not allocate file descriptor");
|
||||
return -1;
|
||||
bool _find_available_fd(int *fd) const
|
||||
{
|
||||
/* allocate the first free file descriptor */
|
||||
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)
|
||||
if (_fds[i].allocated == false) {
|
||||
*fd = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_valid_fd(fd)) {
|
||||
error("file descriptor ", fd, " is out of range");
|
||||
return -2;
|
||||
}
|
||||
void _assign_fd(int fd, Shared_pointer<Io_channel> &io_channel)
|
||||
{
|
||||
_fds[fd].io_channel = io_channel;
|
||||
_fds[fd].allocated = true;
|
||||
}
|
||||
|
||||
_assign_fd(fd, io_channel);
|
||||
return fd;
|
||||
void _reset_fd(int fd)
|
||||
{
|
||||
_fds[fd].io_channel = Shared_pointer<Io_channel>();
|
||||
_fds[fd].allocated = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
File_descriptor_registry()
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate I/O channel with file descriptor
|
||||
*
|
||||
* \return noux file descriptor used for the I/O channel
|
||||
*/
|
||||
virtual int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1)
|
||||
{
|
||||
if ((fd == -1) && !_find_available_fd(&fd)) {
|
||||
error("could not allocate file descriptor");
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual void remove_io_channel(int fd)
|
||||
{
|
||||
if (!_valid_fd(fd))
|
||||
error("file descriptor ", fd, " is out of range");
|
||||
else
|
||||
_reset_fd(fd);
|
||||
if (!_valid_fd(fd)) {
|
||||
error("file descriptor ", fd, " is out of range");
|
||||
return -2;
|
||||
}
|
||||
|
||||
bool fd_in_use(int fd) const
|
||||
{
|
||||
return (_valid_fd(fd) && _fds[fd].io_channel);
|
||||
}
|
||||
_assign_fd(fd, io_channel);
|
||||
return fd;
|
||||
}
|
||||
|
||||
Shared_pointer<Io_channel> io_channel_by_fd(int fd) const
|
||||
{
|
||||
if (!fd_in_use(fd))
|
||||
return Shared_pointer<Io_channel>();
|
||||
virtual void remove_io_channel(int fd)
|
||||
{
|
||||
if (!_valid_fd(fd))
|
||||
error("file descriptor ", fd, " is out of range");
|
||||
else
|
||||
_reset_fd(fd);
|
||||
}
|
||||
|
||||
return _fds[fd].io_channel;
|
||||
}
|
||||
bool fd_in_use(int fd) const
|
||||
{
|
||||
return (_valid_fd(fd) && _fds[fd].io_channel);
|
||||
}
|
||||
|
||||
virtual void flush()
|
||||
{
|
||||
/* close all file descriptors */
|
||||
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)
|
||||
_reset_fd(i);
|
||||
}
|
||||
};
|
||||
}
|
||||
Shared_pointer<Io_channel> io_channel_by_fd(int fd) const
|
||||
{
|
||||
if (!fd_in_use(fd))
|
||||
return Shared_pointer<Io_channel>();
|
||||
|
||||
return _fds[fd].io_channel;
|
||||
}
|
||||
|
||||
virtual void flush()
|
||||
{
|
||||
/* close all file descriptors */
|
||||
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)
|
||||
_reset_fd(i);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__FILE_DESCRIPTOR_REGISTRY_H_ */
|
||||
|
@ -28,174 +28,176 @@
|
||||
#include <io_channel_listener.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Terminal_io_channel;
|
||||
|
||||
extern Genode::Lock &signal_lock();
|
||||
|
||||
/**
|
||||
* Input/output channel backend that is used for calling
|
||||
* different methos which does not belong to the original
|
||||
* interface, e.g. network methods.
|
||||
*/
|
||||
class Io_channel_backend
|
||||
{
|
||||
public:
|
||||
class Io_channel_backend;
|
||||
class Io_channel;
|
||||
|
||||
virtual ~Io_channel_backend() { }
|
||||
|
||||
virtual int type() const { return -1; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Input/output channel interface
|
||||
*/
|
||||
class Io_channel : public Reference_counter
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* List of notifiers (i.e., processes) used by threads that block
|
||||
* for an I/O-channel event
|
||||
*/
|
||||
List<Wake_up_notifier> _notifiers;
|
||||
Lock _notifiers_lock;
|
||||
List<Io_channel_listener> _interrupt_handlers;
|
||||
Lock _interrupt_handlers_lock;
|
||||
|
||||
public:
|
||||
|
||||
bool close_on_execve;
|
||||
|
||||
Io_channel() : close_on_execve(false) { }
|
||||
|
||||
virtual ~Io_channel() { }
|
||||
|
||||
virtual Io_channel_backend* backend() { return 0; }
|
||||
|
||||
virtual bool write(Sysio *sysio, size_t &offset) { return false; }
|
||||
virtual bool read(Sysio *sysio) { return false; }
|
||||
virtual bool fstat(Sysio *sysio) { return false; }
|
||||
virtual bool ftruncate(Sysio *sysio) { return false; }
|
||||
virtual bool fcntl(Sysio *sysio) { return false; }
|
||||
virtual bool dirent(Sysio *sysio) { return false; }
|
||||
virtual bool ioctl(Sysio *sysio) { return false; }
|
||||
virtual bool lseek(Sysio *sysio) { return false; }
|
||||
|
||||
/**
|
||||
* Return true if an unblocking condition of the channel is satisfied
|
||||
*
|
||||
* \param rd if true, check for data available for reading
|
||||
* \param wr if true, check for readiness for writing
|
||||
* \param ex if true, check for exceptions
|
||||
*/
|
||||
virtual bool check_unblock(bool rd, bool wr, bool ex) const {
|
||||
return false; }
|
||||
|
||||
/**
|
||||
* Return true if the channel is set to non-blocking mode
|
||||
*/
|
||||
virtual bool nonblocking() { return false; }
|
||||
|
||||
/**
|
||||
* Register blocker for getting waked up on an I/O channel event
|
||||
*
|
||||
* This function is normally called by the to-be-blocked thread
|
||||
* prior blocking itself, e.g., during a 'select' syscall.
|
||||
*/
|
||||
void register_wake_up_notifier(Wake_up_notifier *notifier)
|
||||
{
|
||||
Lock::Guard guard(_notifiers_lock);
|
||||
|
||||
_notifiers.insert(notifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister wake-up notifier
|
||||
*
|
||||
* This function is normally called after a blocker has left the
|
||||
* blocking condition, e.g., during the return from the 'select'
|
||||
* syscall'.
|
||||
*/
|
||||
void unregister_wake_up_notifier(Wake_up_notifier *notifier)
|
||||
{
|
||||
Lock::Guard guard(_notifiers_lock);
|
||||
|
||||
_notifiers.remove(notifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell all registered notifiers about an occurred I/O event
|
||||
*
|
||||
* This function is called by I/O channel implementations that
|
||||
* respond to external signals, e.g., the availability of new
|
||||
* input from a terminal session.
|
||||
*/
|
||||
void invoke_all_notifiers()
|
||||
{
|
||||
Lock::Guard guard(_notifiers_lock);
|
||||
|
||||
for (Wake_up_notifier *n = _notifiers.first(); n; n = n->next())
|
||||
n->wake_up();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register interrupt handler
|
||||
*
|
||||
* This function is called by Child objects to get woken up if the
|
||||
* terminal sends, for example, Ctrl-C.
|
||||
*/
|
||||
void register_interrupt_handler(Io_channel_listener *handler)
|
||||
{
|
||||
Lock::Guard guard(_interrupt_handlers_lock);
|
||||
|
||||
_interrupt_handlers.insert(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister interrupt handler
|
||||
*/
|
||||
void unregister_interrupt_handler(Io_channel_listener *handler)
|
||||
{
|
||||
Lock::Guard guard(_interrupt_handlers_lock);
|
||||
|
||||
_interrupt_handlers.remove(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the 'Io_channel_listener' object which contains the given
|
||||
* 'Interrupt_handler' pointer
|
||||
*/
|
||||
Io_channel_listener *lookup_io_channel_listener(Interrupt_handler *handler)
|
||||
{
|
||||
for (Io_channel_listener *l = _interrupt_handlers.first();
|
||||
l; l = l->next())
|
||||
if (l->object() == handler)
|
||||
return l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell all registered handlers about an interrupt event
|
||||
*/
|
||||
void invoke_all_interrupt_handlers()
|
||||
{
|
||||
Lock::Guard signal_lock_guard(signal_lock());
|
||||
Lock::Guard guard(_interrupt_handlers_lock);
|
||||
|
||||
for (Io_channel_listener *l = _interrupt_handlers.first();
|
||||
l; l = l->next())
|
||||
l->object()->handle_interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the file associated with the I/O channel
|
||||
*
|
||||
* This function is used to simplify the implemenation of SYSCALL_FSTAT
|
||||
* and is only implemented by Vfs_io_channel.
|
||||
*/
|
||||
virtual bool path(char *path, size_t len) { return false; }
|
||||
};
|
||||
class Terminal_io_channel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Input/output channel backend that is used for calling
|
||||
* different methods, which does not belong to the original
|
||||
* interface, e.g. network methods.
|
||||
*/
|
||||
struct Noux::Io_channel_backend
|
||||
{
|
||||
virtual ~Io_channel_backend() { }
|
||||
|
||||
virtual int type() const { return -1; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Input/output channel interface
|
||||
*/
|
||||
class Noux::Io_channel : public Reference_counter
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* List of notifiers (i.e., processes) used by threads that block
|
||||
* for an I/O-channel event
|
||||
*/
|
||||
List<Wake_up_notifier> _notifiers;
|
||||
Lock _notifiers_lock;
|
||||
List<Io_channel_listener> _interrupt_handlers;
|
||||
Lock _interrupt_handlers_lock;
|
||||
|
||||
public:
|
||||
|
||||
bool close_on_execve;
|
||||
|
||||
Io_channel() : close_on_execve(false) { }
|
||||
|
||||
virtual ~Io_channel() { }
|
||||
|
||||
virtual Io_channel_backend *backend() { return nullptr; }
|
||||
|
||||
virtual bool write(Sysio &sysio, size_t &offset) { return false; }
|
||||
virtual bool read(Sysio &sysio) { return false; }
|
||||
virtual bool fstat(Sysio &sysio) { return false; }
|
||||
virtual bool ftruncate(Sysio &sysio) { return false; }
|
||||
virtual bool fcntl(Sysio &sysio) { return false; }
|
||||
virtual bool dirent(Sysio &sysio) { return false; }
|
||||
virtual bool ioctl(Sysio &sysio) { return false; }
|
||||
virtual bool lseek(Sysio &sysio) { return false; }
|
||||
|
||||
/**
|
||||
* Return true if an unblocking condition of the channel is satisfied
|
||||
*
|
||||
* \param rd if true, check for data available for reading
|
||||
* \param wr if true, check for readiness for writing
|
||||
* \param ex if true, check for exceptions
|
||||
*/
|
||||
virtual bool check_unblock(bool rd, bool wr, bool ex) const {
|
||||
return false; }
|
||||
|
||||
/**
|
||||
* Return true if the channel is set to non-blocking mode
|
||||
*/
|
||||
virtual bool nonblocking() { return false; }
|
||||
|
||||
/**
|
||||
* Register blocker for getting waked up on an I/O channel event
|
||||
*
|
||||
* This function is normally called by the to-be-blocked thread
|
||||
* prior blocking itself, e.g., during a 'select' syscall.
|
||||
*/
|
||||
void register_wake_up_notifier(Wake_up_notifier *notifier)
|
||||
{
|
||||
Lock::Guard guard(_notifiers_lock);
|
||||
|
||||
_notifiers.insert(notifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister wake-up notifier
|
||||
*
|
||||
* This function is normally called after a blocker has left the
|
||||
* blocking condition, e.g., during the return from the 'select'
|
||||
* syscall'.
|
||||
*/
|
||||
void unregister_wake_up_notifier(Wake_up_notifier *notifier)
|
||||
{
|
||||
Lock::Guard guard(_notifiers_lock);
|
||||
|
||||
_notifiers.remove(notifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell all registered notifiers about an occurred I/O event
|
||||
*
|
||||
* This function is called by I/O channel implementations that
|
||||
* respond to external signals, e.g., the availability of new
|
||||
* input from a terminal session.
|
||||
*/
|
||||
void invoke_all_notifiers()
|
||||
{
|
||||
Lock::Guard guard(_notifiers_lock);
|
||||
|
||||
for (Wake_up_notifier *n = _notifiers.first(); n; n = n->next())
|
||||
n->wake_up();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register interrupt handler
|
||||
*
|
||||
* This function is called by Child objects to get woken up if the
|
||||
* terminal sends, for example, Ctrl-C.
|
||||
*/
|
||||
void register_interrupt_handler(Io_channel_listener *handler)
|
||||
{
|
||||
Lock::Guard guard(_interrupt_handlers_lock);
|
||||
|
||||
_interrupt_handlers.insert(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister interrupt handler
|
||||
*/
|
||||
void unregister_interrupt_handler(Io_channel_listener *handler)
|
||||
{
|
||||
Lock::Guard guard(_interrupt_handlers_lock);
|
||||
|
||||
_interrupt_handlers.remove(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the 'Io_channel_listener' object which contains the given
|
||||
* 'Interrupt_handler' pointer
|
||||
*/
|
||||
Io_channel_listener *lookup_io_channel_listener(Interrupt_handler *handler)
|
||||
{
|
||||
for (Io_channel_listener *l = _interrupt_handlers.first();
|
||||
l; l = l->next())
|
||||
if (l->object() == handler)
|
||||
return l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell all registered handlers about an interrupt event
|
||||
*/
|
||||
void invoke_all_interrupt_handlers()
|
||||
{
|
||||
Lock::Guard signal_lock_guard(signal_lock());
|
||||
Lock::Guard guard(_interrupt_handlers_lock);
|
||||
|
||||
for (Io_channel_listener *l = _interrupt_handlers.first();
|
||||
l; l = l->next())
|
||||
l->object()->handle_interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the file associated with the I/O channel
|
||||
*
|
||||
* This function is used to simplify the implemenation of SYSCALL_FSTAT
|
||||
* and is only implemented by Vfs_io_channel.
|
||||
*/
|
||||
virtual bool path(char *path, size_t len) { return false; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__IO_CHANNEL_H_ */
|
||||
|
@ -20,9 +20,7 @@
|
||||
/* Noux includes */
|
||||
#include <interrupt_handler.h>
|
||||
|
||||
namespace Noux {
|
||||
typedef List_element<Interrupt_handler> Io_channel_listener;
|
||||
}
|
||||
namespace Noux { typedef List_element<Interrupt_handler> Io_channel_listener; }
|
||||
|
||||
#endif /* _NOUX__IO_CHANNEL_LISTENER__H_ */
|
||||
|
||||
|
@ -20,63 +20,62 @@
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
struct Io_receptor : List<Io_receptor>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
Lock *_lock;
|
||||
|
||||
public:
|
||||
|
||||
Io_receptor(Lock *lock)
|
||||
:
|
||||
_lock(lock)
|
||||
{ }
|
||||
|
||||
void check_and_wakeup()
|
||||
{
|
||||
if (_lock)
|
||||
_lock->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
class Io_receptor_registry
|
||||
{
|
||||
private:
|
||||
|
||||
List<Io_receptor> _receptors;
|
||||
Lock _receptors_lock;
|
||||
|
||||
public:
|
||||
|
||||
Io_receptor_registry() { }
|
||||
|
||||
~Io_receptor_registry()
|
||||
{
|
||||
Io_receptor *receptor;
|
||||
while ((receptor = _receptors.first()) != 0)
|
||||
_receptors.remove(receptor);
|
||||
}
|
||||
|
||||
void register_receptor(Io_receptor *receptor)
|
||||
{
|
||||
Lock::Guard guard(_receptors_lock);
|
||||
|
||||
_receptors.insert(receptor);
|
||||
}
|
||||
|
||||
void unregister_receptor(Io_receptor *receptor)
|
||||
{
|
||||
Lock::Guard guard(_receptors_lock);
|
||||
|
||||
_receptors.remove(receptor);
|
||||
}
|
||||
|
||||
Io_receptor *first() { return _receptors.first(); }
|
||||
|
||||
};
|
||||
|
||||
struct Io_receptor;
|
||||
struct Io_receptor_registry;
|
||||
}
|
||||
|
||||
|
||||
struct Noux::Io_receptor : List<Io_receptor>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
Lock *_lock;
|
||||
|
||||
public:
|
||||
|
||||
Io_receptor(Lock *lock) : _lock(lock) { }
|
||||
|
||||
void check_and_wakeup()
|
||||
{
|
||||
if (_lock)
|
||||
_lock->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Noux::Io_receptor_registry
|
||||
{
|
||||
private:
|
||||
|
||||
List<Io_receptor> _receptors;
|
||||
Lock _receptors_lock;
|
||||
|
||||
public:
|
||||
|
||||
Io_receptor_registry() { }
|
||||
|
||||
~Io_receptor_registry()
|
||||
{
|
||||
Io_receptor *receptor;
|
||||
while ((receptor = _receptors.first()) != 0)
|
||||
_receptors.remove(receptor);
|
||||
}
|
||||
|
||||
void register_receptor(Io_receptor *receptor)
|
||||
{
|
||||
Lock::Guard guard(_receptors_lock);
|
||||
|
||||
_receptors.insert(receptor);
|
||||
}
|
||||
|
||||
void unregister_receptor(Io_receptor *receptor)
|
||||
{
|
||||
Lock::Guard guard(_receptors_lock);
|
||||
|
||||
_receptors.remove(receptor);
|
||||
}
|
||||
|
||||
Io_receptor *first() { return _receptors.first(); }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__IO_RECEPTOR_REGISTRY_H_ */
|
||||
|
@ -36,16 +36,19 @@ class Noux::Local_rom_factory : public Local_rom_service::Factory
|
||||
{
|
||||
private:
|
||||
|
||||
Allocator &_alloc;
|
||||
Env &_env;
|
||||
Rpc_entrypoint &_ep;
|
||||
Vfs::Dir_file_system &_root_dir;
|
||||
Dataspace_registry &_registry;
|
||||
|
||||
public:
|
||||
|
||||
Local_rom_factory(Rpc_entrypoint &ep, Vfs::Dir_file_system &root_dir,
|
||||
Local_rom_factory(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
|
||||
Vfs::Dir_file_system &root_dir,
|
||||
Dataspace_registry ®istry)
|
||||
:
|
||||
_ep(ep), _root_dir(root_dir), _registry(registry)
|
||||
_alloc(alloc), _env(env), _ep(ep), _root_dir(root_dir), _registry(registry)
|
||||
{ }
|
||||
|
||||
Rom_session_component &create(Args const &args, Affinity) override
|
||||
@ -54,8 +57,8 @@ class Noux::Local_rom_factory : public Local_rom_service::Factory
|
||||
Rom_session_component::Name const rom_name =
|
||||
label_from_args(args.string()).last_element();
|
||||
|
||||
return *new (env()->heap())
|
||||
Rom_session_component(_ep, _root_dir, _registry, rom_name);
|
||||
return *new (_alloc)
|
||||
Rom_session_component(_alloc, _env, _ep, _root_dir, _registry, rom_name);
|
||||
}
|
||||
catch (Rom_connection::Rom_connection_failed) { throw Denied(); }
|
||||
}
|
||||
@ -64,7 +67,7 @@ class Noux::Local_rom_factory : public Local_rom_service::Factory
|
||||
|
||||
void destroy(Rom_session_component &session) override
|
||||
{
|
||||
Genode::destroy(env()->heap(), &session);
|
||||
Genode::destroy(_alloc, &session);
|
||||
}
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,9 @@
|
||||
TARGET = noux
|
||||
LIBS = alarm vfs
|
||||
SRC_CC = main.cc dummy_net.cc
|
||||
SRC_CC = main.cc syscall.cc dummy_net.cc
|
||||
INC_DIR += $(PRG_DIR)
|
||||
INC_DIR += $(PRG_DIR)/../
|
||||
|
||||
vpath main.cc $(PRG_DIR)/..
|
||||
vpath syscall.cc $(PRG_DIR)/..
|
||||
vpath dummy_net.cc $(PRG_DIR)
|
||||
|
@ -72,6 +72,7 @@ void init_network()
|
||||
libc_select_notify = select_notify;
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Noux net syscall dispatcher **
|
||||
*********************************/
|
||||
@ -117,16 +118,16 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
break;
|
||||
case SYSCALL_SOCKET:
|
||||
{
|
||||
Socket_io_channel *socket_io_channel = new Socket_io_channel();
|
||||
Socket_io_channel *socket_io_channel = new (_heap) Socket_io_channel();
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(socket_io_channel->backend(), backend);
|
||||
|
||||
if (!backend->socket(&_sysio)) {
|
||||
if (!backend->socket(_sysio)) {
|
||||
delete socket_io_channel;
|
||||
return false;
|
||||
}
|
||||
|
||||
Shared_pointer<Io_channel> io_channel(socket_io_channel, Genode::env()->heap());
|
||||
Shared_pointer<Io_channel> io_channel(socket_io_channel, _heap);
|
||||
|
||||
_sysio.socket_out.fd = add_io_channel(io_channel);
|
||||
|
||||
@ -138,7 +139,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return backend->getsockopt(&_sysio);
|
||||
return backend->getsockopt(_sysio);
|
||||
}
|
||||
case SYSCALL_SETSOCKOPT:
|
||||
{
|
||||
@ -146,7 +147,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return backend->setsockopt(&_sysio);
|
||||
return backend->setsockopt(_sysio);
|
||||
}
|
||||
case SYSCALL_ACCEPT:
|
||||
{
|
||||
@ -154,12 +155,12 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
int socket = backend->accept(&_sysio);
|
||||
int socket = backend->accept(_sysio);
|
||||
if (socket == -1)
|
||||
return false;
|
||||
|
||||
Socket_io_channel *socket_io_channel = new Socket_io_channel(socket);
|
||||
Shared_pointer<Io_channel> io_channel(socket_io_channel, Genode::env()->heap());
|
||||
Socket_io_channel *socket_io_channel = new (_heap) Socket_io_channel(socket);
|
||||
Shared_pointer<Io_channel> io_channel(socket_io_channel, _heap);
|
||||
|
||||
_sysio.accept_out.fd = add_io_channel(io_channel);
|
||||
|
||||
@ -171,7 +172,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->bind(&_sysio) == -1) ? false : true;
|
||||
return (backend->bind(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_LISTEN:
|
||||
{
|
||||
@ -179,7 +180,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->listen(&_sysio) == -1) ? false : true;
|
||||
return (backend->listen(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_SEND:
|
||||
{
|
||||
@ -187,7 +188,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->send(&_sysio) == -1) ? false : true;
|
||||
return (backend->send(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_SENDTO:
|
||||
{
|
||||
@ -195,7 +196,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->sendto(&_sysio) == -1) ? false : true;
|
||||
return (backend->sendto(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_RECV:
|
||||
{
|
||||
@ -203,7 +204,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->recv(&_sysio) == -1) ? false : true;
|
||||
return (backend->recv(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_RECVFROM:
|
||||
{
|
||||
@ -211,7 +212,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->recvfrom(&_sysio) == -1) ? false : true;
|
||||
return (backend->recvfrom(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_GETPEERNAME:
|
||||
{
|
||||
@ -219,7 +220,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->getpeername(&_sysio) == -1) ? false : true;
|
||||
return (backend->getpeername(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_SHUTDOWN:
|
||||
{
|
||||
@ -227,7 +228,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->shutdown(&_sysio) == -1) ? false : true;
|
||||
return (backend->shutdown(_sysio) == -1) ? false : true;
|
||||
}
|
||||
case SYSCALL_CONNECT:
|
||||
{
|
||||
@ -235,7 +236,7 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
|
||||
|
||||
GET_SOCKET_IO_CHANNEL_BACKEND(io->backend(), backend);
|
||||
|
||||
return (backend->connect(&_sysio) == -1) ? false : true;
|
||||
return (backend->connect(_sysio) == -1) ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,492 +32,470 @@
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
namespace Noux {
|
||||
class Socket_io_channel_backend;
|
||||
class Socket_io_channel;
|
||||
}
|
||||
|
||||
class Socket_io_channel_backend : public Io_channel_backend
|
||||
{
|
||||
private:
|
||||
|
||||
int _socket;
|
||||
class Noux::Socket_io_channel_backend : public Io_channel_backend
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
int _socket;
|
||||
|
||||
Socket_io_channel_backend()
|
||||
:
|
||||
_socket(-1)
|
||||
{ }
|
||||
public:
|
||||
|
||||
Socket_io_channel_backend(int s)
|
||||
:
|
||||
_socket(s)
|
||||
{ }
|
||||
Socket_io_channel_backend()
|
||||
:
|
||||
_socket(-1)
|
||||
{ }
|
||||
|
||||
~Socket_io_channel_backend()
|
||||
{
|
||||
if (_socket != -1) {
|
||||
::shutdown(_socket, SHUT_RDWR);
|
||||
::close(_socket);
|
||||
}
|
||||
Socket_io_channel_backend(int s)
|
||||
:
|
||||
_socket(s)
|
||||
{ }
|
||||
|
||||
~Socket_io_channel_backend()
|
||||
{
|
||||
if (_socket != -1) {
|
||||
::shutdown(_socket, SHUT_RDWR);
|
||||
::close(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
int type() const { return 1; }
|
||||
int type() const { return 1; }
|
||||
|
||||
int socket() const { return _socket; }
|
||||
int socket() const { return _socket; }
|
||||
|
||||
/**
|
||||
* Io_channel interface implementation (only needed methods)
|
||||
*/
|
||||
/**
|
||||
* Io_channel interface implementation (only needed methods)
|
||||
*/
|
||||
|
||||
bool write(Sysio *sysio, ::size_t &count)
|
||||
{
|
||||
ssize_t result = ::write(_socket, sysio->write_in.chunk,
|
||||
sysio->write_in.count);
|
||||
bool write(Sysio &sysio, ::size_t &count)
|
||||
{
|
||||
ssize_t result = ::write(_socket, sysio.write_in.chunk,
|
||||
sysio.write_in.count);
|
||||
|
||||
if (result > -1) {
|
||||
sysio->write_out.count = result;
|
||||
count = result;
|
||||
if (result > -1) {
|
||||
sysio.write_out.count = result;
|
||||
count = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (errno) {
|
||||
/* case EAGAIN: sysio->error.read = Sysio::READ_ERR_AGAIN; break; */
|
||||
case EWOULDBLOCK: sysio->error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio->error.read = Vfs::File_io_service::READ_ERR_INVALID; break;
|
||||
case EIO: sysio->error.read = Vfs::File_io_service::READ_ERR_IO; break;
|
||||
default:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio)
|
||||
{
|
||||
::size_t const max_count = Genode::min(sysio->read_in.count, sizeof(sysio->read_out.chunk));
|
||||
|
||||
ssize_t result = ::read(_socket, sysio->read_out.chunk, max_count);
|
||||
|
||||
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 = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio->error.read = Vfs::File_io_service::READ_ERR_INVALID; break;
|
||||
case EIO: sysio->error.read = Vfs::File_io_service::READ_ERR_IO; break;
|
||||
default:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio)
|
||||
{
|
||||
int cmd = -1;
|
||||
switch (sysio->fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS: cmd = F_GETFL; break;
|
||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS: cmd = F_SETFL; break;
|
||||
default:
|
||||
log("invalid fcntl command: ", (int)sysio->fcntl_in.cmd);
|
||||
sysio->error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = ::fcntl(_socket, cmd, sysio->fcntl_in.long_arg);
|
||||
|
||||
sysio->fcntl_out.result = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio) { return false; }
|
||||
|
||||
bool ioctl(Sysio *sysio)
|
||||
{
|
||||
int request;
|
||||
|
||||
switch (sysio->ioctl_in.request) {
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_FIONBIO: request = FIONBIO; break;
|
||||
default:
|
||||
log(__func__, ": invalid ioctl request: ", (int)sysio->ioctl_in.request);
|
||||
return false;
|
||||
}
|
||||
int result = ::ioctl(_socket, request, NULL);
|
||||
|
||||
return result ? false : true;
|
||||
switch (errno) {
|
||||
/* case EAGAIN: sysio.error.read = Sysio::READ_ERR_AGAIN; break; */
|
||||
case EWOULDBLOCK: sysio.error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio.error.read = Vfs::File_io_service::READ_ERR_INVALID; break;
|
||||
case EIO: sysio.error.read = Vfs::File_io_service::READ_ERR_IO; break;
|
||||
default:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
{
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
fd_set exceptfds;
|
||||
int ready;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timeout will be overriden in libc's select() function
|
||||
* but we still need a valid pointer because libc's select()
|
||||
* will block forever otherwise.
|
||||
*/
|
||||
struct timeval timeout = { 0, 0 };
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
|
||||
FD_SET(_socket, &readfds);
|
||||
FD_SET(_socket, &writefds);
|
||||
FD_SET(_socket, &exceptfds);
|
||||
bool read(Sysio &sysio)
|
||||
{
|
||||
::size_t const max_count = Genode::min(sysio.read_in.count, sizeof(sysio.read_out.chunk));
|
||||
|
||||
ready = ::select(_socket + 1, &readfds, &writefds, &exceptfds, &timeout);
|
||||
|
||||
if (ready > 0) {
|
||||
if (rd) {
|
||||
if (FD_ISSET(_socket, &readfds))
|
||||
return true;
|
||||
}
|
||||
ssize_t result = ::read(_socket, sysio.read_out.chunk, max_count);
|
||||
|
||||
if (wr) {
|
||||
if (FD_ISSET(_socket, &writefds))
|
||||
return true;
|
||||
}
|
||||
if (result > -1) {
|
||||
sysio.read_out.count = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ex) {
|
||||
if (FD_ISSET(_socket, &exceptfds))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
switch (errno) {
|
||||
/* case EAGAIN: sysio.error.read = Sysio::READ_ERR_AGAIN; break; */
|
||||
case EWOULDBLOCK: sysio.error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio.error.read = Vfs::File_io_service::READ_ERR_INVALID; break;
|
||||
case EIO: sysio.error.read = Vfs::File_io_service::READ_ERR_IO; break;
|
||||
default:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* HACK: Since lwip won't mark fds as writable, even if they
|
||||
* are, if asked multiple times we return true in this
|
||||
* case. Hopefully that won't break any time soon.
|
||||
*/
|
||||
if (wr)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio &sysio)
|
||||
{
|
||||
int cmd = -1;
|
||||
switch (sysio.fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS: cmd = F_GETFL; break;
|
||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS: cmd = F_SETFL; break;
|
||||
default:
|
||||
log("invalid fcntl command: ", (int)sysio.fcntl_in.cmd);
|
||||
sysio.error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = ::fcntl(_socket, cmd, sysio.fcntl_in.long_arg);
|
||||
|
||||
sysio.fcntl_out.result = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio &sysio) { return false; }
|
||||
|
||||
bool ioctl(Sysio &sysio)
|
||||
{
|
||||
int request;
|
||||
|
||||
switch (sysio.ioctl_in.request) {
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_FIONBIO: request = FIONBIO; break;
|
||||
default:
|
||||
log(__func__, ": invalid ioctl request: ", (int)sysio.ioctl_in.request);
|
||||
return false;
|
||||
}
|
||||
int result = ::ioctl(_socket, request, NULL);
|
||||
|
||||
return result ? false : true;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
{
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
fd_set exceptfds;
|
||||
int ready;
|
||||
|
||||
/**
|
||||
* Socket methods
|
||||
* The timeout will be overriden in libc's select() function
|
||||
* but we still need a valid pointer because libc's select()
|
||||
* will block forever otherwise.
|
||||
*/
|
||||
struct timeval timeout = { 0, 0 };
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
|
||||
FD_SET(_socket, &readfds);
|
||||
FD_SET(_socket, &writefds);
|
||||
FD_SET(_socket, &exceptfds);
|
||||
|
||||
int accept(Sysio *sysio)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (sysio->accept_in.addrlen == 0) {
|
||||
result = ::accept(_socket, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
result = ::accept(_socket, (sockaddr *)&sysio->accept_in.addr,
|
||||
&sysio->accept_in.addrlen);
|
||||
ready = ::select(_socket + 1, &readfds, &writefds, &exceptfds, &timeout);
|
||||
|
||||
if (ready > 0) {
|
||||
if (rd) {
|
||||
if (FD_ISSET(_socket, &readfds))
|
||||
return true;
|
||||
}
|
||||
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
if (wr) {
|
||||
if (FD_ISSET(_socket, &writefds))
|
||||
return true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int bind(Sysio *sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
if (ex) {
|
||||
if (FD_ISSET(_socket, &exceptfds))
|
||||
return true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int connect(Sysio *sysio)
|
||||
{
|
||||
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;
|
||||
case ECONNRESET: sysio->error.connect = Sysio::CONNECT_ERR_RESET; break;
|
||||
case ECONNABORTED: sysio->error.connect = Sysio::CONNECT_ERR_ABORTED; break;
|
||||
case EHOSTUNREACH: sysio->error.connect = Sysio::CONNECT_ERR_NO_ROUTE; break;
|
||||
|
||||
default:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int getpeername(Sysio *sysio)
|
||||
{
|
||||
return ::getpeername(_socket, (struct sockaddr *)&sysio->getpeername_in.addr,
|
||||
(socklen_t *)&sysio->getpeername_in.addrlen);
|
||||
}
|
||||
|
||||
bool getsockopt(Sysio *sysio)
|
||||
{
|
||||
int result = ::getsockopt(_socket, sysio->getsockopt_in.level,
|
||||
sysio->getsockopt_in.optname,
|
||||
sysio->getsockopt_in.optval,
|
||||
&sysio->getsockopt_in.optlen);
|
||||
|
||||
return (result == -1) ? false : true;
|
||||
}
|
||||
|
||||
int listen(Sysio *sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t recv(Sysio *sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio->recv_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t recvfrom(Sysio *sysio)
|
||||
{
|
||||
ssize_t result = ::recvfrom(_socket, sysio->recv_in.buf, sysio->recv_in.len,
|
||||
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:
|
||||
log(__func__, " unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio->recvfrom_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool setsockopt(Sysio *sysio)
|
||||
{
|
||||
/*
|
||||
* Filter options out because lwip only supports several socket
|
||||
* options. Therefore for now we silently return 0 and notify
|
||||
* the user via debug message.
|
||||
*/
|
||||
switch (sysio->setsockopt_in.optname) {
|
||||
case SO_DEBUG:
|
||||
case SO_LINGER:
|
||||
warning("SOL_SOCKET option '", sysio->setsockopt_in.optname, "' "
|
||||
"is currently not supported, however we report success");
|
||||
return true;
|
||||
}
|
||||
|
||||
int result = ::setsockopt(_socket, sysio->setsockopt_in.level,
|
||||
sysio->setsockopt_in.optname,
|
||||
sysio->setsockopt_in.optval,
|
||||
sysio->setsockopt_in.optlen);
|
||||
|
||||
return (result == -1) ? false : true;
|
||||
}
|
||||
|
||||
ssize_t send(Sysio *sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio->send_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t sendto(Sysio *sysio)
|
||||
{
|
||||
ssize_t result = ::sendto(_socket, sysio->sendto_in.buf, sysio->sendto_in.len,
|
||||
sysio->sendto_in.flags,
|
||||
(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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio->sendto_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int shutdown(Sysio *sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool socket(Sysio *sysio)
|
||||
{
|
||||
_socket = ::socket(sysio->socket_in.domain,
|
||||
sysio->socket_in.type,
|
||||
sysio->socket_in.protocol);
|
||||
|
||||
return (_socket == -1) ? false : true;
|
||||
}
|
||||
};
|
||||
|
||||
class Socket_io_channel : public Io_channel
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
Socket_io_channel_backend *_backend;
|
||||
|
||||
public:
|
||||
|
||||
Socket_io_channel()
|
||||
:
|
||||
_backend(new (env()->heap()) Socket_io_channel_backend())
|
||||
{ }
|
||||
|
||||
Socket_io_channel(int s)
|
||||
:
|
||||
_backend(new (env()->heap()) Socket_io_channel_backend(s))
|
||||
{ }
|
||||
|
||||
~Socket_io_channel()
|
||||
{
|
||||
destroy(env()->heap(), _backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Io_channel interface (only needed methods)
|
||||
* HACK: Since lwip won't mark fds as writable, even if they
|
||||
* are, if asked multiple times we return true in this
|
||||
* case. Hopefully that won't break any time soon.
|
||||
*/
|
||||
if (wr)
|
||||
return true;
|
||||
|
||||
Io_channel_backend *backend() { return _backend; }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write(Sysio *sysio, ::size_t &count)
|
||||
{
|
||||
return _backend->write(sysio, count);
|
||||
/**
|
||||
* Socket methods
|
||||
*/
|
||||
|
||||
int accept(Sysio &sysio)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (sysio.accept_in.addrlen == 0) {
|
||||
result = ::accept(_socket, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
result = ::accept(_socket, (sockaddr *)&sysio.accept_in.addr,
|
||||
&sysio.accept_in.addrlen);
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio)
|
||||
{
|
||||
return _backend->read(sysio);
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool fcntl(Sysio* sysio)
|
||||
{
|
||||
return _backend->fcntl(sysio);
|
||||
return result;
|
||||
}
|
||||
|
||||
int bind(Sysio &sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ioctl(Sysio *sysio)
|
||||
{
|
||||
return _backend->ioctl(sysio);
|
||||
return result;
|
||||
}
|
||||
|
||||
int connect(Sysio &sysio)
|
||||
{
|
||||
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;
|
||||
case ECONNRESET: sysio.error.connect = Sysio::CONNECT_ERR_RESET; break;
|
||||
case ECONNABORTED: sysio.error.connect = Sysio::CONNECT_ERR_ABORTED; break;
|
||||
case EHOSTUNREACH: sysio.error.connect = Sysio::CONNECT_ERR_NO_ROUTE; break;
|
||||
|
||||
default:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
{
|
||||
return _backend->check_unblock(rd, wr, ex);
|
||||
return result;
|
||||
}
|
||||
|
||||
int getpeername(Sysio &sysio)
|
||||
{
|
||||
return ::getpeername(_socket, (struct sockaddr *)&sysio.getpeername_in.addr,
|
||||
(socklen_t *)&sysio.getpeername_in.addrlen);
|
||||
}
|
||||
|
||||
bool getsockopt(Sysio &sysio)
|
||||
{
|
||||
int result = ::getsockopt(_socket, sysio.getsockopt_in.level,
|
||||
sysio.getsockopt_in.optname,
|
||||
sysio.getsockopt_in.optval,
|
||||
&sysio.getsockopt_in.optlen);
|
||||
|
||||
return (result == -1) ? false : true;
|
||||
}
|
||||
|
||||
int listen(Sysio &sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
ssize_t recv(Sysio &sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio.recv_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t recvfrom(Sysio &sysio)
|
||||
{
|
||||
ssize_t result = ::recvfrom(_socket, sysio.recv_in.buf, sysio.recv_in.len,
|
||||
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:
|
||||
log(__func__, " unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio.recvfrom_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool setsockopt(Sysio &sysio)
|
||||
{
|
||||
/*
|
||||
* Filter options out because lwip only supports several socket
|
||||
* options. Therefore for now we silently return 0 and notify
|
||||
* the user via debug message.
|
||||
*/
|
||||
switch (sysio.setsockopt_in.optname) {
|
||||
case SO_DEBUG:
|
||||
case SO_LINGER:
|
||||
warning("SOL_SOCKET option '", sysio.setsockopt_in.optname, "' "
|
||||
"is currently not supported, however we report success");
|
||||
return true;
|
||||
}
|
||||
|
||||
int result = ::setsockopt(_socket, sysio.setsockopt_in.level,
|
||||
sysio.setsockopt_in.optname,
|
||||
sysio.setsockopt_in.optval,
|
||||
sysio.setsockopt_in.optlen);
|
||||
|
||||
return (result == -1) ? false : true;
|
||||
}
|
||||
|
||||
ssize_t send(Sysio &sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio.send_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t sendto(Sysio &sysio)
|
||||
{
|
||||
ssize_t result = ::sendto(_socket, sysio.sendto_in.buf, sysio.sendto_in.len,
|
||||
sysio.sendto_in.flags,
|
||||
(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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sysio.sendto_out.len = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int shutdown(Sysio &sysio)
|
||||
{
|
||||
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:
|
||||
log(__func__, ": unhandled errno: ", (int)errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool socket(Sysio &sysio)
|
||||
{
|
||||
_socket = ::socket(sysio.socket_in.domain,
|
||||
sysio.socket_in.type,
|
||||
sysio.socket_in.protocol);
|
||||
|
||||
return (_socket == -1) ? false : true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Noux::Socket_io_channel : public Io_channel
|
||||
{
|
||||
private:
|
||||
|
||||
Socket_io_channel_backend _backend;
|
||||
|
||||
public:
|
||||
|
||||
Socket_io_channel() { }
|
||||
Socket_io_channel(int s) : _backend(s) { }
|
||||
~Socket_io_channel() { }
|
||||
|
||||
/*
|
||||
* Io_channel interface (only needed methods)
|
||||
*/
|
||||
|
||||
Io_channel_backend *backend() { return &_backend; }
|
||||
|
||||
bool write(Sysio &sysio, ::size_t &count)
|
||||
{
|
||||
return _backend.write(sysio, count);
|
||||
}
|
||||
|
||||
bool read(Sysio &sysio) { return _backend.read(sysio); }
|
||||
bool fcntl(Sysio &sysio) { return _backend.fcntl(sysio); }
|
||||
bool ioctl(Sysio &sysio) { return _backend.ioctl(sysio); }
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
{
|
||||
return _backend.check_unblock(rd, wr, ex);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__SOCKET_IO_CHANNEL_H_ */
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
TARGET = noux_net
|
||||
LIBS += alarm libc libc_lwip_nic_dhcp vfs
|
||||
|
||||
SRC_CC = main.cc net.cc
|
||||
SRC_CC = main.cc syscall.cc net.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
||||
INC_DIR += $(PRG_DIR)/../
|
||||
|
||||
vpath main.cc $(PRG_DIR)/..
|
||||
vpath net.cc $(PRG_DIR)
|
||||
vpath main.cc $(PRG_DIR)/..
|
||||
vpath syscall.cc $(PRG_DIR)/..
|
||||
vpath net.cc $(PRG_DIR)
|
||||
|
@ -18,18 +18,17 @@
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
struct Family_member;
|
||||
struct Parent_execve;
|
||||
}
|
||||
|
||||
struct Parent_execve
|
||||
{
|
||||
virtual void execve_child(Family_member &child,
|
||||
const char *filename,
|
||||
Args const &args,
|
||||
Sysio::Env const &env,
|
||||
bool verbose) = 0;
|
||||
};
|
||||
|
||||
struct Noux::Parent_execve
|
||||
{
|
||||
virtual void execve_child(Family_member &child,
|
||||
const char *filename,
|
||||
Args const &args,
|
||||
Sysio::Env const &env) = 0;
|
||||
};
|
||||
|
||||
#endif /* _NOUX__PARENT_EXECVE__H_ */
|
||||
|
@ -42,13 +42,14 @@ class Noux::Pd_session_component : public Rpc_object<Pd_session>
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Pd_session_component(Rpc_entrypoint &ep, Child_policy::Name const &name,
|
||||
Pd_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
|
||||
Child_policy::Name const &name,
|
||||
Dataspace_registry &ds_registry)
|
||||
:
|
||||
_ep(ep), _pd(name.string()),
|
||||
_address_space(_ep, ds_registry, _pd, _pd.address_space()),
|
||||
_stack_area (_ep, ds_registry, _pd, _pd.stack_area()),
|
||||
_linker_area (_ep, ds_registry, _pd, _pd.linker_area())
|
||||
_ep(ep), _pd(env, name.string()),
|
||||
_address_space(alloc, _ep, ds_registry, _pd, _pd.address_space()),
|
||||
_stack_area (alloc, _ep, ds_registry, _pd, _pd.stack_area()),
|
||||
_linker_area (alloc, _ep, ds_registry, _pd, _pd.linker_area())
|
||||
{
|
||||
_ep.manage(this);
|
||||
}
|
||||
@ -60,9 +61,9 @@ class Noux::Pd_session_component : public Rpc_object<Pd_session>
|
||||
|
||||
Pd_session_capability core_pd_cap() { return _pd.cap(); }
|
||||
|
||||
void poke(addr_t dst_addr, void const *src, size_t len)
|
||||
void poke(Region_map &rm, addr_t dst_addr, char const *src, size_t len)
|
||||
{
|
||||
_address_space.poke(dst_addr, src, len);
|
||||
_address_space.poke(rm, dst_addr, src, len);
|
||||
}
|
||||
|
||||
Capability<Region_map> lookup_region_map(addr_t const addr)
|
||||
@ -76,13 +77,15 @@ class Noux::Pd_session_component : public Rpc_object<Pd_session>
|
||||
|
||||
void replay(Ram_session &dst_ram,
|
||||
Pd_session_component &dst_pd,
|
||||
Region_map &local_rm,
|
||||
Allocator &alloc,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep)
|
||||
{
|
||||
/* replay region map into new protection domain */
|
||||
_stack_area .replay(dst_ram, dst_pd.stack_area_region_map(), ds_registry, ep);
|
||||
_linker_area .replay(dst_ram, dst_pd.linker_area_region_map(), ds_registry, ep);
|
||||
_address_space.replay(dst_ram, dst_pd.address_space_region_map(), ds_registry, ep);
|
||||
_stack_area .replay(dst_ram, dst_pd.stack_area_region_map(), local_rm, alloc, ds_registry, ep);
|
||||
_linker_area .replay(dst_ram, dst_pd.linker_area_region_map(), local_rm, alloc, ds_registry, ep);
|
||||
_address_space.replay(dst_ram, dst_pd.address_space_region_map(), local_rm, alloc, ds_registry, ep);
|
||||
|
||||
Region_map &dst_address_space = dst_pd.address_space_region_map();
|
||||
Region_map &dst_stack_area = dst_pd.stack_area_region_map();
|
||||
|
@ -18,350 +18,330 @@
|
||||
#include <io_channel.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Pipe : public Reference_counter
|
||||
{
|
||||
private:
|
||||
|
||||
Lock mutable _lock;
|
||||
|
||||
enum { BUFFER_SIZE = 4096 };
|
||||
char _buffer[BUFFER_SIZE];
|
||||
|
||||
unsigned _read_offset;
|
||||
unsigned _write_offset;
|
||||
|
||||
Signal_context_capability _read_ready_sigh;
|
||||
Signal_context_capability _write_ready_sigh;
|
||||
|
||||
bool _writer_is_gone;
|
||||
|
||||
/**
|
||||
* Return space available in the buffer for writing, in bytes
|
||||
*/
|
||||
size_t _avail_buffer_space() const
|
||||
{
|
||||
if (_read_offset < _write_offset)
|
||||
return (BUFFER_SIZE - _write_offset) + _read_offset - 1;
|
||||
|
||||
if (_read_offset > _write_offset)
|
||||
return _read_offset - _write_offset - 1;
|
||||
|
||||
/* _read_offset == _write_offset */
|
||||
return BUFFER_SIZE - 1;
|
||||
}
|
||||
|
||||
bool _any_space_avail_for_writing() const
|
||||
{
|
||||
return _avail_buffer_space() > 0;;
|
||||
}
|
||||
|
||||
void _wake_up_reader()
|
||||
{
|
||||
if (_read_ready_sigh.valid())
|
||||
Signal_transmitter(_read_ready_sigh).submit();
|
||||
}
|
||||
|
||||
void _wake_up_writer()
|
||||
{
|
||||
if (_write_ready_sigh.valid())
|
||||
Signal_transmitter(_write_ready_sigh).submit();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Pipe()
|
||||
: _read_offset(0), _write_offset(0), _writer_is_gone(false) { }
|
||||
|
||||
~Pipe()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
}
|
||||
|
||||
void writer_close()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
_writer_is_gone = true;
|
||||
_write_ready_sigh = Signal_context_capability();
|
||||
_wake_up_reader();
|
||||
}
|
||||
|
||||
void reader_close()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_read_ready_sigh = Signal_context_capability();
|
||||
}
|
||||
|
||||
bool writer_is_gone() const
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
return _writer_is_gone;
|
||||
}
|
||||
|
||||
bool any_space_avail_for_writing() const
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
return _any_space_avail_for_writing();
|
||||
}
|
||||
|
||||
bool data_avail_for_reading() const
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
return _read_offset != _write_offset;
|
||||
}
|
||||
|
||||
size_t read(char *dst, size_t dst_len)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (_read_offset < _write_offset) {
|
||||
|
||||
size_t len = min(dst_len, _write_offset - _read_offset);
|
||||
memcpy(dst, &_buffer[_read_offset], len);
|
||||
|
||||
_read_offset += len;
|
||||
_wake_up_writer();
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
if (_read_offset > _write_offset) {
|
||||
|
||||
size_t const upper_len = min(dst_len, BUFFER_SIZE - _read_offset);
|
||||
memcpy(dst, &_buffer[_read_offset], upper_len);
|
||||
|
||||
size_t const lower_len = min(dst_len - upper_len, _write_offset);
|
||||
if (lower_len) {
|
||||
memcpy(dst + upper_len, &_buffer[0], lower_len);
|
||||
_read_offset = lower_len;
|
||||
} else {
|
||||
_read_offset += upper_len;
|
||||
}
|
||||
_wake_up_writer();
|
||||
|
||||
return upper_len + lower_len;
|
||||
}
|
||||
|
||||
/* _read_offset == _write_offset */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to pipe buffer
|
||||
*
|
||||
* \return number of written bytes (may be less than 'len')
|
||||
*/
|
||||
size_t write(char *src, size_t len)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
/* trim write request to the available buffer space */
|
||||
size_t const trimmed_len = min(len, _avail_buffer_space());
|
||||
|
||||
/*
|
||||
* Remember pipe state prior writing to see whether a reader
|
||||
* must be unblocked after writing.
|
||||
*/
|
||||
bool const pipe_was_empty = (_read_offset == _write_offset);
|
||||
|
||||
/* write data up to the upper boundary of the pipe buffer */
|
||||
size_t const upper_len = min(BUFFER_SIZE - _write_offset, trimmed_len);
|
||||
memcpy(&_buffer[_write_offset], src, upper_len);
|
||||
|
||||
_write_offset += upper_len;
|
||||
|
||||
/*
|
||||
* Determine number of remaining bytes beyond the buffer boundary.
|
||||
* The buffer wraps. So this data will end up in the lower part
|
||||
* of the pipe buffer.
|
||||
*/
|
||||
size_t const lower_len = trimmed_len - upper_len;
|
||||
|
||||
if (lower_len > 0) {
|
||||
|
||||
/* pipe buffer wrap-around, write remaining data to the lower part */
|
||||
memcpy(&_buffer[0], src + upper_len, lower_len);
|
||||
_write_offset = lower_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wake up reader who may block for incoming data.
|
||||
*/
|
||||
if (pipe_was_empty || !_any_space_avail_for_writing())
|
||||
_wake_up_reader();
|
||||
|
||||
/* return number of written bytes */
|
||||
return trimmed_len;
|
||||
}
|
||||
|
||||
void register_write_ready_sigh(Signal_context_capability sigh)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_write_ready_sigh = sigh;
|
||||
}
|
||||
|
||||
void register_read_ready_sigh(Signal_context_capability sigh)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_read_ready_sigh = sigh;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Pipe_sink_io_channel : public Io_channel, public Signal_dispatcher_base
|
||||
{
|
||||
private:
|
||||
|
||||
Shared_pointer<Pipe> _pipe;
|
||||
Signal_receiver &_sig_rec;
|
||||
|
||||
public:
|
||||
|
||||
Pipe_sink_io_channel(Shared_pointer<Pipe> pipe,
|
||||
Signal_receiver &sig_rec)
|
||||
: _pipe(pipe), _sig_rec(sig_rec)
|
||||
{
|
||||
pipe->register_write_ready_sigh(_sig_rec.manage(this));
|
||||
}
|
||||
|
||||
~Pipe_sink_io_channel()
|
||||
{
|
||||
_sig_rec.dissolve(this);
|
||||
_pipe->writer_close();
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
return wr && _pipe->any_space_avail_for_writing();
|
||||
}
|
||||
|
||||
bool write(Sysio *sysio, size_t &offset) override
|
||||
{
|
||||
/*
|
||||
* If the write operation is larger than the space available in
|
||||
* the pipe buffer, the write function is successively called
|
||||
* for different portions of original write request. The
|
||||
* current read pointer of the request is tracked via the
|
||||
* 'count' in/out argument. If completed, 'count' equals
|
||||
* 'write_in.count'.
|
||||
*/
|
||||
|
||||
/* dimension the pipe write operation to the not yet written data */
|
||||
size_t curr_count = _pipe->write(sysio->write_in.chunk + offset,
|
||||
sysio->write_in.count - offset);
|
||||
offset += curr_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
sysio->fcntl_out.result = Sysio::OPEN_MODE_WRONLY;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
sysio->fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
** Signal_dispatcher_base interface **
|
||||
**************************************/
|
||||
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new STDIN input
|
||||
*/
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Pipe_source_io_channel : public Io_channel, public Signal_dispatcher_base
|
||||
{
|
||||
private:
|
||||
|
||||
Shared_pointer<Pipe> _pipe;
|
||||
Signal_receiver &_sig_rec;
|
||||
|
||||
public:
|
||||
|
||||
Pipe_source_io_channel(Shared_pointer<Pipe> pipe, Signal_receiver &sig_rec)
|
||||
: _pipe(pipe), _sig_rec(sig_rec)
|
||||
{
|
||||
_pipe->register_read_ready_sigh(sig_rec.manage(this));
|
||||
}
|
||||
|
||||
~Pipe_source_io_channel()
|
||||
{
|
||||
_sig_rec.dissolve(this);
|
||||
_pipe->reader_close();
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
/* unblock if the writer has already closed its pipe end */
|
||||
if (_pipe->writer_is_gone())
|
||||
return true;
|
||||
|
||||
return (rd && _pipe->data_avail_for_reading());
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio) override
|
||||
{
|
||||
size_t const max_count =
|
||||
min(sysio->read_in.count,
|
||||
sizeof(sysio->read_out.chunk));
|
||||
|
||||
sysio->read_out.count =
|
||||
_pipe->read(sysio->read_out.chunk, max_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
sysio->fcntl_out.result = Sysio::OPEN_MODE_RDONLY;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
sysio->fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
** Signal_dispatcher_base interface **
|
||||
**************************************/
|
||||
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new STDIN input
|
||||
*/
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
};
|
||||
class Pipe;
|
||||
class Pipe_sink_io_channel;
|
||||
class Pipe_source_io_channel;
|
||||
}
|
||||
|
||||
|
||||
class Noux::Pipe : public Reference_counter
|
||||
{
|
||||
private:
|
||||
|
||||
Lock mutable _lock;
|
||||
|
||||
enum { BUFFER_SIZE = 4096 };
|
||||
char _buffer[BUFFER_SIZE];
|
||||
|
||||
unsigned _read_offset;
|
||||
unsigned _write_offset;
|
||||
|
||||
Signal_context_capability _read_ready_sigh;
|
||||
Signal_context_capability _write_ready_sigh;
|
||||
|
||||
bool _writer_is_gone;
|
||||
|
||||
/**
|
||||
* Return space available in the buffer for writing, in bytes
|
||||
*/
|
||||
size_t _avail_buffer_space() const
|
||||
{
|
||||
if (_read_offset < _write_offset)
|
||||
return (BUFFER_SIZE - _write_offset) + _read_offset - 1;
|
||||
|
||||
if (_read_offset > _write_offset)
|
||||
return _read_offset - _write_offset - 1;
|
||||
|
||||
/* _read_offset == _write_offset */
|
||||
return BUFFER_SIZE - 1;
|
||||
}
|
||||
|
||||
bool _any_space_avail_for_writing() const
|
||||
{
|
||||
return _avail_buffer_space() > 0;;
|
||||
}
|
||||
|
||||
void _wake_up_reader()
|
||||
{
|
||||
if (_read_ready_sigh.valid())
|
||||
Signal_transmitter(_read_ready_sigh).submit();
|
||||
}
|
||||
|
||||
void _wake_up_writer()
|
||||
{
|
||||
if (_write_ready_sigh.valid())
|
||||
Signal_transmitter(_write_ready_sigh).submit();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Pipe()
|
||||
: _read_offset(0), _write_offset(0), _writer_is_gone(false) { }
|
||||
|
||||
~Pipe()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
}
|
||||
|
||||
void writer_close()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
_writer_is_gone = true;
|
||||
_write_ready_sigh = Signal_context_capability();
|
||||
_wake_up_reader();
|
||||
}
|
||||
|
||||
void reader_close()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_read_ready_sigh = Signal_context_capability();
|
||||
}
|
||||
|
||||
bool writer_is_gone() const
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
return _writer_is_gone;
|
||||
}
|
||||
|
||||
bool any_space_avail_for_writing() const
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
return _any_space_avail_for_writing();
|
||||
}
|
||||
|
||||
bool data_avail_for_reading() const
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
return _read_offset != _write_offset;
|
||||
}
|
||||
|
||||
size_t read(char *dst, size_t dst_len)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (_read_offset < _write_offset) {
|
||||
|
||||
size_t len = min(dst_len, _write_offset - _read_offset);
|
||||
memcpy(dst, &_buffer[_read_offset], len);
|
||||
|
||||
_read_offset += len;
|
||||
_wake_up_writer();
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
if (_read_offset > _write_offset) {
|
||||
|
||||
size_t const upper_len = min(dst_len, BUFFER_SIZE - _read_offset);
|
||||
memcpy(dst, &_buffer[_read_offset], upper_len);
|
||||
|
||||
size_t const lower_len = min(dst_len - upper_len, _write_offset);
|
||||
if (lower_len) {
|
||||
memcpy(dst + upper_len, &_buffer[0], lower_len);
|
||||
_read_offset = lower_len;
|
||||
} else {
|
||||
_read_offset += upper_len;
|
||||
}
|
||||
_wake_up_writer();
|
||||
|
||||
return upper_len + lower_len;
|
||||
}
|
||||
|
||||
/* _read_offset == _write_offset */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to pipe buffer
|
||||
*
|
||||
* \return number of written bytes (may be less than 'len')
|
||||
*/
|
||||
size_t write(char *src, size_t len)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
/* trim write request to the available buffer space */
|
||||
size_t const trimmed_len = min(len, _avail_buffer_space());
|
||||
|
||||
/*
|
||||
* Remember pipe state prior writing to see whether a reader
|
||||
* must be unblocked after writing.
|
||||
*/
|
||||
bool const pipe_was_empty = (_read_offset == _write_offset);
|
||||
|
||||
/* write data up to the upper boundary of the pipe buffer */
|
||||
size_t const upper_len = min(BUFFER_SIZE - _write_offset, trimmed_len);
|
||||
memcpy(&_buffer[_write_offset], src, upper_len);
|
||||
|
||||
_write_offset += upper_len;
|
||||
|
||||
/*
|
||||
* Determine number of remaining bytes beyond the buffer boundary.
|
||||
* The buffer wraps. So this data will end up in the lower part
|
||||
* of the pipe buffer.
|
||||
*/
|
||||
size_t const lower_len = trimmed_len - upper_len;
|
||||
|
||||
if (lower_len > 0) {
|
||||
|
||||
/* pipe buffer wrap-around, write remaining data to the lower part */
|
||||
memcpy(&_buffer[0], src + upper_len, lower_len);
|
||||
_write_offset = lower_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wake up reader who may block for incoming data.
|
||||
*/
|
||||
if (pipe_was_empty || !_any_space_avail_for_writing())
|
||||
_wake_up_reader();
|
||||
|
||||
/* return number of written bytes */
|
||||
return trimmed_len;
|
||||
}
|
||||
|
||||
void register_write_ready_sigh(Signal_context_capability sigh)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_write_ready_sigh = sigh;
|
||||
}
|
||||
|
||||
void register_read_ready_sigh(Signal_context_capability sigh)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_read_ready_sigh = sigh;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Noux::Pipe_sink_io_channel : public Io_channel
|
||||
{
|
||||
private:
|
||||
|
||||
Signal_handler<Pipe_sink_io_channel> _write_ready_handler;
|
||||
|
||||
void _handle_write_ready() { Io_channel::invoke_all_notifiers(); }
|
||||
|
||||
Shared_pointer<Pipe> _pipe;
|
||||
|
||||
public:
|
||||
|
||||
Pipe_sink_io_channel(Shared_pointer<Pipe> pipe, Entrypoint &ep)
|
||||
:
|
||||
_write_ready_handler(ep, *this, &Pipe_sink_io_channel::_handle_write_ready),
|
||||
_pipe(pipe)
|
||||
{
|
||||
pipe->register_write_ready_sigh(_write_ready_handler);
|
||||
}
|
||||
|
||||
~Pipe_sink_io_channel() { _pipe->writer_close(); }
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
return wr && _pipe->any_space_avail_for_writing();
|
||||
}
|
||||
|
||||
bool write(Sysio &sysio, size_t &offset) override
|
||||
{
|
||||
/*
|
||||
* If the write operation is larger than the space available in
|
||||
* the pipe buffer, the write function is successively called
|
||||
* for different portions of original write request. The
|
||||
* current read pointer of the request is tracked via the
|
||||
* 'count' in/out argument. If completed, 'count' equals
|
||||
* 'write_in.count'.
|
||||
*/
|
||||
|
||||
/* dimension the pipe write operation to the not yet written data */
|
||||
size_t curr_count = _pipe->write(sysio.write_in.chunk + offset,
|
||||
sysio.write_in.count - offset);
|
||||
offset += curr_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio &sysio) override
|
||||
{
|
||||
switch (sysio.fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
sysio.fcntl_out.result = Sysio::OPEN_MODE_WRONLY;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool fstat(Sysio &sysio) override
|
||||
{
|
||||
sysio.fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Noux::Pipe_source_io_channel : public Io_channel
|
||||
{
|
||||
private:
|
||||
|
||||
Signal_handler<Pipe_source_io_channel> _read_avail_handler;
|
||||
|
||||
void _handle_read_avail() { Io_channel::invoke_all_notifiers(); }
|
||||
|
||||
Shared_pointer<Pipe> _pipe;
|
||||
|
||||
public:
|
||||
|
||||
Pipe_source_io_channel(Shared_pointer<Pipe> pipe, Entrypoint &ep)
|
||||
:
|
||||
_read_avail_handler(ep, *this, &Pipe_source_io_channel::_handle_read_avail),
|
||||
_pipe(pipe)
|
||||
{
|
||||
_pipe->register_read_ready_sigh(_read_avail_handler);
|
||||
}
|
||||
|
||||
~Pipe_source_io_channel() { _pipe->reader_close(); }
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
/* unblock if the writer has already closed its pipe end */
|
||||
if (_pipe->writer_is_gone())
|
||||
return true;
|
||||
|
||||
return (rd && _pipe->data_avail_for_reading());
|
||||
}
|
||||
|
||||
bool read(Sysio &sysio) override
|
||||
{
|
||||
size_t const max_count =
|
||||
min(sysio.read_in.count,
|
||||
sizeof(sysio.read_out.chunk));
|
||||
|
||||
sysio.read_out.count =
|
||||
_pipe->read(sysio.read_out.chunk, max_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio &sysio) override
|
||||
{
|
||||
switch (sysio.fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
sysio.fcntl_out.result = Sysio::OPEN_MODE_RDONLY;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool fstat(Sysio &sysio) override
|
||||
{
|
||||
sysio.fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__PIPE_IO_CHANNEL_H_ */
|
||||
|
@ -26,163 +26,164 @@
|
||||
/* Genode includes */
|
||||
#include <ram_session/client.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/env.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <dataspace_registry.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
struct Ram_dataspace_info : Dataspace_info,
|
||||
List<Ram_dataspace_info>::Element
|
||||
{
|
||||
Ram_dataspace_info(Ram_dataspace_capability ds_cap)
|
||||
: Dataspace_info(ds_cap) { }
|
||||
|
||||
Dataspace_capability fork(Ram_session &ram,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &) override
|
||||
{
|
||||
size_t const size = Dataspace_client(ds_cap()).size();
|
||||
|
||||
Ram_dataspace_capability dst_ds;
|
||||
|
||||
try { dst_ds = ram.alloc(size); }
|
||||
catch (...) { return Dataspace_capability(); }
|
||||
|
||||
void *src = 0;
|
||||
try {
|
||||
src = env()->rm_session()->attach(ds_cap());
|
||||
} catch (...) { }
|
||||
|
||||
void *dst = 0;
|
||||
try {
|
||||
dst = env()->rm_session()->attach(dst_ds);
|
||||
} catch (...) { }
|
||||
|
||||
if (src && dst)
|
||||
memcpy(dst, src, size);
|
||||
|
||||
if (src) env()->rm_session()->detach(src);
|
||||
if (dst) env()->rm_session()->detach(dst);
|
||||
|
||||
if (!src || !dst) {
|
||||
ram.free(dst_ds);
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
ds_registry.insert(new (env()->heap()) Ram_dataspace_info(dst_ds));
|
||||
|
||||
return dst_ds;
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
{
|
||||
if ((dst_offset >= size()) || (dst_offset + len > size())) {
|
||||
error("illegal attemt to write beyond dataspace boundary");
|
||||
return;
|
||||
}
|
||||
|
||||
char *dst = 0;
|
||||
try {
|
||||
dst = env()->rm_session()->attach(ds_cap());
|
||||
} catch (...) { }
|
||||
|
||||
if (src && dst)
|
||||
memcpy(dst + dst_offset, src, len);
|
||||
|
||||
if (dst) env()->rm_session()->detach(dst);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Ram_session_component : public Rpc_object<Ram_session>
|
||||
{
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
|
||||
List<Ram_dataspace_info> _list;
|
||||
|
||||
/*
|
||||
* Track the RAM resources accumulated via RAM session allocations.
|
||||
*
|
||||
* XXX not used yet
|
||||
*/
|
||||
size_t _used_quota;
|
||||
|
||||
Dataspace_registry &_registry;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Ram_session_component(Rpc_entrypoint &ep, Dataspace_registry ®istry)
|
||||
: _ep(ep), _used_quota(0), _registry(registry) { _ep.manage(this); }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Ram_session_component()
|
||||
{
|
||||
_ep.dissolve(this);
|
||||
Ram_dataspace_info *info = 0;
|
||||
while ((info = _list.first()))
|
||||
free(static_cap_cast<Ram_dataspace>(info->ds_cap()));
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** Ram_session interface **
|
||||
***************************/
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached)
|
||||
{
|
||||
Ram_dataspace_capability ds_cap =
|
||||
env()->ram_session()->alloc(size, cached);
|
||||
|
||||
Ram_dataspace_info *ds_info = new (env()->heap())
|
||||
Ram_dataspace_info(ds_cap);
|
||||
|
||||
_used_quota += ds_info->size();
|
||||
|
||||
_registry.insert(ds_info);
|
||||
_list.insert(ds_info);
|
||||
|
||||
return ds_cap;
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability ds_cap)
|
||||
{
|
||||
Ram_dataspace_info *ds_info;
|
||||
|
||||
auto lambda = [&] (Ram_dataspace_info *rdi) {
|
||||
ds_info = rdi;
|
||||
|
||||
if (!ds_info) {
|
||||
error("RAM free: dataspace lookup failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_registry.remove(ds_info);
|
||||
|
||||
ds_info->dissolve_users();
|
||||
|
||||
_list.remove(ds_info);
|
||||
_used_quota -= ds_info->size();
|
||||
|
||||
env()->ram_session()->free(ds_cap);
|
||||
};
|
||||
_registry.apply(ds_cap, lambda);
|
||||
destroy(env()->heap(), ds_info);
|
||||
}
|
||||
|
||||
int ref_account(Ram_session_capability) { return 0; }
|
||||
int transfer_quota(Ram_session_capability, size_t) { return 0; }
|
||||
size_t quota() { return env()->ram_session()->quota(); }
|
||||
size_t used() { return _used_quota; }
|
||||
};
|
||||
struct Ram_dataspace_info;
|
||||
struct Ram_session_component;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
struct Noux::Ram_dataspace_info : Dataspace_info,
|
||||
List<Ram_dataspace_info>::Element
|
||||
{
|
||||
Ram_dataspace_info(Ram_dataspace_capability ds_cap)
|
||||
: Dataspace_info(ds_cap) { }
|
||||
|
||||
Dataspace_capability fork(Ram_session &ram,
|
||||
Region_map &local_rm,
|
||||
Allocator &alloc,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &) override
|
||||
{
|
||||
size_t const size = Dataspace_client(ds_cap()).size();
|
||||
Ram_dataspace_capability dst_ds_cap;
|
||||
|
||||
try {
|
||||
dst_ds_cap = ram.alloc(size);
|
||||
|
||||
Attached_dataspace src_ds(local_rm, ds_cap());
|
||||
Attached_dataspace dst_ds(local_rm, dst_ds_cap);
|
||||
memcpy(dst_ds.local_addr<char>(), src_ds.local_addr<char>(), size);
|
||||
|
||||
ds_registry.insert(new (alloc) Ram_dataspace_info(dst_ds_cap));
|
||||
return dst_ds_cap;
|
||||
|
||||
} catch (...) {
|
||||
error("fork of RAM dataspace failed");
|
||||
|
||||
if (dst_ds_cap.valid())
|
||||
ram.free(dst_ds_cap);
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
}
|
||||
|
||||
void poke(Region_map &rm, addr_t dst_offset, char const *src, size_t len) override
|
||||
{
|
||||
if (!src) return;
|
||||
|
||||
if ((dst_offset >= size()) || (dst_offset + len > size())) {
|
||||
error("illegal attemt to write beyond dataspace boundary");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Attached_dataspace ds(rm, ds_cap());
|
||||
memcpy(ds.local_addr<char>() + dst_offset, src, len);
|
||||
} catch (...) { warning("poke: failed to attach RAM dataspace"); }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Noux::Ram_session_component : public Rpc_object<Ram_session>
|
||||
{
|
||||
private:
|
||||
|
||||
Ram_session &_ram;
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
|
||||
List<Ram_dataspace_info> _list;
|
||||
|
||||
/*
|
||||
* Track the RAM resources accumulated via RAM session allocations.
|
||||
*
|
||||
* XXX not used yet
|
||||
*/
|
||||
size_t _used_quota;
|
||||
|
||||
Dataspace_registry &_registry;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Ram_session_component(Ram_session &ram, Allocator &alloc,
|
||||
Rpc_entrypoint &ep, Dataspace_registry ®istry)
|
||||
:
|
||||
_ram(ram), _alloc(alloc), _ep(ep), _used_quota(0),
|
||||
_registry(registry)
|
||||
{
|
||||
_ep.manage(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Ram_session_component()
|
||||
{
|
||||
_ep.dissolve(this);
|
||||
Ram_dataspace_info *info = 0;
|
||||
while ((info = _list.first()))
|
||||
free(static_cap_cast<Ram_dataspace>(info->ds_cap()));
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** Ram_session interface **
|
||||
***************************/
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached)
|
||||
{
|
||||
Ram_dataspace_capability ds_cap =
|
||||
_ram.alloc(size, cached);
|
||||
|
||||
Ram_dataspace_info *ds_info = new (_alloc) Ram_dataspace_info(ds_cap);
|
||||
|
||||
_used_quota += ds_info->size();
|
||||
|
||||
_registry.insert(ds_info);
|
||||
_list.insert(ds_info);
|
||||
|
||||
return ds_cap;
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability ds_cap)
|
||||
{
|
||||
Ram_dataspace_info *ds_info;
|
||||
|
||||
auto lambda = [&] (Ram_dataspace_info *rdi) {
|
||||
ds_info = rdi;
|
||||
|
||||
if (!ds_info) {
|
||||
error("RAM free: dataspace lookup failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_registry.remove(ds_info);
|
||||
|
||||
ds_info->dissolve_users();
|
||||
|
||||
_list.remove(ds_info);
|
||||
_used_quota -= ds_info->size();
|
||||
|
||||
_ram.free(ds_cap);
|
||||
};
|
||||
_registry.apply(ds_cap, lambda);
|
||||
destroy(_alloc, ds_info);
|
||||
}
|
||||
|
||||
int ref_account(Ram_session_capability) { return 0; }
|
||||
int transfer_quota(Ram_session_capability, size_t) { return 0; }
|
||||
size_t quota() { return _ram.quota(); }
|
||||
size_t used() { return _used_quota; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__RAM_SESSION_COMPONENT_H_ */
|
||||
|
@ -1,281 +0,0 @@
|
||||
/*
|
||||
* \brief Device Random filesystem
|
||||
* \author Josef Soentgen
|
||||
* \date 2012-07-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 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 _NOUX__RANDOM_FILE_SYSTEM_H_
|
||||
#define _NOUX__RANDOM_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/stdint.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010, 2012
|
||||
* Thorsten Glaser <tg@mirbsd.org>
|
||||
* Copyright (c) 2012
|
||||
* Josef Soentgen <cnuke@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
* are retained or reproduced in an accompanying document, permission
|
||||
* is granted to deal in this work without restriction, including un-
|
||||
* limited rights to use, publicly perform, distribute, sell, modify,
|
||||
* merge, give away, or sublicence.
|
||||
*
|
||||
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
|
||||
* the utmost extent permitted by applicable law, neither express nor
|
||||
* implied; without malicious intent or gross negligence. In no event
|
||||
* may a licensor, author or contributor be held liable for indirect,
|
||||
* direct, other damage, loss, or other issues arising in any way out
|
||||
* of dealing in the work, even if advised of the possibility of such
|
||||
* damage or existence of a defect, except proven that it results out
|
||||
* of said person's immediate fault when using the work as intended.
|
||||
*-
|
||||
* arc4random for use as NOUX random device.
|
||||
*
|
||||
* From:
|
||||
* MirOS: src/kern/c/arcfour_base.c,v 1.1 2010/09/12 17:10:49 tg Exp
|
||||
* MirOS: src/kern/c/arcfour_ksa.c,v 1.1 2010/09/12 17:10:50 tg Exp
|
||||
* MirOS: src/lib/libc/crypt/arc4random_base.c,v 1.4 2011/07/06 22:22:09 tg Exp
|
||||
* MirOS: src/lib/libc/crypt/arc4random_buf.c,v 1.1 2010/09/12 17:10:53 tg Exp
|
||||
*/
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
/**
|
||||
* Arcfour cipher re-implementation from (alledged) spec description.
|
||||
*/
|
||||
|
||||
class Arc4random
|
||||
{
|
||||
private:
|
||||
|
||||
uint8_t S[256];
|
||||
uint8_t i;
|
||||
uint8_t j;
|
||||
uint16_t num;
|
||||
uint8_t initialised;
|
||||
|
||||
/*
|
||||
* Base cipher operation: initialise state
|
||||
*/
|
||||
void init(void)
|
||||
{
|
||||
register uint8_t n = 0;
|
||||
|
||||
do {
|
||||
S[n] = n;
|
||||
} while (++n);
|
||||
|
||||
i = j = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Base cipher operation: get byte of keystream.
|
||||
*/
|
||||
uint8_t byte(void)
|
||||
{
|
||||
register uint8_t si, sj;
|
||||
|
||||
si = S[++i];
|
||||
j += si;
|
||||
sj = S[j];
|
||||
S[i] = sj;
|
||||
S[j] = si;
|
||||
return (S[(uint8_t)(si + sj)]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Normal key scheduling algorithm.
|
||||
*/
|
||||
void ksa(const uint8_t *key, size_t keylen)
|
||||
{
|
||||
register uint8_t si, n = 0;
|
||||
|
||||
--i;
|
||||
do {
|
||||
++i;
|
||||
si = S[i];
|
||||
j = (uint8_t)(j + si + key[n++ % keylen]);
|
||||
S[i] = S[j];
|
||||
S[j] = si;
|
||||
} while (n);
|
||||
j = ++i;
|
||||
}
|
||||
|
||||
/*
|
||||
* arc4random implementation
|
||||
*/
|
||||
void stir_unlocked(void)
|
||||
{
|
||||
register unsigned int m;
|
||||
uint8_t n;
|
||||
struct {
|
||||
// struct timeval tv;
|
||||
// pid_t mypid;
|
||||
uint32_t mypid;
|
||||
const void *stkptr, *bssptr, *txtptr;
|
||||
uint16_t num;
|
||||
uint8_t initialised;
|
||||
// FIXME sizeof (sb) should be as close to 256 as possible
|
||||
} sb;
|
||||
|
||||
/* save some state; while not a secret, helps through variety */
|
||||
//sb.mypid = getpid();
|
||||
sb.mypid = 42;
|
||||
sb.stkptr = &sb;
|
||||
sb.bssptr = &i;
|
||||
//sb.txtptr = &byte;
|
||||
sb.txtptr = (const void *)0xDEADBEEF;;
|
||||
sb.num = num;
|
||||
sb.initialised = initialised;
|
||||
|
||||
/* initialise i, j and the S-box if not done yet */
|
||||
if (!initialised) {
|
||||
init();
|
||||
initialised = 1;
|
||||
}
|
||||
|
||||
// FIXME initialize more sb member
|
||||
|
||||
/* dance around by some bytes for added protection */
|
||||
n = byte();
|
||||
/* and carry some over to below */
|
||||
m = byte();
|
||||
while (n--)
|
||||
(void)byte();
|
||||
m += byte();
|
||||
|
||||
/* while time is not a secret, it helps through variety */
|
||||
//gettimeofday(&sb.tv, NULL);
|
||||
|
||||
/* actually add the hopefully random-containing seed */
|
||||
ksa((const uint8_t *)&sb, sizeof(sb));
|
||||
|
||||
/* throw away the first part of the arcfour keystream */
|
||||
/* with some bytes varied for added protection */
|
||||
m += 256 * 4 + (byte() & 0x1F);
|
||||
while (m--)
|
||||
(void)byte();
|
||||
/* state is now good for so many bytes: (not so much in NOUX) */
|
||||
num = 2000;
|
||||
}
|
||||
|
||||
void buf(void *buf_, unsigned long long len)
|
||||
{
|
||||
size_t chunk;
|
||||
uint8_t *buf = (uint8_t *)buf_;
|
||||
uint8_t m, n;
|
||||
|
||||
/* operate in chunks of at most 256 bytes */
|
||||
while ((chunk = len > 256 ? 256 : len) > 0) {
|
||||
/* adjust len */
|
||||
len -= chunk;
|
||||
|
||||
/* is the state good for this? (or even initialised, yet?) */
|
||||
if (num < chunk)
|
||||
stir_unlocked();
|
||||
|
||||
/* dance around a few bytes for added protection */
|
||||
m = byte() & 3;
|
||||
/* and carry some down below */
|
||||
n = byte() & 3;
|
||||
while (m--)
|
||||
(void)byte();
|
||||
|
||||
/* actually read out the keystream into buf */
|
||||
while (chunk--) {
|
||||
*buf++ = byte();
|
||||
}
|
||||
|
||||
/* dance around the bytes read from above, for protection */
|
||||
while (n--)
|
||||
(void)byte();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Arc4random(void* bytes, size_t nbytes)
|
||||
:
|
||||
i(0),
|
||||
j(0),
|
||||
num(0),
|
||||
initialised(0)
|
||||
{
|
||||
memset(S, 0, 256);
|
||||
}
|
||||
|
||||
void get(void *_buf, unsigned long long len)
|
||||
{
|
||||
buf(_buf, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Random_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
Arc4random _arc4random;
|
||||
|
||||
public:
|
||||
|
||||
Random_file_system(Genode::Env&, Genode::Allocator&,
|
||||
Genode::Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config),
|
||||
_arc4random(0, 0)
|
||||
{ }
|
||||
|
||||
static char const *name() { return "random"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs::Vfs_handle *, char const *,
|
||||
Vfs::file_size buf_size,
|
||||
Vfs::file_size &out_count) override
|
||||
{
|
||||
out_count = buf_size;
|
||||
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs::Vfs_handle *vfs_handle, char *dst,
|
||||
Vfs::file_size count,
|
||||
Vfs::file_size &out_count) override
|
||||
{
|
||||
_arc4random.get(dst, count);
|
||||
out_count = count;
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs::Vfs_handle *,
|
||||
Vfs::file_size) override
|
||||
{
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__RANDOM_H_ */
|
@ -17,28 +17,29 @@
|
||||
namespace Noux {
|
||||
|
||||
class Index_out_of_range { };
|
||||
|
||||
template <typename T>
|
||||
struct Range_checked_index
|
||||
{
|
||||
T value;
|
||||
T const max;
|
||||
|
||||
Range_checked_index(T value, T max)
|
||||
: value(value), max(max) { }
|
||||
|
||||
T operator++ (int)
|
||||
{
|
||||
T old_value = value;
|
||||
|
||||
if (++value >= max)
|
||||
throw Index_out_of_range();
|
||||
|
||||
return old_value;
|
||||
}
|
||||
|
||||
operator T () { return value; }
|
||||
};
|
||||
template <typename> struct Range_checked_index;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct Noux::Range_checked_index
|
||||
{
|
||||
T value;
|
||||
T const max;
|
||||
|
||||
Range_checked_index(T value, T max) : value(value), max(max) { }
|
||||
|
||||
T operator++ (int)
|
||||
{
|
||||
T old_value = value;
|
||||
|
||||
if (++value >= max)
|
||||
throw Index_out_of_range();
|
||||
|
||||
return old_value;
|
||||
}
|
||||
|
||||
operator T () { return value; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__RANGE_CHECKED_INDEX_H_ */
|
||||
|
@ -36,6 +36,8 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
static constexpr bool verbose_attach = false;
|
||||
static constexpr bool verbose_replay = false;
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
|
||||
/**
|
||||
@ -104,13 +106,13 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
* quota upgrades
|
||||
* \param rm region map at core
|
||||
*/
|
||||
Region_map_component(Rpc_entrypoint &ep,
|
||||
Region_map_component(Allocator &alloc, Rpc_entrypoint &ep,
|
||||
Dataspace_registry &ds_registry,
|
||||
Pd_connection &pd,
|
||||
Capability<Region_map> rm)
|
||||
:
|
||||
Dataspace_info(Region_map_client(rm).dataspace()),
|
||||
_ep(ep), _rm(rm), _pd(pd), _ds_registry(ds_registry)
|
||||
_alloc(alloc), _ep(ep), _rm(rm), _pd(pd), _ds_registry(ds_registry)
|
||||
{
|
||||
_ep.manage(this);
|
||||
_ds_registry.insert(this);
|
||||
@ -159,6 +161,8 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
*/
|
||||
void replay(Ram_session &dst_ram,
|
||||
Region_map &dst_rm,
|
||||
Region_map &local_rm,
|
||||
Allocator &alloc,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep)
|
||||
{
|
||||
@ -170,7 +174,7 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
Dataspace_capability ds;
|
||||
if (info) {
|
||||
|
||||
ds = info->fork(dst_ram, ds_registry, ep);
|
||||
ds = info->fork(dst_ram, local_rm, alloc, ds_registry, ep);
|
||||
|
||||
/*
|
||||
* XXX We could detect dataspaces that are attached
|
||||
@ -242,7 +246,7 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
}
|
||||
}
|
||||
|
||||
Region * region = new (env()->heap())
|
||||
Region * region = new (_alloc)
|
||||
Region(*this, ds, size, offset, local_addr);
|
||||
|
||||
/* register region as user of RAM dataspaces */
|
||||
@ -289,10 +293,9 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
_ds_registry.apply(region->ds, [&] (Dataspace_info *info) {
|
||||
if (info) info->unregister_user(*region); });
|
||||
|
||||
destroy(env()->heap(), region);
|
||||
destroy(_alloc, region);
|
||||
|
||||
_rm.detach(local_addr);
|
||||
|
||||
}
|
||||
|
||||
void fault_handler(Signal_context_capability handler) override
|
||||
@ -323,6 +326,8 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
******************************/
|
||||
|
||||
Dataspace_capability fork(Ram_session &,
|
||||
Region_map &,
|
||||
Allocator &,
|
||||
Dataspace_registry &,
|
||||
Rpc_entrypoint &) override
|
||||
{
|
||||
@ -358,7 +363,7 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
return _ds_registry.apply(region->ds, lambda);
|
||||
}
|
||||
|
||||
void poke(addr_t dst_addr, void const *src, size_t len) override
|
||||
void poke(Region_map &rm, addr_t dst_addr, char const *src, size_t len) override
|
||||
{
|
||||
Dataspace_capability ds_cap;
|
||||
addr_t local_addr;
|
||||
@ -395,7 +400,7 @@ class Noux::Region_map_component : public Rpc_object<Region_map>,
|
||||
error("attempt to write to unknown dataspace type");
|
||||
for (;;);
|
||||
}
|
||||
info->poke(dst_addr - local_addr, src, len);
|
||||
info->poke(rm, dst_addr - local_addr, src, len);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -32,14 +32,16 @@ struct Noux::Rom_dataspace_info : Dataspace_info
|
||||
~Rom_dataspace_info() { }
|
||||
|
||||
Dataspace_capability fork(Ram_session &,
|
||||
Region_map &,
|
||||
Allocator &alloc,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &) override
|
||||
{
|
||||
ds_registry.insert(new (env()->heap()) Rom_dataspace_info(ds_cap()));
|
||||
ds_registry.insert(new (alloc) Rom_dataspace_info(ds_cap()));
|
||||
return ds_cap();
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
void poke(Region_map &, addr_t dst_offset, char const *src, size_t len)
|
||||
{
|
||||
error("attempt to poke onto a ROM dataspace");
|
||||
}
|
||||
@ -101,7 +103,7 @@ class Noux::Rom_session_component : public Rpc_object<Rom_session>
|
||||
*/
|
||||
Constructible<Rom_connection> _rom_from_parent;
|
||||
|
||||
Dataspace_capability _init_ds_cap(Name const &name)
|
||||
Dataspace_capability _init_ds_cap(Env &env, Name const &name)
|
||||
{
|
||||
if (name.string()[0] == '/') {
|
||||
_rom_from_vfs.construct(_root_dir, name);
|
||||
@ -111,7 +113,7 @@ class Noux::Rom_session_component : public Rpc_object<Rom_session>
|
||||
if (name == forked_magic_binary_name())
|
||||
return Dataspace_capability();
|
||||
|
||||
_rom_from_parent.construct(name.string());
|
||||
_rom_from_parent.construct(env, name.string());
|
||||
Dataspace_capability ds = _rom_from_parent->dataspace();
|
||||
return ds;
|
||||
}
|
||||
@ -120,14 +122,15 @@ class Noux::Rom_session_component : public Rpc_object<Rom_session>
|
||||
|
||||
public:
|
||||
|
||||
Rom_session_component(Rpc_entrypoint &ep, Vfs::Dir_file_system &root_dir,
|
||||
Rom_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
|
||||
Vfs::Dir_file_system &root_dir,
|
||||
Dataspace_registry &ds_registry, Name const &name)
|
||||
:
|
||||
_ep(ep), _root_dir(root_dir), _ds_registry(ds_registry),
|
||||
_ds_cap(_init_ds_cap(name))
|
||||
_ds_cap(_init_ds_cap(env, name))
|
||||
{
|
||||
_ep.manage(this);
|
||||
_ds_registry.insert(new (env()->heap()) Rom_dataspace_info(_ds_cap));
|
||||
_ds_registry.insert(new (alloc) Rom_dataspace_info(_ds_cap));
|
||||
}
|
||||
|
||||
~Rom_session_component()
|
||||
|
@ -23,135 +23,137 @@
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Reference_counter;
|
||||
class Shared_pointer_base;
|
||||
|
||||
class Reference_counter
|
||||
{
|
||||
private:
|
||||
|
||||
Lock _lock;
|
||||
long _value;
|
||||
|
||||
friend class Shared_pointer_base;
|
||||
|
||||
void _inc_ref_count()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_value++;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return reference counter after decrement
|
||||
*/
|
||||
long _dec_ref_count()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
return --_value;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Reference_counter() : _value(0) { }
|
||||
};
|
||||
|
||||
|
||||
class Shared_pointer_base
|
||||
{
|
||||
protected:
|
||||
|
||||
Reference_counter *_ref_counter;
|
||||
|
||||
Shared_pointer_base(Reference_counter *ref_counter)
|
||||
: _ref_counter(ref_counter) { }
|
||||
|
||||
void _inc_ref_count() {
|
||||
if (_ref_counter) _ref_counter->_inc_ref_count(); }
|
||||
|
||||
bool _dec_ref_count() {
|
||||
return _ref_counter && (_ref_counter->_dec_ref_count() == 0); }
|
||||
|
||||
long count() const { return _ref_counter ? _ref_counter->_value : -99; }
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class Shared_pointer : public Shared_pointer_base
|
||||
{
|
||||
private:
|
||||
|
||||
T *_ptr;
|
||||
Allocator *_alloc;
|
||||
|
||||
void _dec_ref_count()
|
||||
{
|
||||
if (Shared_pointer_base::_dec_ref_count()) {
|
||||
|
||||
destroy(_alloc, _ptr);
|
||||
_ptr = 0;
|
||||
_alloc = 0;
|
||||
_ref_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Shared_pointer() : Shared_pointer_base(0), _ptr(0), _alloc(0) { }
|
||||
|
||||
Shared_pointer(T *ptr, Allocator *alloc)
|
||||
: Shared_pointer_base(ptr), _ptr(ptr), _alloc(alloc)
|
||||
{
|
||||
_inc_ref_count();
|
||||
}
|
||||
|
||||
Shared_pointer(Shared_pointer const & from)
|
||||
:
|
||||
Shared_pointer_base(from._ref_counter),
|
||||
_ptr(from._ptr), _alloc(from._alloc)
|
||||
{
|
||||
_inc_ref_count();
|
||||
}
|
||||
|
||||
Shared_pointer & operator=(const Shared_pointer& from)
|
||||
{
|
||||
/* check for self assignment */
|
||||
if (_ptr == from._ptr)
|
||||
return *this;
|
||||
|
||||
/* forget about original pointed-to object */
|
||||
_dec_ref_count();
|
||||
|
||||
_ref_counter = from._ref_counter;
|
||||
_ptr = from._ptr;
|
||||
_alloc = from._alloc;
|
||||
|
||||
/* account for newly assigned pointed-to object */
|
||||
_inc_ref_count();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Shared_pointer()
|
||||
{
|
||||
_dec_ref_count();
|
||||
}
|
||||
|
||||
T * operator -> () { return _ptr; }
|
||||
T const* operator -> () const { return _ptr; }
|
||||
|
||||
operator bool () const { return _ptr != 0; }
|
||||
|
||||
bool operator== (const Shared_pointer &other)
|
||||
{
|
||||
return (_ptr == other._ptr);
|
||||
}
|
||||
|
||||
template<typename To>
|
||||
Shared_pointer<To> dynamic_pointer_cast()
|
||||
{
|
||||
return Shared_pointer<To>(dynamic_cast<To *>(_ptr), _alloc);
|
||||
}
|
||||
};
|
||||
template <typename T> class Shared_pointer;
|
||||
}
|
||||
|
||||
|
||||
class Noux::Reference_counter
|
||||
{
|
||||
private:
|
||||
|
||||
Lock _lock;
|
||||
long _value;
|
||||
|
||||
friend class Shared_pointer_base;
|
||||
|
||||
void _inc_ref_count()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
_value++;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return reference counter after decrement
|
||||
*/
|
||||
long _dec_ref_count()
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
return --_value;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Reference_counter() : _value(0) { }
|
||||
};
|
||||
|
||||
|
||||
class Noux::Shared_pointer_base
|
||||
{
|
||||
protected:
|
||||
|
||||
Reference_counter *_ref_counter;
|
||||
|
||||
Shared_pointer_base(Reference_counter *ref_counter)
|
||||
: _ref_counter(ref_counter) { }
|
||||
|
||||
void _inc_ref_count() {
|
||||
if (_ref_counter) _ref_counter->_inc_ref_count(); }
|
||||
|
||||
bool _dec_ref_count() {
|
||||
return _ref_counter && (_ref_counter->_dec_ref_count() == 0); }
|
||||
|
||||
long count() const { return _ref_counter ? _ref_counter->_value : -99; }
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class Noux::Shared_pointer : public Shared_pointer_base
|
||||
{
|
||||
private:
|
||||
|
||||
T *_ptr;
|
||||
Allocator *_alloc;
|
||||
|
||||
void _dec_ref_count()
|
||||
{
|
||||
if (Shared_pointer_base::_dec_ref_count()) {
|
||||
|
||||
destroy(_alloc, _ptr);
|
||||
_ptr = 0;
|
||||
_alloc = 0;
|
||||
_ref_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Shared_pointer() : Shared_pointer_base(0), _ptr(0), _alloc(0) { }
|
||||
|
||||
Shared_pointer(T *ptr, Allocator &alloc)
|
||||
: Shared_pointer_base(ptr), _ptr(ptr), _alloc(&alloc)
|
||||
{
|
||||
_inc_ref_count();
|
||||
}
|
||||
|
||||
Shared_pointer(Shared_pointer const & from)
|
||||
:
|
||||
Shared_pointer_base(from._ref_counter),
|
||||
_ptr(from._ptr), _alloc(from._alloc)
|
||||
{
|
||||
_inc_ref_count();
|
||||
}
|
||||
|
||||
Shared_pointer & operator=(const Shared_pointer& from)
|
||||
{
|
||||
/* check for self assignment */
|
||||
if (_ptr == from._ptr)
|
||||
return *this;
|
||||
|
||||
/* forget about original pointed-to object */
|
||||
_dec_ref_count();
|
||||
|
||||
_ref_counter = from._ref_counter;
|
||||
_ptr = from._ptr;
|
||||
_alloc = from._alloc;
|
||||
|
||||
/* account for newly assigned pointed-to object */
|
||||
_inc_ref_count();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Shared_pointer()
|
||||
{
|
||||
_dec_ref_count();
|
||||
}
|
||||
|
||||
T * operator -> () { return _ptr; }
|
||||
T const* operator -> () const { return _ptr; }
|
||||
|
||||
operator bool () const { return _ptr != 0; }
|
||||
|
||||
bool operator== (const Shared_pointer &other)
|
||||
{
|
||||
return (_ptr == other._ptr);
|
||||
}
|
||||
|
||||
template<typename To>
|
||||
Shared_pointer<To> dynamic_pointer_cast()
|
||||
{
|
||||
return Shared_pointer<To>(dynamic_cast<To *>(_ptr), _alloc);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__SHARED_POINTER_H_ */
|
||||
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* \brief Stdio filesystem
|
||||
* \author Josef Soentgen
|
||||
* \date 2012-08-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 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 _NOUX__STDIO_FILE_SYSTEM_H_
|
||||
#define _NOUX__STDIO_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/log.h>
|
||||
#include <util/string.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "terminal_connection.h"
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Stdio_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
Terminal::Session_client *_terminal;
|
||||
bool _echo;
|
||||
|
||||
public:
|
||||
|
||||
Stdio_file_system(Genode::Env&, Genode::Allocator&,
|
||||
Genode::Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config),
|
||||
_terminal(terminal()),
|
||||
_echo(true)
|
||||
{ }
|
||||
|
||||
static char const *name() { return "stdio"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs::Vfs_handle *, char const *buf,
|
||||
Vfs::file_size buf_size,
|
||||
Vfs::file_size &out_count) override
|
||||
{
|
||||
buf_size = buf_size > 0xFFFFFFFFULL ? ~0UL : buf_size;
|
||||
|
||||
out_count = _terminal->write(buf, buf_size);
|
||||
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs::Vfs_handle *, char *dst,
|
||||
Vfs::file_size count,
|
||||
Vfs::file_size &out_count) override
|
||||
{
|
||||
count = count > 0xFFFFFFFFULL ? ~0UL : count;
|
||||
|
||||
out_count = _terminal->read(dst, count);
|
||||
|
||||
if (_echo)
|
||||
_terminal->write(dst, count);
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs::Vfs_handle *,
|
||||
Vfs::file_size) override
|
||||
{
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
|
||||
Ioctl_result ioctl(Vfs::Vfs_handle *vfs_handle, Ioctl_opcode opcode,
|
||||
Ioctl_arg arg, Ioctl_out &out) override
|
||||
{
|
||||
switch (opcode) {
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAF:
|
||||
{
|
||||
_echo = (arg & (Vfs::File_io_service::IOCTL_VAL_ECHO));
|
||||
return IOCTL_OK;
|
||||
}
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAW:
|
||||
{
|
||||
warning(__func__, ": OP_TIOCSETAW not implemented");
|
||||
return IOCTL_ERR_INVALID;
|
||||
}
|
||||
|
||||
default:
|
||||
warning(__func__, ": invalid ioctl(request=", Hex(opcode), ")");
|
||||
break;
|
||||
}
|
||||
|
||||
return IOCTL_ERR_INVALID;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__STDIO_FILE_SYSTEM_H_ */
|
798
repos/ports/src/noux/syscall.cc
Normal file
798
repos/ports/src/noux/syscall.cc
Normal file
@ -0,0 +1,798 @@
|
||||
/*
|
||||
* \brief Noux syscall dispatcher
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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.
|
||||
*/
|
||||
|
||||
/* Noux includes */
|
||||
#include <child.h>
|
||||
#include <child_env.h>
|
||||
#include <vfs_io_channel.h>
|
||||
#include <pipe_io_channel.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
/**
|
||||
* This function is used to generate inode values from the given
|
||||
* path using the FNV-1a algorithm.
|
||||
*/
|
||||
inline uint32_t hash_path(const char *path, size_t len)
|
||||
{
|
||||
const unsigned char * p = reinterpret_cast<const unsigned char*>(path);
|
||||
uint32_t hash = 2166136261U;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
hash ^= p[i];
|
||||
hash *= 16777619;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
{
|
||||
if (_verbose.syscalls())
|
||||
log("PID ", pid(), " -> SYSCALL ", Noux::Session::syscall_name(sc));
|
||||
|
||||
bool result = false;
|
||||
|
||||
try {
|
||||
switch (sc) {
|
||||
|
||||
case SYSCALL_WRITE:
|
||||
{
|
||||
size_t const count_in = _sysio.write_in.count;
|
||||
|
||||
for (size_t offset = 0; offset != count_in; ) {
|
||||
|
||||
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.write_in.fd);
|
||||
|
||||
if (!io->nonblocking())
|
||||
_block_for_io_channel(io, false, true, false);
|
||||
|
||||
if (io->check_unblock(false, true, false)) {
|
||||
/*
|
||||
* 'io->write' is expected to update
|
||||
* '_sysio.write_out.count' and 'offset'
|
||||
*/
|
||||
result = io->write(_sysio, offset);
|
||||
if (result == false)
|
||||
break;
|
||||
} else {
|
||||
if (result == false) {
|
||||
/* nothing was written yet */
|
||||
_sysio.error.write = Vfs::File_io_service::WRITE_ERR_INTERRUPT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_READ:
|
||||
{
|
||||
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.read_in.fd);
|
||||
|
||||
if (!io->nonblocking())
|
||||
_block_for_io_channel(io, true, false, false);
|
||||
|
||||
if (io->check_unblock(true, false, false))
|
||||
result = io->read(_sysio);
|
||||
else
|
||||
_sysio.error.read = Vfs::File_io_service::READ_ERR_INTERRUPT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_FTRUNCATE:
|
||||
{
|
||||
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.ftruncate_in.fd);
|
||||
|
||||
_block_for_io_channel(io, false, true, false);
|
||||
|
||||
if (io->check_unblock(false, true, false))
|
||||
result = io->ftruncate(_sysio);
|
||||
else
|
||||
_sysio.error.ftruncate = Vfs::File_io_service::FTRUNCATE_ERR_INTERRUPT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_STAT:
|
||||
case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */
|
||||
{
|
||||
/**
|
||||
* We calculate the inode by hashing the path because there is
|
||||
* no inode registry in noux.
|
||||
*/
|
||||
size_t path_len = strlen(_sysio.stat_in.path);
|
||||
uint32_t path_hash = hash_path(_sysio.stat_in.path, path_len);
|
||||
|
||||
Vfs::Directory_service::Stat stat_out;
|
||||
_sysio.error.stat = _root_dir.stat(_sysio.stat_in.path, stat_out);
|
||||
|
||||
result = (_sysio.error.stat == Vfs::Directory_service::STAT_OK);
|
||||
|
||||
/*
|
||||
* Instead of using the uid/gid given by the actual file system
|
||||
* we use the ones specificed in the config.
|
||||
*/
|
||||
if (result) {
|
||||
stat_out.uid = _user_info.uid();
|
||||
stat_out.gid = _user_info.gid();
|
||||
|
||||
stat_out.inode = path_hash;
|
||||
}
|
||||
|
||||
_sysio.stat_out.st = stat_out;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_FSTAT:
|
||||
{
|
||||
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.fstat_in.fd);
|
||||
|
||||
result = io->fstat(_sysio);
|
||||
|
||||
if (result) {
|
||||
Sysio::Path path;
|
||||
|
||||
/**
|
||||
* Only actual fd's are valid fstat targets.
|
||||
*/
|
||||
if (io->path(path, sizeof (path))) {
|
||||
size_t path_len = strlen(path);
|
||||
uint32_t path_hash = hash_path(path, path_len);
|
||||
|
||||
_sysio.stat_out.st.inode = path_hash;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_FCNTL:
|
||||
|
||||
if (_sysio.fcntl_in.cmd == Sysio::FCNTL_CMD_SET_FD_FLAGS) {
|
||||
|
||||
/* we assume that there is only the close-on-execve flag */
|
||||
_lookup_channel(_sysio.fcntl_in.fd)->close_on_execve =
|
||||
!!_sysio.fcntl_in.long_arg;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
result = _lookup_channel(_sysio.fcntl_in.fd)->fcntl(_sysio);
|
||||
break;
|
||||
|
||||
case SYSCALL_OPEN:
|
||||
{
|
||||
Vfs::Vfs_handle *vfs_handle = 0;
|
||||
_sysio.error.open = _root_dir.open(_sysio.open_in.path,
|
||||
_sysio.open_in.mode,
|
||||
&vfs_handle, _heap);
|
||||
if (!vfs_handle)
|
||||
break;
|
||||
|
||||
char const *leaf_path = _root_dir.leaf_path(_sysio.open_in.path);
|
||||
|
||||
/*
|
||||
* File descriptors of opened directories are handled by
|
||||
* '_root_dir'. In this case, we use the absolute path as leaf
|
||||
* path because path operations always refer to the global
|
||||
* root.
|
||||
*/
|
||||
if (&vfs_handle->ds() == &_root_dir)
|
||||
leaf_path = _sysio.open_in.path;
|
||||
|
||||
Shared_pointer<Io_channel>
|
||||
channel(new (_heap) Vfs_io_channel(_sysio.open_in.path,
|
||||
leaf_path, &_root_dir,
|
||||
vfs_handle, _env.ep()),
|
||||
_heap);
|
||||
|
||||
_sysio.open_out.fd = add_io_channel(channel);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_CLOSE:
|
||||
{
|
||||
remove_io_channel(_sysio.close_in.fd);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_IOCTL:
|
||||
|
||||
result = _lookup_channel(_sysio.ioctl_in.fd)->ioctl(_sysio);
|
||||
break;
|
||||
|
||||
case SYSCALL_LSEEK:
|
||||
|
||||
result = _lookup_channel(_sysio.lseek_in.fd)->lseek(_sysio);
|
||||
break;
|
||||
|
||||
case SYSCALL_DIRENT:
|
||||
|
||||
result = _lookup_channel(_sysio.dirent_in.fd)->dirent(_sysio);
|
||||
break;
|
||||
|
||||
case SYSCALL_EXECVE:
|
||||
{
|
||||
/*
|
||||
* We have to check the dataspace twice because the binary
|
||||
* could be a script that uses an interpreter which maybe
|
||||
* does not exist.
|
||||
*/
|
||||
Dataspace_capability binary_ds =
|
||||
_root_dir.dataspace(_sysio.execve_in.filename);
|
||||
|
||||
if (!binary_ds.valid()) {
|
||||
_sysio.error.execve = Sysio::EXECVE_NONEXISTENT;
|
||||
break;
|
||||
}
|
||||
|
||||
Child_env<sizeof(_sysio.execve_in.args)>
|
||||
child_env(_env.rm(),
|
||||
_sysio.execve_in.filename, binary_ds,
|
||||
_sysio.execve_in.args, _sysio.execve_in.env);
|
||||
|
||||
_root_dir.release(_sysio.execve_in.filename, binary_ds);
|
||||
|
||||
binary_ds = _root_dir.dataspace(child_env.binary_name());
|
||||
|
||||
if (!binary_ds.valid()) {
|
||||
_sysio.error.execve = Sysio::EXECVE_NONEXISTENT;
|
||||
break;
|
||||
}
|
||||
|
||||
_root_dir.release(child_env.binary_name(), binary_ds);
|
||||
|
||||
try {
|
||||
_parent_execve.execve_child(*this,
|
||||
child_env.binary_name(),
|
||||
child_env.args(),
|
||||
child_env.env());
|
||||
|
||||
/*
|
||||
* 'return' instead of 'break' to skip possible signal delivery,
|
||||
* which might cause the old child process to exit itself
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
catch (Child::Binary_does_not_exist) {
|
||||
_sysio.error.execve = Sysio::EXECVE_NONEXISTENT; }
|
||||
catch (Child::Insufficient_memory) {
|
||||
_sysio.error.execve = Sysio::EXECVE_NOMEM; }
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_SELECT:
|
||||
{
|
||||
size_t in_fds_total = _sysio.select_in.fds.total_fds();
|
||||
Sysio::Select_fds in_fds;
|
||||
for (Genode::size_t i = 0; i < in_fds_total; i++)
|
||||
in_fds.array[i] = _sysio.select_in.fds.array[i];
|
||||
in_fds.num_rd = _sysio.select_in.fds.num_rd;
|
||||
in_fds.num_wr = _sysio.select_in.fds.num_wr;
|
||||
in_fds.num_ex = _sysio.select_in.fds.num_ex;
|
||||
|
||||
int _rd_array[in_fds_total];
|
||||
int _wr_array[in_fds_total];
|
||||
|
||||
long timeout_sec = _sysio.select_in.timeout.sec;
|
||||
long timeout_usec = _sysio.select_in.timeout.usec;
|
||||
bool timeout_reached = false;
|
||||
|
||||
/* reset the blocker lock to the 'locked' state */
|
||||
_blocker.unlock();
|
||||
_blocker.lock();
|
||||
|
||||
/*
|
||||
* Register ourself at all watched I/O channels
|
||||
*
|
||||
* We instantiate as many notifiers as we have file
|
||||
* descriptors to observe. Each notifier is associated
|
||||
* with the child's blocking semaphore. When any of the
|
||||
* notifiers get woken up, the semaphore gets unblocked.
|
||||
*
|
||||
* XXX However, the blocker may get unblocked for other
|
||||
* conditions such as the destruction of the child.
|
||||
* ...to be done.
|
||||
*/
|
||||
|
||||
Wake_up_notifier notifiers[in_fds_total];
|
||||
|
||||
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
||||
int fd = in_fds.array[i];
|
||||
if (!fd_in_use(fd)) continue;
|
||||
|
||||
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
||||
notifiers[i].lock = &_blocker;
|
||||
|
||||
io->register_wake_up_notifier(¬ifiers[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ourself at the Io_receptor_registry
|
||||
*
|
||||
* Each entry in the registry will be unblocked if an external
|
||||
* event has happend, e.g. network I/O.
|
||||
*/
|
||||
|
||||
Io_receptor receptor(&_blocker);
|
||||
io_receptor_registry()->register_receptor(&receptor);
|
||||
|
||||
|
||||
/*
|
||||
* Block for one action of the watched file descriptors
|
||||
*/
|
||||
for (;;) {
|
||||
|
||||
/*
|
||||
* Check I/O channels of specified file descriptors for
|
||||
* unblock condition. Return if one I/O channel satisfies
|
||||
* the condition.
|
||||
*/
|
||||
size_t unblock_rd = 0;
|
||||
size_t unblock_wr = 0;
|
||||
size_t unblock_ex = 0;
|
||||
|
||||
/* process read fds */
|
||||
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
||||
|
||||
int fd = in_fds.array[i];
|
||||
if (!fd_in_use(fd)) continue;
|
||||
|
||||
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
||||
|
||||
if (in_fds.watch_for_rd(i))
|
||||
if (io->check_unblock(true, false, false)) {
|
||||
_rd_array[unblock_rd++] = fd;
|
||||
}
|
||||
if (in_fds.watch_for_wr(i))
|
||||
if (io->check_unblock(false, true, false)) {
|
||||
_wr_array[unblock_wr++] = fd;
|
||||
}
|
||||
if (in_fds.watch_for_ex(i))
|
||||
if (io->check_unblock(false, false, true)) {
|
||||
unblock_ex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (unblock_rd || unblock_wr || unblock_ex) {
|
||||
/**
|
||||
* Merge the fd arrays in one output array
|
||||
*/
|
||||
for (size_t i = 0; i < unblock_rd; i++) {
|
||||
_sysio.select_out.fds.array[i] = _rd_array[i];
|
||||
}
|
||||
_sysio.select_out.fds.num_rd = unblock_rd;
|
||||
|
||||
/* XXX could use a pointer to select_out.fds.array instead */
|
||||
for (size_t j = unblock_rd, i = 0; i < unblock_wr; i++, j++) {
|
||||
_sysio.select_out.fds.array[j] = _wr_array[i];
|
||||
}
|
||||
_sysio.select_out.fds.num_wr = unblock_wr;
|
||||
|
||||
/* exception fds are currently not considered */
|
||||
_sysio.select_out.fds.num_ex = unblock_ex;
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return if timeout is zero or timeout exceeded
|
||||
*/
|
||||
|
||||
if (_sysio.select_in.timeout.zero() || timeout_reached) {
|
||||
/*
|
||||
if (timeout_reached) log("timeout_reached");
|
||||
else log("timeout.zero()");
|
||||
*/
|
||||
_sysio.select_out.fds.num_rd = 0;
|
||||
_sysio.select_out.fds.num_wr = 0;
|
||||
_sysio.select_out.fds.num_ex = 0;
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return if signals are pending
|
||||
*/
|
||||
|
||||
if (!_pending_signals.empty()) {
|
||||
_sysio.error.select = Sysio::SELECT_ERR_INTERRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Block at barrier except when reaching the timeout
|
||||
*/
|
||||
|
||||
if (!_sysio.select_in.timeout.infinite()) {
|
||||
unsigned long to_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
|
||||
Timeout_state ts;
|
||||
Timeout_alarm ta(ts, _blocker, _timeout_scheduler, to_msec);
|
||||
|
||||
/* block until timeout is reached or we were unblocked */
|
||||
_blocker.lock();
|
||||
|
||||
if (ts.timed_out) {
|
||||
timeout_reached = 1;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* We woke up before reaching the timeout,
|
||||
* so we discard the alarm
|
||||
*/
|
||||
ta.discard();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* let's block infinitely */
|
||||
_blocker.lock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister barrier at watched I/O channels
|
||||
*/
|
||||
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
||||
int fd = in_fds.array[i];
|
||||
if (!fd_in_use(fd)) continue;
|
||||
|
||||
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
||||
io->unregister_wake_up_notifier(¬ifiers[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister receptor
|
||||
*/
|
||||
io_receptor_registry()->unregister_receptor(&receptor);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_FORK:
|
||||
{
|
||||
Genode::addr_t ip = _sysio.fork_in.ip;
|
||||
Genode::addr_t sp = _sysio.fork_in.sp;
|
||||
Genode::addr_t parent_cap_addr = _sysio.fork_in.parent_cap_addr;
|
||||
|
||||
int const new_pid = _pid_allocator.alloc();
|
||||
Child * child = nullptr;
|
||||
|
||||
try {
|
||||
/*
|
||||
* XXX To ease debugging, it would be useful to generate a
|
||||
* unique name that includes the PID instead of just
|
||||
* reusing the name of the parent.
|
||||
*/
|
||||
child = new (_heap) Child(_child_policy.name(),
|
||||
_verbose,
|
||||
_user_info,
|
||||
this,
|
||||
_kill_broadcaster,
|
||||
_timeout_scheduler,
|
||||
*this,
|
||||
_pid_allocator,
|
||||
new_pid,
|
||||
_env,
|
||||
_root_dir,
|
||||
_args,
|
||||
_sysio_env.env(),
|
||||
_heap,
|
||||
_ref_ram, _ref_ram_cap,
|
||||
_parent_services,
|
||||
true,
|
||||
_destruct_queue);
|
||||
} catch (Child::Insufficient_memory) {
|
||||
_sysio.error.fork = Sysio::FORK_NOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
Family_member::insert(child);
|
||||
|
||||
_assign_io_channels_to(child);
|
||||
|
||||
/* copy our address space into the new child */
|
||||
try {
|
||||
_pd.replay(child->ram(), child->pd(), _env.rm(), _heap,
|
||||
child->ds_registry(), _ep);
|
||||
|
||||
/* start executing the main thread of the new process */
|
||||
child->start_forked_main_thread(ip, sp, parent_cap_addr);
|
||||
|
||||
/* activate child entrypoint, thereby starting the new process */
|
||||
child->start();
|
||||
|
||||
_sysio.fork_out.pid = new_pid;
|
||||
|
||||
result = true;
|
||||
}
|
||||
catch (Region_map::Region_conflict) {
|
||||
error("region conflict while replaying the address space"); }
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_GETPID:
|
||||
{
|
||||
_sysio.getpid_out.pid = pid();
|
||||
return true;
|
||||
}
|
||||
|
||||
case SYSCALL_WAIT4:
|
||||
{
|
||||
Family_member *exited = _sysio.wait4_in.nohang ? poll4() : wait4();
|
||||
|
||||
if (exited) {
|
||||
_sysio.wait4_out.pid = exited->pid();
|
||||
_sysio.wait4_out.status = exited->exit_status();
|
||||
Family_member::remove(exited);
|
||||
|
||||
static_cast<Child *>(exited)->submit_exit_signal();
|
||||
|
||||
} else {
|
||||
if (_sysio.wait4_in.nohang) {
|
||||
_sysio.wait4_out.pid = 0;
|
||||
_sysio.wait4_out.status = 0;
|
||||
} else {
|
||||
_sysio.error.wait4 = Sysio::WAIT4_ERR_INTERRUPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_PIPE:
|
||||
{
|
||||
Shared_pointer<Pipe> pipe (new (_heap) Pipe, _heap);
|
||||
Shared_pointer<Io_channel> pipe_sink (new (_heap) Pipe_sink_io_channel (pipe, _env.ep()), _heap);
|
||||
Shared_pointer<Io_channel> pipe_source(new (_heap) Pipe_source_io_channel(pipe, _env.ep()), _heap);
|
||||
|
||||
_sysio.pipe_out.fd[0] = add_io_channel(pipe_source);
|
||||
_sysio.pipe_out.fd[1] = add_io_channel(pipe_sink);
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_DUP2:
|
||||
{
|
||||
int fd = add_io_channel(io_channel_by_fd(_sysio.dup2_in.fd),
|
||||
_sysio.dup2_in.to_fd);
|
||||
|
||||
_sysio.dup2_out.fd = fd;
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_UNLINK:
|
||||
|
||||
_sysio.error.unlink = _root_dir.unlink(_sysio.unlink_in.path);
|
||||
|
||||
result = (_sysio.error.unlink == Vfs::Directory_service::UNLINK_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_READLINK:
|
||||
{
|
||||
Vfs::file_size out_count = 0;
|
||||
|
||||
_sysio.error.readlink =
|
||||
_root_dir.readlink(_sysio.readlink_in.path,
|
||||
_sysio.readlink_out.chunk,
|
||||
min(_sysio.readlink_in.bufsiz,
|
||||
sizeof(_sysio.readlink_out.chunk)),
|
||||
out_count);
|
||||
|
||||
_sysio.readlink_out.count = out_count;
|
||||
|
||||
result = (_sysio.error.readlink == Vfs::Directory_service::READLINK_OK);
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_RENAME:
|
||||
|
||||
_sysio.error.rename = _root_dir.rename(_sysio.rename_in.from_path,
|
||||
_sysio.rename_in.to_path);
|
||||
|
||||
result = (_sysio.error.rename == Vfs::Directory_service::RENAME_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_MKDIR:
|
||||
|
||||
_sysio.error.mkdir = _root_dir.mkdir(_sysio.mkdir_in.path, 0);
|
||||
|
||||
result = (_sysio.error.mkdir == Vfs::Directory_service::MKDIR_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_SYMLINK:
|
||||
|
||||
_sysio.error.symlink = _root_dir.symlink(_sysio.symlink_in.oldpath,
|
||||
_sysio.symlink_in.newpath);
|
||||
|
||||
result = (_sysio.error.symlink == Vfs::Directory_service::SYMLINK_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_USERINFO:
|
||||
{
|
||||
if (_sysio.userinfo_in.request == Sysio::USERINFO_GET_UID
|
||||
|| _sysio.userinfo_in.request == Sysio::USERINFO_GET_GID) {
|
||||
_sysio.userinfo_out.uid = _user_info.uid();
|
||||
_sysio.userinfo_out.gid = _user_info.gid();
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since NOUX supports exactly one user, return false if we
|
||||
* got a unknown uid.
|
||||
*/
|
||||
if (_sysio.userinfo_in.uid != _user_info.uid())
|
||||
break;
|
||||
|
||||
Genode::memcpy(_sysio.userinfo_out.name,
|
||||
_user_info.name().string(),
|
||||
sizeof(User_info::Name));
|
||||
Genode::memcpy(_sysio.userinfo_out.shell,
|
||||
_user_info.shell().string(),
|
||||
sizeof(User_info::Shell));
|
||||
Genode::memcpy(_sysio.userinfo_out.home,
|
||||
_user_info.home().string(),
|
||||
sizeof(User_info::Home));
|
||||
|
||||
_sysio.userinfo_out.uid = _user_info.uid();
|
||||
_sysio.userinfo_out.gid = _user_info.gid();
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_GETTIMEOFDAY:
|
||||
{
|
||||
/**
|
||||
* Since the timeout_scheduler thread is started after noux it
|
||||
* basicly returns the eleapsed time since noux was started. We
|
||||
* abuse this timer to provide a more useful implemenation of
|
||||
* gettimeofday() to make certain programs (e.g. ping(1)) happy.
|
||||
* Note: this is just a short-term solution because Genode currently
|
||||
* lacks a proper time interface (there is a RTC driver however, but
|
||||
* there is no interface for it).
|
||||
*/
|
||||
unsigned long time = _timeout_scheduler.curr_time();
|
||||
|
||||
_sysio.gettimeofday_out.sec = (time / 1000);
|
||||
_sysio.gettimeofday_out.usec = (time % 1000) * 1000;
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_CLOCK_GETTIME:
|
||||
{
|
||||
/**
|
||||
* It's the same procedure as in SYSCALL_GETTIMEOFDAY.
|
||||
*/
|
||||
unsigned long time = _timeout_scheduler.curr_time();
|
||||
|
||||
switch (_sysio.clock_gettime_in.clock_id) {
|
||||
|
||||
/* CLOCK_SECOND is used by time(3) in the libc. */
|
||||
case Sysio::CLOCK_ID_SECOND:
|
||||
{
|
||||
_sysio.clock_gettime_out.sec = (time / 1000);
|
||||
_sysio.clock_gettime_out.nsec = 0;
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
_sysio.clock_gettime_out.sec = 0;
|
||||
_sysio.clock_gettime_out.nsec = 0;
|
||||
_sysio.error.clock = Sysio::CLOCK_ERR_INVALID;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case SYSCALL_UTIMES:
|
||||
{
|
||||
/**
|
||||
* This systemcall is currently not implemented because we lack
|
||||
* the needed mechanisms in most file-systems.
|
||||
*
|
||||
* But we return true anyway to keep certain programs, e.g. make
|
||||
* happy.
|
||||
*/
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_SYNC:
|
||||
{
|
||||
_root_dir.sync("/");
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_KILL:
|
||||
{
|
||||
if (_kill_broadcaster.kill(_sysio.kill_in.pid,
|
||||
_sysio.kill_in.sig))
|
||||
result = true;
|
||||
else
|
||||
_sysio.error.kill = Sysio::KILL_ERR_SRCH;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_GETDTABLESIZE:
|
||||
{
|
||||
_sysio.getdtablesize_out.n =
|
||||
Noux::File_descriptor_registry::MAX_FILE_DESCRIPTORS;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSCALL_SOCKET:
|
||||
case SYSCALL_GETSOCKOPT:
|
||||
case SYSCALL_SETSOCKOPT:
|
||||
case SYSCALL_ACCEPT:
|
||||
case SYSCALL_BIND:
|
||||
case SYSCALL_LISTEN:
|
||||
case SYSCALL_SEND:
|
||||
case SYSCALL_SENDTO:
|
||||
case SYSCALL_RECV:
|
||||
case SYSCALL_RECVFROM:
|
||||
case SYSCALL_GETPEERNAME:
|
||||
case SYSCALL_SHUTDOWN:
|
||||
case SYSCALL_CONNECT:
|
||||
|
||||
result = _syscall_net(sc);
|
||||
break;
|
||||
|
||||
case SYSCALL_INVALID: break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (Invalid_fd) {
|
||||
_sysio.error.general = Vfs::Directory_service::ERR_FD_INVALID;
|
||||
error("invalid file descriptor"); }
|
||||
|
||||
catch (...) { error("unexpected exception"); }
|
||||
|
||||
/* handle signals which might have occured */
|
||||
while (!_pending_signals.empty() &&
|
||||
(_sysio.pending_signals.avail_capacity() > 0)) {
|
||||
_sysio.pending_signals.add(_pending_signals.get());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* \brief Terminal connection
|
||||
* \author Josef Soentgen
|
||||
* \date 2012-08-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 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 _NOUX__TERMINAL_CONNECTION_H_
|
||||
#define _NOUX__TERMINAL_CONNECTION_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/lock.h>
|
||||
#include <terminal_session/connection.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
|
||||
namespace Noux { Terminal::Connection *terminal(); }
|
||||
|
||||
#endif /* _NOUX__TERMINAL_CONNECTION_H_ */
|
@ -24,210 +24,201 @@
|
||||
#include <io_channel.h>
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux {
|
||||
namespace Noux { struct Terminal_io_channel; }
|
||||
|
||||
struct Terminal_io_channel : Io_channel, Signal_dispatcher_base
|
||||
|
||||
struct Noux::Terminal_io_channel : Io_channel
|
||||
{
|
||||
Terminal::Session &_terminal;
|
||||
|
||||
Signal_handler<Terminal_io_channel> _read_avail_handler;
|
||||
|
||||
bool eof = false;
|
||||
|
||||
enum Type { STDIN, STDOUT, STDERR } type;
|
||||
|
||||
Ring_buffer<char, Sysio::CHUNK_SIZE + 1> read_buffer;
|
||||
|
||||
Terminal_io_channel(Terminal::Session &terminal, Type type,
|
||||
Entrypoint &ep)
|
||||
:
|
||||
_terminal(terminal),
|
||||
_read_avail_handler(ep, *this, &Terminal_io_channel::_handle_read_avail),
|
||||
type(type)
|
||||
{
|
||||
Terminal::Session &terminal;
|
||||
Signal_receiver &sig_rec;
|
||||
bool eof;
|
||||
|
||||
enum Type { STDIN, STDOUT, STDERR } type;
|
||||
|
||||
Ring_buffer<char, Sysio::CHUNK_SIZE + 1> read_buffer;
|
||||
|
||||
Terminal_io_channel(Terminal::Session &terminal, Type type,
|
||||
Signal_receiver &sig_rec)
|
||||
: terminal(terminal), sig_rec(sig_rec), eof(false), type(type)
|
||||
{
|
||||
/*
|
||||
* Enable wake up STDIN channel on the presence of new input
|
||||
*
|
||||
* By registering our I/O channel as signal handler, the Noux
|
||||
* main loop will be unblocked on the arrival of new input.
|
||||
* It will check if the received signal belongs to an I/O channel
|
||||
* and invokes the 'handle_signal' function of the I/O channel.
|
||||
*
|
||||
* This gives us the opportunity to handle the unblocking of
|
||||
* blocking system calls such as 'select'.
|
||||
*/
|
||||
if (type == STDIN) {
|
||||
terminal.read_avail_sigh(sig_rec.manage(this));
|
||||
}
|
||||
/*
|
||||
* Enable wake up STDIN channel on the presence of new input
|
||||
*
|
||||
* By registering our I/O channel as signal handler, the Noux
|
||||
* main loop will be unblocked on the arrival of new input.
|
||||
* It will check if the received signal belongs to an I/O channel
|
||||
* and invokes the 'handle_signal' function of the I/O channel.
|
||||
*
|
||||
* This gives us the opportunity to handle the unblocking of
|
||||
* blocking system calls such as 'select'.
|
||||
*/
|
||||
if (type == STDIN) {
|
||||
terminal.read_avail_sigh(_read_avail_handler);
|
||||
}
|
||||
}
|
||||
|
||||
~Terminal_io_channel()
|
||||
{
|
||||
try { sig_rec.dissolve(this); }
|
||||
catch (Genode::Signal_receiver::Context_not_associated) { }
|
||||
}
|
||||
bool write(Sysio &sysio, size_t &offset) override
|
||||
{
|
||||
size_t const count = min(sysio.write_in.count,
|
||||
sizeof(sysio.write_in.chunk));
|
||||
|
||||
bool write(Sysio *sysio, size_t &offset) override
|
||||
{
|
||||
size_t const count = min(sysio->write_in.count,
|
||||
sizeof(sysio->write_in.chunk));
|
||||
_terminal.write(sysio.write_in.chunk, count);
|
||||
|
||||
terminal.write(sysio->write_in.chunk, count);
|
||||
sysio.write_out.count = count;
|
||||
offset = count;
|
||||
|
||||
sysio->write_out.count = count;
|
||||
offset = count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio) override
|
||||
{
|
||||
if (type != STDIN) {
|
||||
error("attempt to read from terminal output channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* deliver EOF observed by the previous 'read' call */
|
||||
if (eof) {
|
||||
sysio->read_out.count = 0;
|
||||
eof = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t const max_count =
|
||||
min(sysio->read_in.count,
|
||||
sizeof(sysio->read_out.chunk));
|
||||
|
||||
for (sysio->read_out.count = 0;
|
||||
(sysio->read_out.count < max_count) && !read_buffer.empty();
|
||||
sysio->read_out.count++) {
|
||||
|
||||
char c = read_buffer.get();
|
||||
|
||||
enum { EOF = 4 };
|
||||
|
||||
if (c == EOF) {
|
||||
|
||||
/*
|
||||
* If EOF was the only character of the batch, the count
|
||||
* has reached zero. In this case the read result indicates
|
||||
* the EOF condition as is. However, if count is greater
|
||||
* than zero, we deliver the previous characters of the
|
||||
* batch and return the zero result from the subsequent
|
||||
* 'read' call. This condition is tracked by the 'eof'
|
||||
* variable.
|
||||
*/
|
||||
if (sysio->read_out.count > 0)
|
||||
eof = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sysio->read_out.chunk[sysio->read_out.count] = c;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio) override
|
||||
{
|
||||
/**
|
||||
* Actually it is "inappropiate" to use fcntl() directly on terminals
|
||||
* (atleast according to the Open Group Specification). We do it anyway
|
||||
* since in our case stdout/in/err is directly connected to the terminal.
|
||||
*
|
||||
* Some GNU programms check if stdout is open by calling fcntl(stdout, F_GETFL, ...).
|
||||
*/
|
||||
switch (sysio->fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
sysio->fcntl_out.result = 0;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio &sysio) override
|
||||
{
|
||||
if (type != STDIN) {
|
||||
error("attempt to read from terminal output channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
/*
|
||||
* Supply stat values such that libc is happy. I.e., the libc
|
||||
* is checking for the file descriptor 1 being a character
|
||||
* device.
|
||||
*/
|
||||
sysio->fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
/* deliver EOF observed by the previous 'read' call */
|
||||
if (eof) {
|
||||
sysio.read_out.count = 0;
|
||||
eof = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
/* never block for writing */
|
||||
if (wr) return true;
|
||||
size_t const max_count =
|
||||
min(sysio.read_in.count,
|
||||
sizeof(sysio.read_out.chunk));
|
||||
|
||||
/*
|
||||
* Unblock I/O channel if the terminal has new user input. Channels
|
||||
* otther than STDIN will never unblock.
|
||||
*/
|
||||
return (rd && (type == STDIN) && !read_buffer.empty());
|
||||
}
|
||||
for (sysio.read_out.count = 0;
|
||||
(sysio.read_out.count < max_count) && !read_buffer.empty();
|
||||
sysio.read_out.count++) {
|
||||
|
||||
bool ioctl(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->ioctl_in.request) {
|
||||
char c = read_buffer.get();
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCGWINSZ:
|
||||
{
|
||||
Terminal::Session::Size size = terminal.size();
|
||||
sysio->ioctl_out.tiocgwinsz.rows = size.lines();
|
||||
sysio->ioctl_out.tiocgwinsz.columns = size.columns();
|
||||
return true;
|
||||
}
|
||||
enum { EOF = 4 };
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAF:
|
||||
{
|
||||
warning(__func__, ": OP_TIOCSETAF not implemented");
|
||||
return false;
|
||||
}
|
||||
if (c == EOF) {
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAW:
|
||||
{
|
||||
warning(__func__, ": OP_TIOCSETAW not implemented");
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* If EOF was the only character of the batch, the count
|
||||
* has reached zero. In this case the read result indicates
|
||||
* the EOF condition as is. However, if count is greater
|
||||
* than zero, we deliver the previous characters of the
|
||||
* batch and return the zero result from the subsequent
|
||||
* 'read' call. This condition is tracked by the 'eof'
|
||||
* variable.
|
||||
*/
|
||||
if (sysio.read_out.count > 0)
|
||||
eof = true;
|
||||
|
||||
default:
|
||||
|
||||
warning("invalid ioctl request ", (int)sysio->ioctl_in.request);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
** Signal_dispatcher_base interface **
|
||||
**************************************/
|
||||
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new STDIN input
|
||||
*/
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
while ((read_buffer.avail_capacity() > 0) &&
|
||||
terminal.avail()) {
|
||||
|
||||
char c;
|
||||
terminal.read(&c, 1);
|
||||
|
||||
enum { INTERRUPT = 3 };
|
||||
|
||||
if (c == INTERRUPT) {
|
||||
Io_channel::invoke_all_interrupt_handlers();
|
||||
} else {
|
||||
read_buffer.add(c);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Io_channel::invoke_all_notifiers();
|
||||
sysio.read_out.chunk[sysio.read_out.count] = c;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio &sysio) override
|
||||
{
|
||||
/**
|
||||
* Actually it is "inappropiate" to use fcntl() directly on terminals
|
||||
* (atleast according to the Open Group Specification). We do it anyway
|
||||
* since in our case stdout/in/err is directly connected to the terminal.
|
||||
*
|
||||
* Some GNU programms check if stdout is open by calling fcntl(stdout, F_GETFL, ...).
|
||||
*/
|
||||
switch (sysio.fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
sysio.fcntl_out.result = 0;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fstat(Sysio &sysio) override
|
||||
{
|
||||
/*
|
||||
* Supply stat values such that libc is happy. I.e., the libc
|
||||
* is checking for the file descriptor 1 being a character
|
||||
* device.
|
||||
*/
|
||||
sysio.fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
/* never block for writing */
|
||||
if (wr) return true;
|
||||
|
||||
/*
|
||||
* Unblock I/O channel if the terminal has new user input. Channels
|
||||
* otther than STDIN will never unblock.
|
||||
*/
|
||||
return (rd && (type == STDIN) && !read_buffer.empty());
|
||||
}
|
||||
|
||||
bool ioctl(Sysio &sysio) override
|
||||
{
|
||||
switch (sysio.ioctl_in.request) {
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCGWINSZ:
|
||||
{
|
||||
Terminal::Session::Size size = _terminal.size();
|
||||
sysio.ioctl_out.tiocgwinsz.rows = size.lines();
|
||||
sysio.ioctl_out.tiocgwinsz.columns = size.columns();
|
||||
return true;
|
||||
}
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAF:
|
||||
{
|
||||
warning(__func__, ": OP_TIOCSETAF not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAW:
|
||||
{
|
||||
warning(__func__, ": OP_TIOCSETAW not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
warning("invalid ioctl request ", (int)sysio.ioctl_in.request);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
void _handle_read_avail()
|
||||
{
|
||||
while ((read_buffer.avail_capacity() > 0) &&
|
||||
_terminal.avail()) {
|
||||
|
||||
char c;
|
||||
_terminal.read(&c, 1);
|
||||
|
||||
enum { INTERRUPT = 3 };
|
||||
|
||||
if (c == INTERRUPT) {
|
||||
Io_channel::invoke_all_interrupt_handlers();
|
||||
} else {
|
||||
read_buffer.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__TERMINAL_IO_CHANNEL_H_ */
|
||||
|
102
repos/ports/src/noux/timeout_scheduler.h
Normal file
102
repos/ports/src/noux/timeout_scheduler.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* \brief Timeout mechanism for 'select'
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 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 _NOUX__TIMEOUT_SCHEDULER_H_
|
||||
#define _NOUX__TIMEOUT_SCHEDULER_H_
|
||||
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/alarm.h>
|
||||
|
||||
namespace Noux {
|
||||
class Timeout_scheduler;
|
||||
class Timeout_state;
|
||||
class Timeout_alarm;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
class Noux::Timeout_scheduler : public Alarm_scheduler
|
||||
{
|
||||
private:
|
||||
|
||||
Timer::Connection _timer;
|
||||
Alarm::Time _curr_time { 0 };
|
||||
|
||||
enum { TIMER_GRANULARITY_MSEC = 10 };
|
||||
|
||||
Signal_handler<Timeout_scheduler> _timer_handler;
|
||||
|
||||
void _handle_timer()
|
||||
{
|
||||
_curr_time = _timer.elapsed_ms();
|
||||
Alarm_scheduler::handle(_curr_time);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Timeout_scheduler(Env &env)
|
||||
:
|
||||
_timer(env),
|
||||
_timer_handler(env.ep(), *this, &Timeout_scheduler::_handle_timer)
|
||||
{
|
||||
_timer.sigh(_timer_handler);
|
||||
_timer.trigger_periodic(TIMER_GRANULARITY_MSEC*1000);
|
||||
}
|
||||
|
||||
Alarm::Time curr_time() const { return _curr_time; }
|
||||
};
|
||||
|
||||
|
||||
struct Noux::Timeout_state
|
||||
{
|
||||
bool timed_out;
|
||||
|
||||
Timeout_state() : timed_out(false) { }
|
||||
};
|
||||
|
||||
|
||||
class Noux::Timeout_alarm : public Alarm
|
||||
{
|
||||
private:
|
||||
|
||||
Timeout_state &_state;
|
||||
Lock &_blocker;
|
||||
Timeout_scheduler &_scheduler;
|
||||
|
||||
public:
|
||||
|
||||
Timeout_alarm(Timeout_state &st, Lock &blocker,
|
||||
Timeout_scheduler &scheduler, Time timeout)
|
||||
:
|
||||
_state(st),
|
||||
_blocker(blocker),
|
||||
_scheduler(scheduler)
|
||||
{
|
||||
_scheduler.schedule_absolute(this, _scheduler.curr_time() + timeout);
|
||||
_state.timed_out = false;
|
||||
}
|
||||
|
||||
void discard() { _scheduler.discard(this); }
|
||||
|
||||
protected:
|
||||
|
||||
bool on_alarm(unsigned) override
|
||||
{
|
||||
_state.timed_out = true;
|
||||
_blocker.unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__TIMEOUT_SCHEDULER_H_ */
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* \brief User information
|
||||
* \author Josef Soentgen
|
||||
* \author Norman Feske
|
||||
* \date 2012-07-23
|
||||
*/
|
||||
|
||||
@ -22,49 +23,54 @@
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class User_info;
|
||||
using namespace Genode;
|
||||
|
||||
struct User_info {
|
||||
|
||||
public:
|
||||
|
||||
char name[Sysio::MAX_USERNAME_LEN];
|
||||
char shell[Sysio::MAX_SHELL_LEN];
|
||||
char home[Sysio::MAX_HOME_LEN];
|
||||
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
|
||||
User_info() : uid(0), gid(0)
|
||||
{
|
||||
strncpy(name, "root", sizeof(name));
|
||||
strncpy(home, "/", sizeof(home));
|
||||
strncpy(shell, "/bin/bash", sizeof(shell));
|
||||
}
|
||||
|
||||
void set_info(Genode::Xml_node user_info_node)
|
||||
{
|
||||
try {
|
||||
user_info_node.attribute("name").value(name, sizeof(name));
|
||||
user_info_node.attribute("uid").value(&uid);
|
||||
user_info_node.attribute("gid").value(&gid);
|
||||
|
||||
for (unsigned i = 0; i < user_info_node.num_sub_nodes(); i++) {
|
||||
Xml_node sub_node = user_info_node.sub_node(i);
|
||||
|
||||
if (sub_node.has_type("shell")) {
|
||||
sub_node.attribute("name").value(shell, sizeof(shell));
|
||||
}
|
||||
|
||||
if (sub_node.has_type("home")) {
|
||||
sub_node.attribute("name").value(home, sizeof(home));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class Noux::User_info : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
typedef String<Sysio::MAX_USERNAME_LEN> Name;
|
||||
typedef String<Sysio::MAX_SHELL_LEN> Shell;
|
||||
typedef String<Sysio::MAX_HOME_LEN> Home;
|
||||
|
||||
private:
|
||||
|
||||
unsigned const _uid;
|
||||
unsigned const _gid;
|
||||
|
||||
Name const _name;
|
||||
Shell const _shell;
|
||||
Home const _home;
|
||||
|
||||
template <typename S>
|
||||
S _sub_node_name(Xml_node node, char const *sub_node, S const &default_name)
|
||||
{
|
||||
if (!node.has_sub_node(sub_node))
|
||||
return default_name;
|
||||
|
||||
return node.sub_node(sub_node).attribute_value("name", default_name);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
User_info(Xml_node node)
|
||||
:
|
||||
_uid (node.attribute_value("uid", 0UL)),
|
||||
_gid (node.attribute_value("gid", 0UL)),
|
||||
_name(node.attribute_value("name", Name("root"))),
|
||||
_shell(_sub_node_name(node, "shell", Shell("/bin/bash"))),
|
||||
_home (_sub_node_name(node, "home", Home("name")))
|
||||
{ }
|
||||
|
||||
unsigned uid() const { return _uid; }
|
||||
unsigned gid() const { return _gid; }
|
||||
|
||||
Name name() const { return _name; }
|
||||
Shell shell() const { return _shell; }
|
||||
Home home() const { return _home; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__USER_INFO_H_ */
|
||||
|
48
repos/ports/src/noux/verbose.h
Normal file
48
repos/ports/src/noux/verbose.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* \brief Noux verbosity
|
||||
* \author Norman Feske
|
||||
* \date 2017-01-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 _NOUX__VERBOSE_H_
|
||||
#define _NOUX__VERBOSE_H_
|
||||
|
||||
#include <util/noncopyable.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
namespace Noux { struct Verbose; }
|
||||
|
||||
|
||||
class Noux::Verbose : Genode::Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
bool const _enabled;
|
||||
bool const _ld;
|
||||
bool const _syscalls;
|
||||
bool const _quota;
|
||||
|
||||
public:
|
||||
|
||||
Verbose(Genode::Xml_node config)
|
||||
:
|
||||
_enabled (config.attribute_value("verbose", false)),
|
||||
_ld (config.attribute_value("ld_verbose", false)),
|
||||
_syscalls(config.attribute_value("verbose_syscalls", false)),
|
||||
_quota (config.attribute_value("verbose_quota", false))
|
||||
{ }
|
||||
|
||||
bool enabled() const { return _enabled; }
|
||||
bool ld() const { return _ld; }
|
||||
bool syscalls() const { return _syscalls; }
|
||||
bool quota() const { return _quota; }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__VERBOSE_H_ */
|
@ -18,212 +18,204 @@
|
||||
#include <io_channel.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
|
||||
namespace Noux {
|
||||
namespace Noux { struct Vfs_io_channel; }
|
||||
|
||||
struct Vfs_io_channel : Io_channel, Signal_dispatcher_base
|
||||
|
||||
struct Noux::Vfs_io_channel : Io_channel
|
||||
{
|
||||
Signal_handler<Vfs_io_channel> _read_avail_handler;
|
||||
|
||||
void _handle_read_avail()
|
||||
{
|
||||
Vfs::Vfs_handle *_fh;
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
|
||||
Absolute_path _path;
|
||||
Absolute_path _leaf_path;
|
||||
Vfs::Vfs_handle *_fh;
|
||||
|
||||
Signal_receiver &_sig_rec;
|
||||
Absolute_path _path;
|
||||
Absolute_path _leaf_path;
|
||||
|
||||
Vfs_io_channel(char const *path, char const *leaf_path,
|
||||
Vfs::Dir_file_system *root_dir, Vfs::Vfs_handle *vfs_handle,
|
||||
Signal_receiver &sig_rec)
|
||||
: _fh(vfs_handle), _path(path), _leaf_path(leaf_path),
|
||||
_sig_rec(sig_rec)
|
||||
{
|
||||
_fh->fs().register_read_ready_sigh(_fh, _sig_rec.manage(this));
|
||||
}
|
||||
Vfs_io_channel(char const *path, char const *leaf_path,
|
||||
Vfs::Dir_file_system *root_dir, Vfs::Vfs_handle *vfs_handle,
|
||||
Entrypoint &ep)
|
||||
:
|
||||
_read_avail_handler(ep, *this, &Vfs_io_channel::_handle_read_avail),
|
||||
_fh(vfs_handle), _path(path), _leaf_path(leaf_path)
|
||||
{
|
||||
_fh->fs().register_read_ready_sigh(_fh, _read_avail_handler);
|
||||
}
|
||||
|
||||
~Vfs_io_channel()
|
||||
{
|
||||
_sig_rec.dissolve(this);
|
||||
_fh->ds().close(_fh);
|
||||
}
|
||||
~Vfs_io_channel()
|
||||
{
|
||||
_fh->ds().close(_fh);
|
||||
}
|
||||
|
||||
bool write(Sysio *sysio, size_t &offset) override
|
||||
{
|
||||
Vfs::file_size out_count = 0;
|
||||
bool write(Sysio &sysio, size_t &offset) override
|
||||
{
|
||||
Vfs::file_size out_count = 0;
|
||||
|
||||
sysio->error.write = _fh->fs().write(_fh, sysio->write_in.chunk,
|
||||
sysio->write_in.count, out_count);
|
||||
if (sysio->error.write != Vfs::File_io_service::WRITE_OK)
|
||||
return false;
|
||||
sysio.error.write = _fh->fs().write(_fh, sysio.write_in.chunk,
|
||||
sysio.write_in.count, out_count);
|
||||
if (sysio.error.write != Vfs::File_io_service::WRITE_OK)
|
||||
return false;
|
||||
|
||||
_fh->advance_seek(out_count);
|
||||
_fh->advance_seek(out_count);
|
||||
|
||||
sysio->write_out.count = out_count;
|
||||
offset = out_count;
|
||||
sysio.write_out.count = out_count;
|
||||
offset = out_count;
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio) override
|
||||
{
|
||||
size_t count = min(sysio->read_in.count, sizeof(sysio->read_out.chunk));
|
||||
bool read(Sysio &sysio) override
|
||||
{
|
||||
size_t count = min(sysio.read_in.count, sizeof(sysio.read_out.chunk));
|
||||
|
||||
Vfs::file_size out_count = 0;
|
||||
Vfs::file_size out_count = 0;
|
||||
|
||||
sysio->error.read = _fh->fs().read(_fh, sysio->read_out.chunk, count, out_count);
|
||||
sysio.error.read = _fh->fs().read(_fh, sysio.read_out.chunk, count, out_count);
|
||||
|
||||
if (sysio->error.read != Vfs::File_io_service::READ_OK)
|
||||
return false;
|
||||
if (sysio.error.read != Vfs::File_io_service::READ_OK)
|
||||
return false;
|
||||
|
||||
sysio->read_out.count = out_count;
|
||||
sysio.read_out.count = out_count;
|
||||
|
||||
_fh->advance_seek(out_count);
|
||||
_fh->advance_seek(out_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
/*
|
||||
* 'sysio.stat_in' is not used in '_fh->ds().stat()',
|
||||
* so no 'sysio' member translation is needed here
|
||||
*/
|
||||
Vfs::Directory_service::Stat stat;
|
||||
sysio->error.stat = _fh->ds().stat(_leaf_path.base(), stat);
|
||||
sysio->fstat_out.st = stat;
|
||||
|
||||
return (sysio->error.stat == Vfs::Directory_service::STAT_OK);
|
||||
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio) override
|
||||
{
|
||||
|
||||
sysio->error.ftruncate = _fh->fs().ftruncate(_fh, sysio->ftruncate_in.length);
|
||||
|
||||
return (sysio->error.ftruncate == Vfs::File_io_service::FTRUNCATE_OK);
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
|
||||
sysio->fcntl_out.result = _fh->status_flags();
|
||||
return true;
|
||||
|
||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS:
|
||||
_fh->status_flags(sysio->fcntl_in.long_arg);
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
PWRN("invalid fcntl command %d", sysio->fcntl_in.cmd);
|
||||
sysio->error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fstat(Sysio &sysio) override
|
||||
{
|
||||
/*
|
||||
* The 'dirent' function for the root directory only (the
|
||||
* 'Dir_file_system::open()' function handles all requests referring
|
||||
* to directories). Hence, '_path' is the absolute path of the
|
||||
* directory to inspect.
|
||||
* 'sysio.stat_in' is not used in '_fh->ds().stat()',
|
||||
* so no 'sysio' member translation is needed here
|
||||
*/
|
||||
bool dirent(Sysio *sysio) override
|
||||
{
|
||||
/*
|
||||
* Return artificial dir entries for "." and ".."
|
||||
*/
|
||||
unsigned const index = _fh->seek() / sizeof(Sysio::Dirent);
|
||||
if (index < 2) {
|
||||
sysio->dirent_out.entry.type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
index ? ".." : ".",
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
Vfs::Directory_service::Stat stat;
|
||||
sysio.error.stat = _fh->ds().stat(_leaf_path.base(), stat);
|
||||
sysio.fstat_out.st = stat;
|
||||
|
||||
sysio->dirent_out.entry.fileno = 1;
|
||||
_fh->advance_seek(sizeof(Sysio::Dirent));
|
||||
return true;
|
||||
}
|
||||
return (sysio.error.stat == Vfs::Directory_service::STAT_OK);
|
||||
|
||||
/*
|
||||
* Delegate remaining dir-entry request to the actual file system.
|
||||
* Align index range to zero when calling the directory service.
|
||||
*/
|
||||
}
|
||||
|
||||
Vfs::Directory_service::Dirent dirent;
|
||||
if (!_fh->ds().dirent(_path.base(), index - 2, dirent))
|
||||
return false;
|
||||
sysio->dirent_out.entry = dirent;
|
||||
bool ftruncate(Sysio &sysio) override
|
||||
{
|
||||
|
||||
sysio.error.ftruncate = _fh->fs().ftruncate(_fh, sysio.ftruncate_in.length);
|
||||
|
||||
return (sysio.error.ftruncate == Vfs::File_io_service::FTRUNCATE_OK);
|
||||
}
|
||||
|
||||
bool fcntl(Sysio &sysio) override
|
||||
{
|
||||
switch (sysio.fcntl_in.cmd) {
|
||||
|
||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
||||
|
||||
sysio.fcntl_out.result = _fh->status_flags();
|
||||
return true;
|
||||
|
||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS:
|
||||
_fh->status_flags(sysio.fcntl_in.long_arg);
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
PWRN("invalid fcntl command %d", sysio.fcntl_in.cmd);
|
||||
sysio.error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'dirent' function for the root directory only (the
|
||||
* 'Dir_file_system::open()' function handles all requests referring
|
||||
* to directories). Hence, '_path' is the absolute path of the
|
||||
* directory to inspect.
|
||||
*/
|
||||
bool dirent(Sysio &sysio) override
|
||||
{
|
||||
/*
|
||||
* Return artificial dir entries for "." and ".."
|
||||
*/
|
||||
unsigned const index = _fh->seek() / sizeof(Sysio::Dirent);
|
||||
if (index < 2) {
|
||||
sysio.dirent_out.entry.type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY;
|
||||
strncpy(sysio.dirent_out.entry.name,
|
||||
index ? ".." : ".",
|
||||
sizeof(sysio.dirent_out.entry.name));
|
||||
|
||||
sysio.dirent_out.entry.fileno = 1;
|
||||
_fh->advance_seek(sizeof(Sysio::Dirent));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of file that the I/O channel refers to
|
||||
*
|
||||
* Note that this function overwrites the 'sysio' argument. Do not
|
||||
* call it prior saving all input arguments from the original sysio
|
||||
* structure.
|
||||
/*
|
||||
* Delegate remaining dir-entry request to the actual file system.
|
||||
* Align index range to zero when calling the directory service.
|
||||
*/
|
||||
size_t size(Sysio *sysio)
|
||||
{
|
||||
if (fstat(sysio))
|
||||
return sysio->fstat_out.st.size;
|
||||
|
||||
return 0;
|
||||
Vfs::Directory_service::Dirent dirent;
|
||||
if (!_fh->ds().dirent(_path.base(), index - 2, dirent))
|
||||
return false;
|
||||
sysio.dirent_out.entry = dirent;
|
||||
|
||||
_fh->advance_seek(sizeof(Sysio::Dirent));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of file that the I/O channel refers to
|
||||
*
|
||||
* Note that this function overwrites the 'sysio' argument. Do not
|
||||
* call it prior saving all input arguments from the original sysio
|
||||
* structure.
|
||||
*/
|
||||
size_t size(Sysio &sysio)
|
||||
{
|
||||
if (fstat(sysio))
|
||||
return sysio.fstat_out.st.size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ioctl(Sysio &sysio) override
|
||||
{
|
||||
Vfs::File_system::Ioctl_arg arg = (Vfs::File_system::Ioctl_arg)sysio.ioctl_in.argp;
|
||||
|
||||
sysio.error.ioctl = _fh->fs().ioctl(_fh, sysio.ioctl_in.request, arg, sysio.ioctl_out);
|
||||
|
||||
return (sysio.error.ioctl == Vfs::File_io_service::IOCTL_OK);
|
||||
}
|
||||
|
||||
bool lseek(Sysio &sysio) override
|
||||
{
|
||||
switch (sysio.lseek_in.whence) {
|
||||
case Sysio::LSEEK_SET: _fh->seek(sysio.lseek_in.offset); break;
|
||||
case Sysio::LSEEK_CUR: _fh->advance_seek(sysio.lseek_in.offset); break;
|
||||
case Sysio::LSEEK_END:
|
||||
off_t offset = sysio.lseek_in.offset;
|
||||
sysio.fstat_in.fd = sysio.lseek_in.fd;
|
||||
_fh->seek(size(sysio) + offset);
|
||||
break;
|
||||
}
|
||||
sysio.lseek_out.offset = _fh->seek();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ioctl(Sysio *sysio) override
|
||||
{
|
||||
Vfs::File_system::Ioctl_arg arg = (Vfs::File_system::Ioctl_arg)sysio->ioctl_in.argp;
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
return _fh->fs().check_unblock(_fh, rd, wr, ex);
|
||||
}
|
||||
|
||||
sysio->error.ioctl = _fh->fs().ioctl(_fh, sysio->ioctl_in.request, arg, sysio->ioctl_out);
|
||||
bool path(char *path, size_t len) override
|
||||
{
|
||||
strncpy(path, _path.base(), len);
|
||||
path[len - 1] = '\0';
|
||||
|
||||
return (sysio->error.ioctl == Vfs::File_io_service::IOCTL_OK);
|
||||
}
|
||||
|
||||
bool lseek(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->lseek_in.whence) {
|
||||
case Sysio::LSEEK_SET: _fh->seek(sysio->lseek_in.offset); break;
|
||||
case Sysio::LSEEK_CUR: _fh->advance_seek(sysio->lseek_in.offset); break;
|
||||
case Sysio::LSEEK_END:
|
||||
off_t offset = sysio->lseek_in.offset;
|
||||
sysio->fstat_in.fd = sysio->lseek_in.fd;
|
||||
_fh->seek(size(sysio) + offset);
|
||||
break;
|
||||
}
|
||||
sysio->lseek_out.offset = _fh->seek();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
return _fh->fs().check_unblock(_fh, rd, wr, ex);
|
||||
}
|
||||
|
||||
bool path(char *path, size_t len) override
|
||||
{
|
||||
strncpy(path, _path.base(), len);
|
||||
path[len - 1] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
** Signal_dispatcher_base interface **
|
||||
**************************************/
|
||||
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new input
|
||||
*/
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _NOUX__VFS_IO_CHANNEL_H_ */
|
||||
|
@ -18,21 +18,16 @@
|
||||
#include <util/list.h>
|
||||
#include <base/lock.h>
|
||||
|
||||
namespace Noux {
|
||||
namespace Noux { struct Wake_up_notifier; }
|
||||
|
||||
struct Wake_up_notifier : List<Wake_up_notifier>::Element
|
||||
{
|
||||
Lock *lock;
|
||||
|
||||
Wake_up_notifier(Lock *lock = 0)
|
||||
: lock(lock) { }
|
||||
struct Noux::Wake_up_notifier : List<Wake_up_notifier>::Element
|
||||
{
|
||||
Lock *lock;
|
||||
|
||||
void wake_up()
|
||||
{
|
||||
if (lock)
|
||||
lock->unlock();
|
||||
}
|
||||
};
|
||||
Wake_up_notifier(Lock *lock = nullptr) : lock(lock) { }
|
||||
|
||||
void wake_up() { if (lock) lock->unlock(); }
|
||||
};
|
||||
|
||||
#endif /* _NOUX__WAKE_UP_NOTIFIER__H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user