Noux: add basic 'Ctrl-C' support

This patch implements the POSIX signal functionality needed to interrupt a
running Noux GDB by pressing 'Ctrl-C'.

It allows to register a signal handler for the 'SIGINT' signal, which
gets executed after 'Ctrl-C' is received from the terminal. With the
current state of the implementation, the signal handler only gets executed
when the Noux application calls a 'read()', 'write()', 'ftruncate()' or
'select()' syscall.

Fixes #923.
This commit is contained in:
Christian Prochaska 2013-10-17 12:48:45 +02:00 committed by Christian Helmuth
parent cf040e2833
commit 047d851fb6
17 changed files with 710 additions and 202 deletions

View File

@ -19,6 +19,7 @@
#define _INCLUDE__NOUX_SESSION__SYSIO_H_
/* Genode includes */
#include <os/ring_buffer.h>
#include <util/misc_math.h>
@ -33,16 +34,14 @@ namespace Noux {
struct Sysio
{
/*
* Information about pending signals
*/
enum { SIG_MAX = 32 };
bool sig_mask[SIG_MAX];
/* signal numbers must match with libc signal numbers */
enum Signal {
SIG_INT = 2,
};
/*
* Number of pending signals
*/
int sig_cnt;
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];
@ -295,10 +294,12 @@ namespace Noux {
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS };
enum Ftruncate_error { FTRUNCATE_ERR_NO_PERM = NUM_GENERAL_ERRORS };
enum Ftruncate_error { FTRUNCATE_ERR_NO_PERM = NUM_GENERAL_ERRORS,
FTRUNCATE_ERR_INTERRUPT };
enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM,
OPEN_ERR_EXISTS };
enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS };
enum Select_error { SELECT_ERR_INTERRUPT };
enum Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM };
enum Readlink_error { READLINK_ERR_NO_ENTRY };
enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
@ -310,10 +311,12 @@ namespace Noux {
SYMLINK_ERR_NAME_TOO_LONG};
enum Read_error { READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK,
READ_ERR_INVALID, READ_ERR_IO };
READ_ERR_INVALID, READ_ERR_IO,
READ_ERR_INTERRUPT };
enum Write_error { WRITE_ERR_AGAIN, WRITE_ERR_WOULD_BLOCK,
WRITE_ERR_INVALID, WRITE_ERR_IO };
WRITE_ERR_INVALID, WRITE_ERR_IO,
WRITE_ERR_INTERRUPT };
enum Ioctl_error { IOCTL_ERR_INVALID, IOCTL_ERR_NOTTY };
@ -361,6 +364,7 @@ namespace Noux {
Ftruncate_error ftruncate;
Open_error open;
Execve_error execve;
Select_error select;
Unlink_error unlink;
Readlink_error readlink;
Rename_error rename;

103
ports/run/noux_signals.run Normal file
View File

@ -0,0 +1,103 @@
set build_components {
core init drivers/timer drivers/uart
noux/minimal
test/noux_signals
}
build $build_components
# create tar archive
exec tar cfv bin/noux_signals.tar -h -C bin test-noux_signals
create_boot_directory
append config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="LOG"/>
<service name="CAP"/>
<service name="RAM"/>
<service name="RM"/>
<service name="CPU"/>
<service name="PD"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <any-child/> <parent/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="uart_drv">
<resource name="RAM" quantum="2M"/>
<provides><service name="Terminal"/></provides>
<config>
<policy label="noux" uart="1"/>
</config>
</start>
<start name="noux">
<resource name="RAM" quantum="1G"/>
<config verbose="yes">
<fstab> <tar name="noux_signals.tar" /> </fstab>
<start name="test-noux_signals"> </start>
</config>
</start>
</config>
}
install_config $config
set boot_modules {
core init timer ld.lib.so noux libc.lib.so
uart_drv libc_noux.lib.so noux_signals.tar
}
build_boot_image $boot_modules
#
# Redirect the LOG session output of Noux into a file
#
set noux_output_file "noux_signals.log"
append qemu_args " -nographic"
append qemu_args " -serial file:$noux_output_file"
append qemu_args " -serial mon:stdio"
run_genode_until "test ready" 10
# send Ctrl-C
send \003
run_genode_until "test finished" 10 $spawn_id
set error_occured 0
if {![regexp {0: signal handler for signal 2 called} $output]} {
set error_occured 1
}
if {![regexp {1: signal handler for signal 2 called} $output]} {
set error_occured 1
}
if {![regexp {0: 'read\(\)' returned with error EINTR} $output]} {
set error_occured 1
}
if {![regexp {1: 'read\(\)' returned with error EINTR} $output]} {
set error_occured 1
}
if { $error_occured == 1 } {
puts "output not as expected"
exit -1
}
exec rm bin/noux_signals.tar
exec rm $noux_output_file

View File

@ -45,6 +45,7 @@
#include <libc_mem_alloc.h>
enum { verbose = false };
enum { verbose_signals = false };
void *operator new (size_t, void *ptr) { return ptr; }
@ -89,6 +90,34 @@ Noux::Session *noux() { return noux_connection()->session(); }
Noux::Sysio *sysio() { return noux_connection()->sysio(); }
/* Array of signal handlers */
static struct sigaction signal_action[SIGRTMAX+1];
static bool noux_syscall(Noux::Session::Syscall opcode)
{
bool ret = noux()->syscall(opcode);
/* handle signals */
while (!sysio()->pending_signals.empty()) {
Noux::Sysio::Signal signal = sysio()->pending_signals.get();
if (signal_action[signal].sa_flags & SA_SIGINFO) {
/* TODO: pass siginfo_t struct */
signal_action[signal].sa_sigaction(signal, 0, 0);
} else {
if (signal_action[signal].sa_handler == SIG_DFL) {
/* do nothing */
} else if (signal_action[signal].sa_handler == SIG_IGN) {
/* do nothing */
} else
signal_action[signal].sa_handler(signal);
}
}
return ret;
}
enum { FS_BLOCK_SIZE = 1024 };
@ -124,7 +153,7 @@ extern "C" struct passwd *getpwuid(uid_t uid)
sysio()->userinfo_in.uid = uid;
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_ALL;
if (!noux()->syscall(Noux::Session::SYSCALL_USERINFO)) {
if (!noux_syscall(Noux::Session::SYSCALL_USERINFO)) {
return (struct passwd *)0;
}
@ -144,7 +173,7 @@ extern "C" uid_t getgid()
{
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_GID;
if (!noux()->syscall(Noux::Session::SYSCALL_USERINFO))
if (!noux_syscall(Noux::Session::SYSCALL_USERINFO))
return 0;
uid_t gid = sysio()->userinfo_out.gid;
@ -162,7 +191,7 @@ extern "C" uid_t getuid()
{
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_UID;
if (!noux()->syscall(Noux::Session::SYSCALL_USERINFO))
if (!noux_syscall(Noux::Session::SYSCALL_USERINFO))
return 0;
uid_t uid = sysio()->userinfo_out.uid;
@ -364,11 +393,10 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
/*
* Perform syscall
*/
if (!noux()->syscall(Noux::Session::SYSCALL_SELECT)) {
PWRN("select syscall failed");
// switch (sysio()->error.select) {
// case Noux::Sysio::SELECT_NONEXISTENT: errno = ENOENT; break;
// }
if (!noux_syscall(Noux::Session::SYSCALL_SELECT)) {
switch (sysio()->error.select) {
case Noux::Sysio::SELECT_ERR_INTERRUPT: errno = EINTR; break;
}
return -1;
}
@ -444,7 +472,7 @@ extern "C" pid_t fork(void)
sysio()->fork_in.sp = (Genode::addr_t)(&stack[STACK_SIZE]);
sysio()->fork_in.parent_cap_addr = (Genode::addr_t)(&new_parent);
if (!noux()->syscall(Noux::Session::SYSCALL_FORK)) {
if (!noux_syscall(Noux::Session::SYSCALL_FORK)) {
PERR("fork error %d", sysio()->error.general);
}
@ -458,7 +486,7 @@ extern "C" pid_t vfork(void) { return fork(); }
extern "C" pid_t getpid(void)
{
noux()->syscall(Noux::Session::SYSCALL_GETPID);
noux_syscall(Noux::Session::SYSCALL_GETPID);
return sysio()->getpid_out.pid;
}
@ -493,7 +521,7 @@ extern "C" pid_t _wait4(pid_t pid, int *status, int options,
{
sysio()->wait4_in.pid = pid;
sysio()->wait4_in.nohang = !!(options & WNOHANG);
if (!noux()->syscall(Noux::Session::SYSCALL_WAIT4)) {
if (!noux_syscall(Noux::Session::SYSCALL_WAIT4)) {
PERR("wait4 error %d", sysio()->error.general);
return -1;
}
@ -545,7 +573,7 @@ extern "C" int clock_gettime(clockid_t clk_id, struct timespec *tp)
return -1;
}
if (!noux()->syscall(Noux::Session::SYSCALL_CLOCK_GETTIME)) {
if (!noux_syscall(Noux::Session::SYSCALL_CLOCK_GETTIME)) {
switch (sysio()->error.clock) {
case Noux::Sysio::CLOCK_ERR_INVALID: errno = EINVAL; break;
default: errno = 0; break;
@ -563,7 +591,7 @@ extern "C" int clock_gettime(clockid_t clk_id, struct timespec *tp)
extern "C" int gettimeofday(struct timeval *tv, struct timezone *tz)
{
if (!noux()->syscall(Noux::Session::SYSCALL_GETTIMEOFDAY)) {
if (!noux_syscall(Noux::Session::SYSCALL_GETTIMEOFDAY)) {
errno = EINVAL;
return -1;
}
@ -577,7 +605,7 @@ extern "C" int gettimeofday(struct timeval *tv, struct timezone *tz)
extern "C" int utimes(const char* path, const struct timeval *times)
{
if (!noux()->syscall(Noux::Session::SYSCALL_UTIMES)) {
if (!noux_syscall(Noux::Session::SYSCALL_UTIMES)) {
errno = EINVAL;
return -1;
}
@ -604,20 +632,30 @@ extern "C" int _sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
}
extern "C" int _sigaction(int, const struct sigaction *, struct sigaction *)
extern "C" int _sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
{
/* XXX todo */
errno = ENOSYS;
return -1;
if (verbose_signals)
PDBG("signum = %d, handler = %p", signum, act ? act->sa_handler : 0);
if ((signum < 0) || (signum > SIGRTMAX)) {
errno = EINVAL;
return -1;
}
if (oldact)
*oldact = signal_action[signum];
if (act)
signal_action[signum] = *act;
return 0;
}
extern "C" int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact)
{
/* XXX todo */
errno = ENOSYS;
return -1;
return _sigaction(signum, act, oldact);
}
@ -771,7 +809,7 @@ namespace {
return -1;
}
if (!noux()->syscall(Noux::Session::SYSCALL_EXECVE)) {
if (!noux_syscall(Noux::Session::SYSCALL_EXECVE)) {
PWRN("exec syscall failed for path \"%s\"", filename);
switch (sysio()->error.execve) {
case Noux::Sysio::EXECVE_NONEXISTENT: errno = ENOENT; break;
@ -800,7 +838,7 @@ namespace {
Genode::strncpy(sysio()->stat_in.path, path, sizeof(sysio()->stat_in.path));
if (!noux()->syscall(Noux::Session::SYSCALL_STAT)) {
if (!noux_syscall(Noux::Session::SYSCALL_STAT)) {
if (verbose)
PWRN("stat syscall failed for path \"%s\"", path);
switch (sysio()->error.stat) {
@ -826,7 +864,7 @@ namespace {
while (!opened) {
Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path));
sysio()->open_in.mode = flags;
if (noux()->syscall(Noux::Session::SYSCALL_OPEN))
if (noux_syscall(Noux::Session::SYSCALL_OPEN))
opened = true;
else
switch (sysio()->error.open) {
@ -838,7 +876,7 @@ namespace {
/* O_CREAT is set, so try to create the file */
Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path));
sysio()->open_in.mode = flags | O_EXCL;
if (noux()->syscall(Noux::Session::SYSCALL_OPEN))
if (noux_syscall(Noux::Session::SYSCALL_OPEN))
opened = true;
else
switch (sysio()->error.open) {
@ -878,7 +916,7 @@ namespace {
Genode::strncpy(sysio()->symlink_in.oldpath, oldpath, sizeof(sysio()->symlink_in.oldpath));
Genode::strncpy(sysio()->symlink_in.newpath, newpath, sizeof(sysio()->symlink_in.newpath));
if (!noux()->syscall(Noux::Session::SYSCALL_SYMLINK)) {
if (!noux_syscall(Noux::Session::SYSCALL_SYMLINK)) {
PERR("symlink error");
/* XXX set errno */
return -1;
@ -910,12 +948,13 @@ namespace {
sysio()->write_in.count = curr_count;
Genode::memcpy(sysio()->write_in.chunk, src, curr_count);
if (!noux()->syscall(Noux::Session::SYSCALL_WRITE)) {
if (!noux_syscall(Noux::Session::SYSCALL_WRITE)) {
switch (sysio()->error.write) {
case Noux::Sysio::WRITE_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::WRITE_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::WRITE_ERR_IO: errno = EIO; break;
case Noux::Sysio::WRITE_ERR_INTERRUPT: errno = EINTR; break;
default:
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
errno = EBADF;
@ -944,12 +983,14 @@ namespace {
sysio()->read_in.fd = noux_fd(fd->context);
sysio()->read_in.count = curr_count;
if (!noux()->syscall(Noux::Session::SYSCALL_READ)) {
if (!noux_syscall(Noux::Session::SYSCALL_READ)) {
switch (sysio()->error.read) {
case Noux::Sysio::READ_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
case Noux::Sysio::READ_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::READ_ERR_IO: errno = EIO; break;
case Noux::Sysio::READ_ERR_INTERRUPT: errno = EINTR; break;
default:
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
errno = EBADF;
@ -982,7 +1023,7 @@ namespace {
int Plugin::close(Libc::File_descriptor *fd)
{
sysio()->close_in.fd = noux_fd(fd->context);
if (!noux()->syscall(Noux::Session::SYSCALL_CLOSE)) {
if (!noux_syscall(Noux::Session::SYSCALL_CLOSE)) {
PERR("close error");
/* XXX set errno */
return -1;
@ -1080,7 +1121,7 @@ namespace {
}
/* perform syscall */
if (!noux()->syscall(Noux::Session::SYSCALL_IOCTL)) {
if (!noux_syscall(Noux::Session::SYSCALL_IOCTL)) {
switch (sysio()->error.ioctl) {
case Noux::Sysio::IOCTL_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::IOCTL_ERR_NOTTY: errno = ENOTTY; break;
@ -1118,7 +1159,7 @@ namespace {
int Plugin::pipe(Libc::File_descriptor *pipefd[2])
{
/* perform syscall */
if (!noux()->syscall(Noux::Session::SYSCALL_PIPE)) {
if (!noux_syscall(Noux::Session::SYSCALL_PIPE)) {
PERR("pipe error");
/* XXX set errno */
return -1;
@ -1137,7 +1178,7 @@ namespace {
sysio()->dup2_in.fd = noux_fd(fd->context);
sysio()->dup2_in.to_fd = -1;
if (!noux()->syscall(Noux::Session::SYSCALL_DUP2)) {
if (!noux_syscall(Noux::Session::SYSCALL_DUP2)) {
PERR("dup error");
/* XXX set errno */
return 0;
@ -1160,7 +1201,7 @@ namespace {
sysio()->dup2_in.to_fd = noux_fd(new_fd->context);
/* perform syscall */
if (!noux()->syscall(Noux::Session::SYSCALL_DUP2)) {
if (!noux_syscall(Noux::Session::SYSCALL_DUP2)) {
PERR("dup2 error");
/* XXX set errno */
return -1;
@ -1173,7 +1214,7 @@ namespace {
int Plugin::fstat(Libc::File_descriptor *fd, struct stat *buf)
{
sysio()->fstat_in.fd = noux_fd(fd->context);
if (!noux()->syscall(Noux::Session::SYSCALL_FSTAT)) {
if (!noux_syscall(Noux::Session::SYSCALL_FSTAT)) {
PERR("fstat error");
/* XXX set errno */
return -1;
@ -1196,9 +1237,10 @@ namespace {
{
sysio()->ftruncate_in.fd = noux_fd(fd->context);
sysio()->ftruncate_in.length = length;
if (!noux()->syscall(Noux::Session::SYSCALL_FTRUNCATE)) {
if (!noux_syscall(Noux::Session::SYSCALL_FTRUNCATE)) {
switch (sysio()->error.ftruncate) {
case Noux::Sysio::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break;
case Noux::Sysio::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; break;
}
return -1;
}
@ -1271,10 +1313,16 @@ namespace {
};
/* invoke system call */
if (!noux()->syscall(Noux::Session::SYSCALL_FCNTL)) {
if (!noux_syscall(Noux::Session::SYSCALL_FCNTL)) {
PWRN("fcntl failed (libc_fd= %d, cmd=%x)", fd->libc_fd, cmd);
/* XXX read error code from sysio */
errno = EINVAL;
switch (sysio()->error.fcntl) {
case Noux::Sysio::FCNTL_ERR_CMD_INVALID: errno = EINVAL; break;
default:
switch (sysio()->error.general) {
case Noux::Sysio::ERR_FD_INVALID: errno = EINVAL; break;
case Noux::Sysio::NUM_GENERAL_ERRORS: break;
}
}
return -1;
}
@ -1296,7 +1344,7 @@ namespace {
struct dirent *dirent = (struct dirent *)buf;
Genode::memset(dirent, 0, sizeof(struct dirent));
if (!noux()->syscall(Noux::Session::SYSCALL_DIRENT)) {
if (!noux_syscall(Noux::Session::SYSCALL_DIRENT)) {
switch (sysio()->error.general) {
case Noux::Sysio::ERR_FD_INVALID:
@ -1343,7 +1391,7 @@ namespace {
case SEEK_END: sysio()->lseek_in.whence = Noux::Sysio::LSEEK_END; break;
}
if (!noux()->syscall(Noux::Session::SYSCALL_LSEEK)) {
if (!noux_syscall(Noux::Session::SYSCALL_LSEEK)) {
switch (sysio()->error.general) {
case Noux::Sysio::ERR_FD_INVALID:
@ -1363,7 +1411,7 @@ namespace {
{
Genode::strncpy(sysio()->unlink_in.path, path, sizeof(sysio()->unlink_in.path));
if (!noux()->syscall(Noux::Session::SYSCALL_UNLINK)) {
if (!noux_syscall(Noux::Session::SYSCALL_UNLINK)) {
PWRN("unlink syscall failed for path \"%s\"", path);
switch (sysio()->error.unlink) {
case Noux::Sysio::UNLINK_ERR_NO_ENTRY: errno = ENOENT; break;
@ -1384,7 +1432,7 @@ namespace {
Genode::strncpy(sysio()->readlink_in.path, path, sizeof(sysio()->readlink_in.path));
sysio()->readlink_in.bufsiz = bufsiz;
if (!noux()->syscall(Noux::Session::SYSCALL_READLINK)) {
if (!noux_syscall(Noux::Session::SYSCALL_READLINK)) {
PWRN("readlink syscall failed for \"%s\"", path);
/* XXX set errno */
return -1;
@ -1406,7 +1454,7 @@ namespace {
Genode::strncpy(sysio()->rename_in.from_path, from_path, sizeof(sysio()->rename_in.from_path));
Genode::strncpy(sysio()->rename_in.to_path, to_path, sizeof(sysio()->rename_in.to_path));
if (!noux()->syscall(Noux::Session::SYSCALL_RENAME)) {
if (!noux_syscall(Noux::Session::SYSCALL_RENAME)) {
PWRN("rename syscall failed for \"%s\" -> \"%s\"", from_path, to_path);
switch (sysio()->error.rename) {
case Noux::Sysio::RENAME_ERR_NO_ENTRY: errno = ENOENT; break;
@ -1425,7 +1473,7 @@ namespace {
{
Genode::strncpy(sysio()->mkdir_in.path, path, sizeof(sysio()->mkdir_in.path));
if (!noux()->syscall(Noux::Session::SYSCALL_MKDIR)) {
if (!noux_syscall(Noux::Session::SYSCALL_MKDIR)) {
PWRN("mkdir syscall failed for \"%s\" mode=0x%x", path, (int)mode);
switch (sysio()->error.mkdir) {
case Noux::Sysio::MKDIR_ERR_EXISTS: errno = EEXIST; break;
@ -1486,7 +1534,7 @@ namespace {
sysio()->socket_in.type = type;
sysio()->socket_in.protocol = protocol;
if (!noux()->syscall(Noux::Session::SYSCALL_SOCKET))
if (!noux_syscall(Noux::Session::SYSCALL_SOCKET))
return 0;
Libc::Plugin_context *context = noux_context(sysio()->socket_out.fd);
@ -1507,7 +1555,7 @@ namespace {
Genode::memset(sysio()->getsockopt_in.optval, 0,
sizeof (sysio()->getsockopt_in.optval));
if (!noux()->syscall(Noux::Session::SYSCALL_GETSOCKOPT))
if (!noux_syscall(Noux::Session::SYSCALL_GETSOCKOPT))
return -1;
Genode::memcpy(optval, sysio()->setsockopt_in.optval,
@ -1532,7 +1580,7 @@ namespace {
Genode::memcpy(sysio()->setsockopt_in.optval, optval, optlen);
if (!noux()->syscall(Noux::Session::SYSCALL_SETSOCKOPT)) {
if (!noux_syscall(Noux::Session::SYSCALL_SETSOCKOPT)) {
/* XXX */
return -1;
}
@ -1555,7 +1603,7 @@ namespace {
sysio()->accept_in.addrlen = 0;
}
if (!noux()->syscall(Noux::Session::SYSCALL_ACCEPT)) {
if (!noux_syscall(Noux::Session::SYSCALL_ACCEPT)) {
switch (sysio()->error.accept) {
case Noux::Sysio::ACCEPT_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::ACCEPT_ERR_NO_MEMORY: errno = ENOMEM; break;
@ -1585,7 +1633,7 @@ namespace {
Genode::memcpy(&sysio()->bind_in.addr, addr, sizeof (struct sockaddr));
sysio()->bind_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_BIND)) {
if (!noux_syscall(Noux::Session::SYSCALL_BIND)) {
switch (sysio()->error.bind) {
case Noux::Sysio::BIND_ERR_ACCESS: errno = EACCES; break;
case Noux::Sysio::BIND_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
@ -1608,7 +1656,7 @@ namespace {
Genode::memcpy(&sysio()->connect_in.addr, addr, sizeof (struct sockaddr));
sysio()->connect_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_CONNECT)) {
if (!noux_syscall(Noux::Session::SYSCALL_CONNECT)) {
switch (sysio()->error.connect) {
case Noux::Sysio::CONNECT_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::CONNECT_ERR_ALREADY: errno = EALREADY; break;
@ -1630,7 +1678,7 @@ namespace {
sysio()->getpeername_in.fd = noux_fd(fd->context);
sysio()->getpeername_in.addrlen = *addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_GETPEERNAME)) {
if (!noux_syscall(Noux::Session::SYSCALL_GETPEERNAME)) {
/* errno */
return -1;
}
@ -1648,7 +1696,7 @@ namespace {
sysio()->listen_in.fd = noux_fd(fd->context);
sysio()->listen_in.backlog = backlog;
if (!noux()->syscall(Noux::Session::SYSCALL_LISTEN)) {
if (!noux_syscall(Noux::Session::SYSCALL_LISTEN)) {
switch (sysio()->error.listen) {
case Noux::Sysio::LISTEN_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
case Noux::Sysio::LISTEN_ERR_NOT_SUPPORTED: errno = EOPNOTSUPP; break;
@ -1672,7 +1720,7 @@ namespace {
sysio()->recv_in.fd = noux_fd(fd->context);
sysio()->recv_in.len = curr_len;
if (!noux()->syscall(Noux::Session::SYSCALL_RECV)) {
if (!noux_syscall(Noux::Session::SYSCALL_RECV)) {
switch (sysio()->error.recv) {
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
@ -1717,7 +1765,7 @@ namespace {
else
sysio()->recvfrom_in.addrlen = *addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_RECVFROM)) {
if (!noux_syscall(Noux::Session::SYSCALL_RECVFROM)) {
switch (sysio()->error.recv) {
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
@ -1765,7 +1813,7 @@ namespace {
sysio()->send_in.len = curr_len;
Genode::memcpy(sysio()->send_in.buf, src, curr_len);
if (!noux()->syscall(Noux::Session::SYSCALL_SEND)) {
if (!noux_syscall(Noux::Session::SYSCALL_SEND)) {
PERR("write error %d", sysio()->error.general);
switch (sysio()->error.send) {
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
@ -1816,7 +1864,7 @@ namespace {
Genode::memcpy(&sysio()->sendto_in.dest_addr, dest_addr, addrlen);
}
if (!noux()->syscall(Noux::Session::SYSCALL_SENDTO)) {
if (!noux_syscall(Noux::Session::SYSCALL_SENDTO)) {
switch (sysio()->error.send) {
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::SEND_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
@ -1842,7 +1890,7 @@ namespace {
sysio()->shutdown_in.fd = noux_fd(fd->context);
sysio()->shutdown_in.how = how;
if (!noux()->syscall(Noux::Session::SYSCALL_SHUTDOWN)) {
if (!noux_syscall(Noux::Session::SYSCALL_SHUTDOWN)) {
switch (sysio()->error.shutdown) {
case Noux::Sysio::SHUTDOWN_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
default: errno = 0; break;

View File

@ -32,6 +32,7 @@
#include <io_receptor_registry.h>
#include <destruct_queue.h>
#include <destruct_dispatcher.h>
#include <interrupt_handler.h>
#include <local_cpu_service.h>
#include <local_ram_service.h>
@ -95,16 +96,22 @@ namespace Noux {
class Child : public Rpc_object<Session>,
public File_descriptor_registry,
public Family_member,
public Destruct_queue::Element<Child>
public Destruct_queue::Element<Child>,
public Interrupt_handler
{
private:
Signal_receiver *_sig_rec;
/**
* Semaphore used for implementing blocking syscalls, i.e., select
* Lock used for implementing blocking syscalls, i.e., select
*
* The reason to have it as a member variable instead of creating
* it on demand is to not have to register and unregister an
* interrupt handler at every IO channel on every blocking syscall,
* but only once when the IO channel gets added.
*/
Semaphore _blocker;
Lock _blocker;
Allocator *_alloc;
Destruct_queue &_destruct_queue;
@ -196,6 +203,10 @@ namespace Noux {
Attached_ram_dataspace _sysio_ds;
Sysio * const _sysio;
typedef Ring_buffer<enum Sysio::Signal, Sysio::SIGNAL_QUEUE_SIZE>
Signal_queue;
Signal_queue _pending_signals;
Session_capability const _noux_session_cap;
Local_noux_service _local_noux_service;
@ -242,11 +253,34 @@ namespace Noux {
child->add_io_channel(io_channel_by_fd(fd), fd);
}
void _block_for_io_channel(Shared_pointer<Io_channel> &io)
/**
* Block until the IO channel is ready for reading or writing or an
* exception occured.
*
* \param io the IO channel
* \param rd check for data available for reading
* \param wr check for readiness for writing
* \param ex check for exceptions
*/
void _block_for_io_channel(Shared_pointer<Io_channel> &io,
bool rd, bool wr, bool ex)
{
/* reset the blocker lock to the 'locked' state */
_blocker.unlock();
_blocker.lock();
Wake_up_notifier notifier(&_blocker);
io->register_wake_up_notifier(&notifier);
_blocker.down();
for (;;) {
if (io->check_unblock(rd, wr, ex) ||
!_pending_signals.empty())
break;
/* block (unless the lock got unlocked in the meantime) */
_blocker.lock();
}
io->unregister_wake_up_notifier(&notifier);
}
@ -395,6 +429,78 @@ namespace Noux {
return fd;
return -1;
}
/****************************************
** File_descriptor_registry overrides **
****************************************/
/**
* Find out if the IO channel associated with 'fd' has more file
* descriptors associated with it
*/
bool _is_the_only_fd_for_io_channel(int fd,
Shared_pointer<Io_channel> io_channel)
{
for (int f = 0; f < MAX_FILE_DESCRIPTORS; f++) {
if ((f != fd) &&
fd_in_use(f) &&
(io_channel_by_fd(f) == io_channel))
return false;
}
return true;
}
int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1)
{
fd = File_descriptor_registry::add_io_channel(io_channel, fd);
/* Register the interrupt handler only once per IO channel */
if (_is_the_only_fd_for_io_channel(fd, io_channel))
io_channel->register_interrupt_handler(this);
return fd;
}
void remove_io_channel(int fd)
{
Shared_pointer<Io_channel> io_channel = _lookup_channel(fd);
/*
* Unregister the interrupt handler only if there are no other
* file descriptors associated with the IO channel.
*/
if (_is_the_only_fd_for_io_channel(fd, io_channel))
io_channel->unregister_interrupt_handler(this);
File_descriptor_registry::remove_io_channel(fd);
}
void flush()
{
for (int fd = 0; fd < MAX_FILE_DESCRIPTORS; fd++)
try {
remove_io_channel(fd);
} catch (Invalid_fd) { }
}
/*********************************
** Interrupt_handler interface **
*********************************/
void handle_interrupt()
{
try {
_pending_signals.add(Sysio::SIG_INT);
} catch (Signal_queue::Overflow) {
PERR("signal queue is full - signal dropped");
}
_blocker.unlock();
}
};
};

View File

@ -72,7 +72,7 @@ namespace Noux {
*
* \return noux file descriptor used for the I/O channel
*/
int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1)
virtual int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1)
{
if ((fd == -1) && !_find_available_fd(&fd)) {
PERR("Could not allocate file descriptor");
@ -88,7 +88,7 @@ namespace Noux {
return fd;
}
void remove_io_channel(int fd)
virtual void remove_io_channel(int fd)
{
if (!_is_valid_fd(fd))
PERR("File descriptor %d is out of range", fd);
@ -103,14 +103,13 @@ namespace Noux {
Shared_pointer<Io_channel> io_channel_by_fd(int fd) const
{
if (!fd_in_use(fd)) {
PWRN("File descriptor %d is not open", fd);
if (!fd_in_use(fd))
return Shared_pointer<Io_channel>();
}
return _fds[fd].io_channel;
}
void flush()
virtual void flush()
{
/* close all file descriptors */
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)

View File

@ -33,6 +33,21 @@ namespace Noux {
* a device and is therefore false by default.
*/
virtual bool ioctl(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
/**
* Return true if an unblocking condition of the file 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(Vfs_handle *vfs_handle,
bool rd, bool wr, bool ex)
{ return true; }
virtual void register_read_ready_sigh(Vfs_handle *vfs_handle,
Signal_context_capability sigh)
{ }
};
}

View File

@ -0,0 +1,29 @@
/*
* \brief Interrupt handler interface
* \author Christian Prochaska
* \date 2013-10-08
*/
/*
* Copyright (C) 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__INTERRUPT_HANDLER__H_
#define _NOUX__INTERRUPT_HANDLER__H_
/* Genode includes */
#include <util/list.h>
namespace Noux {
struct Interrupt_handler : List<Interrupt_handler>::Element
{
virtual void handle_interrupt() = 0;
};
};
#endif /* _NOUX__INTERRUPT_HANDLER__H_ */

View File

@ -24,6 +24,7 @@
#include <noux_session/sysio.h>
#include <shared_pointer.h>
#include <wake_up_notifier.h>
#include <interrupt_handler.h>
namespace Noux {
@ -52,8 +53,10 @@ namespace Noux {
* 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<Wake_up_notifier> _notifiers;
Lock _notifiers_lock;
List<Interrupt_handler> _interrupt_handlers;
Lock _interrupt_handlers_lock;
public:
@ -130,6 +133,41 @@ namespace Noux {
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(Interrupt_handler *handler)
{
Lock::Guard guard(_interrupt_handlers_lock);
_interrupt_handlers.insert(handler);
}
/**
* Unregister interrupt handler
*/
void unregister_interrupt_handler(Interrupt_handler *handler)
{
Lock::Guard guard(_interrupt_handlers_lock);
_interrupt_handlers.remove(handler);
}
/**
* Tell all registered handlers about an interrupt event
*/
void invoke_all_interrupt_handlers()
{
Lock::Guard guard(_interrupt_handlers_lock);
for (Interrupt_handler *h = _interrupt_handlers.first();
h; h = h->next())
h->handle_interrupt();
}
};
}

View File

@ -16,7 +16,6 @@
/* Genode includes */
#include <base/lock.h>
#include <base/semaphore.h>
#include <util/list.h>
@ -26,19 +25,19 @@ namespace Noux {
{
private:
Semaphore *_sem;
Lock *_lock;
public:
Io_receptor(Semaphore *semaphore)
Io_receptor(Lock *lock)
:
_sem(semaphore)
_lock(lock)
{ }
void check_and_wakeup()
{
if (_sem)
_sem->up();
if (_lock)
_lock->unlock();
}
};

View File

@ -89,11 +89,11 @@ namespace Noux {
{
private:
Timeout_state *_state;
Semaphore *_blocker;
Lock *_blocker;
Timeout_scheduler *_scheduler;
public:
Timeout_alarm(Timeout_state *st, Semaphore *blocker, Timeout_scheduler *scheduler, Time timeout)
Timeout_alarm(Timeout_state *st, Lock *blocker, Timeout_scheduler *scheduler, Time timeout)
:
_state(st),
_blocker(blocker),
@ -109,7 +109,7 @@ namespace Noux {
bool on_alarm()
{
_state->timed_out = true;
_blocker->up();
_blocker->unlock();
return false;
}
@ -127,6 +127,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Genode::printf("PID %d -> SYSCALL %s\n",
pid(), Noux::Session::syscall_name(sc));
bool result = false;
try {
switch (sc) {
@ -139,17 +141,26 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->write_in.fd);
if (!io->is_nonblocking())
if (!io->check_unblock(false, true, false))
_block_for_io_channel(io);
_block_for_io_channel(io, false, true, false);
/*
* 'io->write' is expected to update 'write_out.count'
*/
if (io->write(_sysio, count) == false)
return false;
if (io->check_unblock(false, true, false)) {
/*
* 'io->write' is expected to update
* '_sysio->write_out.count' and 'count'
*/
result = io->write(_sysio, count);
if (result == false)
break;
} else {
if (result == false) {
/* nothing was written yet */
_sysio->error.write = Sysio::WRITE_ERR_INTERRUPT;
}
break;
}
}
return true;
break;
}
case SYSCALL_READ:
@ -157,20 +168,28 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->read_in.fd);
if (!io->is_nonblocking())
while (!io->check_unblock(true, false, false))
_block_for_io_channel(io);
_block_for_io_channel(io, true, false, false);
return io->read(_sysio);
if (io->check_unblock(true, false, false))
result = io->read(_sysio);
else
_sysio->error.read = Sysio::READ_ERR_INTERRUPT;
break;
}
case SYSCALL_FTRUNCATE:
{
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->ftruncate_in.fd);
while (!io->check_unblock(true, false, false))
_block_for_io_channel(io);
_block_for_io_channel(io, false, true, false);
return io->ftruncate(_sysio);
if (io->check_unblock(false, true, false))
result = io->ftruncate(_sysio);
else
_sysio->error.ftruncate = Sysio::FTRUNCATE_ERR_INTERRUPT;
break;
}
case SYSCALL_STAT:
@ -225,7 +244,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Shared_pointer<Io_channel>
channel(new Vfs_io_channel(_sysio->open_in.path,
leaf_path, root_dir(), vfs_handle),
leaf_path, root_dir(),
vfs_handle, *_sig_rec),
Genode::env()->heap());
_sysio->open_out.fd = add_io_channel(channel);
@ -302,6 +322,17 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_assign_io_channels_to(child);
/*
* Close all open files.
*
* This action is not part of the child destructor,
* because in the case that a child exits itself,
* it may need to close all files to unblock the
* parent (which might be reading from a pipe) before
* the parent can destroy the child object.
*/
flush();
/* signal main thread to remove ourself */
Genode::Signal_transmitter(_destruct_context_cap).submit();
@ -329,6 +360,46 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
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(&notifiers[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
*/
@ -383,13 +454,14 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
/* exception fds are currently not considered */
_sysio->select_out.fds.num_ex = unblock_ex;
return true;
result = true;
break;
}
/*
* Return if I/O channel triggered, but timeout exceeded
* Return if timeout is zero or timeout exceeded
*/
if (_sysio->select_in.timeout.zero() || timeout_reached) {
/*
if (timeout_reached) PINF("timeout_reached");
@ -399,43 +471,19 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_sysio->select_out.fds.num_wr = 0;
_sysio->select_out.fds.num_ex = 0;
return true;
result = true;
break;
}
/*
* 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 semaphore may get unblocked for other
* conditions such as the destruction of the child.
* ...to be done.
* Return if signals are pending
*/
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].semaphore = &_blocker;
io->register_wake_up_notifier(&notifiers[i]);
if (!_pending_signals.empty()) {
_sysio->error.select = Sysio::SELECT_ERR_INTERRUPT;
break;
}
/**
* 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 at barrier except when reaching the timeout
*/
@ -446,7 +494,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Timeout_alarm ta(&ts, &_blocker, Noux::timeout_scheduler(), to_msec);
/* block until timeout is reached or we were unblocked */
_blocker.down();
_blocker.lock();
if (ts.timed_out) {
timeout_reached = 1;
@ -461,28 +509,28 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
}
else {
/* let's block infinitely */
_blocker.down();
_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(&notifiers[i]);
}
/*
* Unregister receptor
*/
io_receptor_registry()->unregister_receptor(&receptor);
}
return true;
/*
* 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(&notifiers[i]);
}
/*
* Unregister receptor
*/
io_receptor_registry()->unregister_receptor(&receptor);
break;
}
case SYSCALL_FORK:
@ -722,7 +770,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
catch (...) { PERR("Unexpected exception"); }
return false;
/* 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;
}

View File

@ -145,6 +145,11 @@ namespace Noux {
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()
{

View File

@ -32,8 +32,6 @@ namespace Noux {
private:
Terminal::Session_client _terminal;
Signal_context _read_avail_sig_ctx;
Signal_receiver _read_avail_sig_rec;
enum { FILENAME_MAX_LEN = 64 };
char _filename[FILENAME_MAX_LEN];
@ -74,11 +72,6 @@ namespace Noux {
/* wati for signal */
sig_rec.wait_for_signal();
sig_rec.dissolve(&sig_ctx);
/*
* Register "read available" signal handler
*/
_terminal.read_avail_sigh(_read_avail_sig_rec.manage(&_read_avail_sig_ctx));
}
@ -212,12 +205,28 @@ namespace Noux {
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
{
_read_avail_sig_rec.wait_for_signal();
sysio->read_out.count = _terminal.read(sysio->read_out.chunk, sysio->read_in.count);
return true;
}
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; }
bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex)
{
if (rd && (_terminal.avail() > 0))
return true;
if (wr)
return true;
return false;
}
void register_read_ready_sigh(Vfs_handle *vfs_handle,
Signal_context_capability sigh)
{
_terminal.read_avail_sigh(sigh);
}
};
}

View File

@ -17,6 +17,7 @@
/* Genode includes */
#include <util/string.h>
#include <base/printf.h>
#include <os/ring_buffer.h>
#include <terminal_session/connection.h>
/* Noux includes */
@ -33,6 +34,8 @@ namespace Noux {
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)
@ -80,29 +83,32 @@ namespace Noux {
min(sysio->read_in.count,
sizeof(sysio->read_out.chunk));
sysio->read_out.count =
terminal.read(sysio->read_out.chunk, max_count);
for (sysio->read_out.count = 0;
(sysio->read_out.count < max_count) && !read_buffer.empty();
sysio->read_out.count++) {
/* scan received characters for EOF */
for (unsigned i = 0; i < sysio->read_out.count; i++) {
char c = read_buffer.get();
enum { EOF = 4 };
if (sysio->read_out.chunk[i] != EOF)
continue;
/* discard EOF character and everything that follows... */
sysio->read_out.count = i;
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;
/*
* 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;
@ -150,7 +156,7 @@ namespace Noux {
* Unblock I/O channel if the terminal has new user input. Channels
* otther than STDIN will never unblock.
*/
return (rd && (type == STDIN) && terminal.avail());
return (rd && (type == STDIN) && !read_buffer.empty());
}
bool ioctl(Sysio *sysio)
@ -194,6 +200,21 @@ namespace Noux {
*/
void dispatch(unsigned)
{
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();
}
};

View File

@ -21,18 +21,29 @@
namespace Noux {
struct Vfs_io_channel : public Io_channel
struct Vfs_io_channel : Io_channel, Signal_dispatcher_base
{
Vfs_handle *_fh;
Absolute_path _path;
Absolute_path _leaf_path;
Vfs_io_channel(char const *path, char const *leaf_path,
Dir_file_system *root_dir, Vfs_handle *vfs_handle)
: _fh(vfs_handle), _path(path), _leaf_path(leaf_path) { }
Signal_receiver &_sig_rec;
~Vfs_io_channel() { destroy(env()->heap(), _fh); }
Vfs_io_channel(char const *path, char const *leaf_path,
Dir_file_system *root_dir, 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()
{
_sig_rec.dissolve(this);
destroy(env()->heap(), _fh);
}
bool write(Sysio *sysio, size_t &count)
{
@ -158,12 +169,21 @@ namespace Noux {
bool check_unblock(bool rd, bool wr, bool ex) const
{
/*
* XXX For now, we use the TAR fs only, which never blocks.
* However, real file systems may block.
*/
return true;
return _fh->fs()->check_unblock(_fh, rd, wr, ex);
}
/**************************************
** Signal_dispatcher_base interface **
**************************************/
/**
* Called by Noux main loop on the occurrence of new input
*/
void dispatch(unsigned)
{
Io_channel::invoke_all_notifiers();
}
};
}

View File

@ -16,21 +16,21 @@
/* Genode includes */
#include <util/list.h>
#include <base/semaphore.h>
#include <base/lock.h>
namespace Noux {
struct Wake_up_notifier : List<Wake_up_notifier>::Element
{
Semaphore *semaphore;
Lock *lock;
Wake_up_notifier(Semaphore *semaphore = 0)
: semaphore(semaphore) { }
Wake_up_notifier(Lock *lock = 0)
: lock(lock) { }
void wake_up()
{
if (semaphore)
semaphore->up();
if (lock)
lock->unlock();
}
};
};

View File

@ -0,0 +1,56 @@
/*
* \brief Noux SIGINT handler test
* \author Christian Prochaska
* \date 2013-10-17
*/
/*
* Copyright (C) 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.
*/
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
void signal_handler(int signal)
{
printf("%d: signal handler for signal %d called\n",
getpid(), signal);
}
int main(int argc, char *argv[])
{
char c;
struct sigaction sa;
memset (&sa, '\0', sizeof(sa));
sa.sa_handler = signal_handler;
sigaction(SIGINT, &sa, 0);
int pid = fork();
if (pid == 0)
printf("test ready\n");
if ((read(0, &c, 1) == -1) && (errno = EINTR))
printf("%d: 'read()' returned with error EINTR\n", getpid());
else
printf("%d: 'read()' returned character 0x = %x\n", getpid(), c);
if (pid > 0) {
waitpid(pid, 0, 0);
printf("test finished\n");
}
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = test-noux_signals
SRC_CC = main.cc
LIBS = libc libc_noux