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_ #define _INCLUDE__NOUX_SESSION__SYSIO_H_
/* Genode includes */ /* Genode includes */
#include <os/ring_buffer.h>
#include <util/misc_math.h> #include <util/misc_math.h>
@ -33,16 +34,14 @@ namespace Noux {
struct Sysio struct Sysio
{ {
/* /* signal numbers must match with libc signal numbers */
* Information about pending signals enum Signal {
*/ SIG_INT = 2,
enum { SIG_MAX = 32 }; };
bool sig_mask[SIG_MAX];
/* enum { SIGNAL_QUEUE_SIZE = 32 };
* Number of pending signals Ring_buffer<enum Signal, SIGNAL_QUEUE_SIZE,
*/ Ring_buffer_unsynchronized> pending_signals;
int sig_cnt;
enum { MAX_PATH_LEN = 512 }; enum { MAX_PATH_LEN = 512 };
typedef char Path[MAX_PATH_LEN]; typedef char Path[MAX_PATH_LEN];
@ -295,10 +294,12 @@ namespace Noux {
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS }; enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS }; enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = 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, enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM,
OPEN_ERR_EXISTS }; OPEN_ERR_EXISTS };
enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS }; 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 Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM };
enum Readlink_error { READLINK_ERR_NO_ENTRY }; enum Readlink_error { READLINK_ERR_NO_ENTRY };
enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS, enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
@ -310,10 +311,12 @@ namespace Noux {
SYMLINK_ERR_NAME_TOO_LONG}; SYMLINK_ERR_NAME_TOO_LONG};
enum Read_error { READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK, 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, 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 }; enum Ioctl_error { IOCTL_ERR_INVALID, IOCTL_ERR_NOTTY };
@ -361,6 +364,7 @@ namespace Noux {
Ftruncate_error ftruncate; Ftruncate_error ftruncate;
Open_error open; Open_error open;
Execve_error execve; Execve_error execve;
Select_error select;
Unlink_error unlink; Unlink_error unlink;
Readlink_error readlink; Readlink_error readlink;
Rename_error rename; 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> #include <libc_mem_alloc.h>
enum { verbose = false }; enum { verbose = false };
enum { verbose_signals = false };
void *operator new (size_t, void *ptr) { return ptr; } 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(); } 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 }; 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.uid = uid;
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_ALL; 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; return (struct passwd *)0;
} }
@ -144,7 +173,7 @@ extern "C" uid_t getgid()
{ {
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_GID; 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; return 0;
uid_t gid = sysio()->userinfo_out.gid; 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; 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; return 0;
uid_t uid = sysio()->userinfo_out.uid; 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 * Perform syscall
*/ */
if (!noux()->syscall(Noux::Session::SYSCALL_SELECT)) { if (!noux_syscall(Noux::Session::SYSCALL_SELECT)) {
PWRN("select syscall failed"); switch (sysio()->error.select) {
// switch (sysio()->error.select) { case Noux::Sysio::SELECT_ERR_INTERRUPT: errno = EINTR; break;
// case Noux::Sysio::SELECT_NONEXISTENT: errno = ENOENT; break; }
// }
return -1; 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.sp = (Genode::addr_t)(&stack[STACK_SIZE]);
sysio()->fork_in.parent_cap_addr = (Genode::addr_t)(&new_parent); 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); 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) extern "C" pid_t getpid(void)
{ {
noux()->syscall(Noux::Session::SYSCALL_GETPID); noux_syscall(Noux::Session::SYSCALL_GETPID);
return sysio()->getpid_out.pid; 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.pid = pid;
sysio()->wait4_in.nohang = !!(options & WNOHANG); 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); PERR("wait4 error %d", sysio()->error.general);
return -1; return -1;
} }
@ -545,7 +573,7 @@ extern "C" int clock_gettime(clockid_t clk_id, struct timespec *tp)
return -1; return -1;
} }
if (!noux()->syscall(Noux::Session::SYSCALL_CLOCK_GETTIME)) { if (!noux_syscall(Noux::Session::SYSCALL_CLOCK_GETTIME)) {
switch (sysio()->error.clock) { switch (sysio()->error.clock) {
case Noux::Sysio::CLOCK_ERR_INVALID: errno = EINVAL; break; case Noux::Sysio::CLOCK_ERR_INVALID: errno = EINVAL; break;
default: errno = 0; 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) 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; errno = EINVAL;
return -1; 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) 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; errno = EINVAL;
return -1; 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 */ if (verbose_signals)
errno = ENOSYS; PDBG("signum = %d, handler = %p", signum, act ? act->sa_handler : 0);
return -1;
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, extern "C" int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact) struct sigaction *oldact)
{ {
/* XXX todo */ return _sigaction(signum, act, oldact);
errno = ENOSYS;
return -1;
} }
@ -771,7 +809,7 @@ namespace {
return -1; return -1;
} }
if (!noux()->syscall(Noux::Session::SYSCALL_EXECVE)) { if (!noux_syscall(Noux::Session::SYSCALL_EXECVE)) {
PWRN("exec syscall failed for path \"%s\"", filename); PWRN("exec syscall failed for path \"%s\"", filename);
switch (sysio()->error.execve) { switch (sysio()->error.execve) {
case Noux::Sysio::EXECVE_NONEXISTENT: errno = ENOENT; break; 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)); 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) if (verbose)
PWRN("stat syscall failed for path \"%s\"", path); PWRN("stat syscall failed for path \"%s\"", path);
switch (sysio()->error.stat) { switch (sysio()->error.stat) {
@ -826,7 +864,7 @@ namespace {
while (!opened) { while (!opened) {
Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path)); Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path));
sysio()->open_in.mode = flags; sysio()->open_in.mode = flags;
if (noux()->syscall(Noux::Session::SYSCALL_OPEN)) if (noux_syscall(Noux::Session::SYSCALL_OPEN))
opened = true; opened = true;
else else
switch (sysio()->error.open) { switch (sysio()->error.open) {
@ -838,7 +876,7 @@ namespace {
/* O_CREAT is set, so try to create the file */ /* O_CREAT is set, so try to create the file */
Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path)); Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path));
sysio()->open_in.mode = flags | O_EXCL; sysio()->open_in.mode = flags | O_EXCL;
if (noux()->syscall(Noux::Session::SYSCALL_OPEN)) if (noux_syscall(Noux::Session::SYSCALL_OPEN))
opened = true; opened = true;
else else
switch (sysio()->error.open) { 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.oldpath, oldpath, sizeof(sysio()->symlink_in.oldpath));
Genode::strncpy(sysio()->symlink_in.newpath, newpath, sizeof(sysio()->symlink_in.newpath)); 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"); PERR("symlink error");
/* XXX set errno */ /* XXX set errno */
return -1; return -1;
@ -910,12 +948,13 @@ namespace {
sysio()->write_in.count = curr_count; sysio()->write_in.count = curr_count;
Genode::memcpy(sysio()->write_in.chunk, src, 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) { switch (sysio()->error.write) {
case Noux::Sysio::WRITE_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::WRITE_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; 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_INVALID: errno = EINVAL; break;
case Noux::Sysio::WRITE_ERR_IO: errno = EIO; break; case Noux::Sysio::WRITE_ERR_IO: errno = EIO; break;
case Noux::Sysio::WRITE_ERR_INTERRUPT: errno = EINTR; break;
default: default:
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID) if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
errno = EBADF; errno = EBADF;
@ -944,12 +983,14 @@ namespace {
sysio()->read_in.fd = noux_fd(fd->context); sysio()->read_in.fd = noux_fd(fd->context);
sysio()->read_in.count = curr_count; sysio()->read_in.count = curr_count;
if (!noux()->syscall(Noux::Session::SYSCALL_READ)) { if (!noux_syscall(Noux::Session::SYSCALL_READ)) {
switch (sysio()->error.read) { switch (sysio()->error.read) {
case Noux::Sysio::READ_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::READ_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; 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_INVALID: errno = EINVAL; break;
case Noux::Sysio::READ_ERR_IO: errno = EIO; break; case Noux::Sysio::READ_ERR_IO: errno = EIO; break;
case Noux::Sysio::READ_ERR_INTERRUPT: errno = EINTR; break;
default: default:
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID) if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
errno = EBADF; errno = EBADF;
@ -982,7 +1023,7 @@ namespace {
int Plugin::close(Libc::File_descriptor *fd) int Plugin::close(Libc::File_descriptor *fd)
{ {
sysio()->close_in.fd = noux_fd(fd->context); 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"); PERR("close error");
/* XXX set errno */ /* XXX set errno */
return -1; return -1;
@ -1080,7 +1121,7 @@ namespace {
} }
/* perform syscall */ /* perform syscall */
if (!noux()->syscall(Noux::Session::SYSCALL_IOCTL)) { if (!noux_syscall(Noux::Session::SYSCALL_IOCTL)) {
switch (sysio()->error.ioctl) { switch (sysio()->error.ioctl) {
case Noux::Sysio::IOCTL_ERR_INVALID: errno = EINVAL; break; case Noux::Sysio::IOCTL_ERR_INVALID: errno = EINVAL; break;
case Noux::Sysio::IOCTL_ERR_NOTTY: errno = ENOTTY; break; case Noux::Sysio::IOCTL_ERR_NOTTY: errno = ENOTTY; break;
@ -1118,7 +1159,7 @@ namespace {
int Plugin::pipe(Libc::File_descriptor *pipefd[2]) int Plugin::pipe(Libc::File_descriptor *pipefd[2])
{ {
/* perform syscall */ /* perform syscall */
if (!noux()->syscall(Noux::Session::SYSCALL_PIPE)) { if (!noux_syscall(Noux::Session::SYSCALL_PIPE)) {
PERR("pipe error"); PERR("pipe error");
/* XXX set errno */ /* XXX set errno */
return -1; return -1;
@ -1137,7 +1178,7 @@ namespace {
sysio()->dup2_in.fd = noux_fd(fd->context); sysio()->dup2_in.fd = noux_fd(fd->context);
sysio()->dup2_in.to_fd = -1; sysio()->dup2_in.to_fd = -1;
if (!noux()->syscall(Noux::Session::SYSCALL_DUP2)) { if (!noux_syscall(Noux::Session::SYSCALL_DUP2)) {
PERR("dup error"); PERR("dup error");
/* XXX set errno */ /* XXX set errno */
return 0; return 0;
@ -1160,7 +1201,7 @@ namespace {
sysio()->dup2_in.to_fd = noux_fd(new_fd->context); sysio()->dup2_in.to_fd = noux_fd(new_fd->context);
/* perform syscall */ /* perform syscall */
if (!noux()->syscall(Noux::Session::SYSCALL_DUP2)) { if (!noux_syscall(Noux::Session::SYSCALL_DUP2)) {
PERR("dup2 error"); PERR("dup2 error");
/* XXX set errno */ /* XXX set errno */
return -1; return -1;
@ -1173,7 +1214,7 @@ namespace {
int Plugin::fstat(Libc::File_descriptor *fd, struct stat *buf) int Plugin::fstat(Libc::File_descriptor *fd, struct stat *buf)
{ {
sysio()->fstat_in.fd = noux_fd(fd->context); 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"); PERR("fstat error");
/* XXX set errno */ /* XXX set errno */
return -1; return -1;
@ -1196,9 +1237,10 @@ namespace {
{ {
sysio()->ftruncate_in.fd = noux_fd(fd->context); sysio()->ftruncate_in.fd = noux_fd(fd->context);
sysio()->ftruncate_in.length = length; sysio()->ftruncate_in.length = length;
if (!noux()->syscall(Noux::Session::SYSCALL_FTRUNCATE)) { if (!noux_syscall(Noux::Session::SYSCALL_FTRUNCATE)) {
switch (sysio()->error.ftruncate) { switch (sysio()->error.ftruncate) {
case Noux::Sysio::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break; case Noux::Sysio::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break;
case Noux::Sysio::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; break;
} }
return -1; return -1;
} }
@ -1271,10 +1313,16 @@ namespace {
}; };
/* invoke system call */ /* 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); PWRN("fcntl failed (libc_fd= %d, cmd=%x)", fd->libc_fd, cmd);
/* XXX read error code from sysio */ switch (sysio()->error.fcntl) {
errno = EINVAL; 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; return -1;
} }
@ -1296,7 +1344,7 @@ namespace {
struct dirent *dirent = (struct dirent *)buf; struct dirent *dirent = (struct dirent *)buf;
Genode::memset(dirent, 0, sizeof(struct dirent)); 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) { switch (sysio()->error.general) {
case Noux::Sysio::ERR_FD_INVALID: case Noux::Sysio::ERR_FD_INVALID:
@ -1343,7 +1391,7 @@ namespace {
case SEEK_END: sysio()->lseek_in.whence = Noux::Sysio::LSEEK_END; break; 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) { switch (sysio()->error.general) {
case Noux::Sysio::ERR_FD_INVALID: case Noux::Sysio::ERR_FD_INVALID:
@ -1363,7 +1411,7 @@ namespace {
{ {
Genode::strncpy(sysio()->unlink_in.path, path, sizeof(sysio()->unlink_in.path)); 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); PWRN("unlink syscall failed for path \"%s\"", path);
switch (sysio()->error.unlink) { switch (sysio()->error.unlink) {
case Noux::Sysio::UNLINK_ERR_NO_ENTRY: errno = ENOENT; break; 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)); Genode::strncpy(sysio()->readlink_in.path, path, sizeof(sysio()->readlink_in.path));
sysio()->readlink_in.bufsiz = bufsiz; 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); PWRN("readlink syscall failed for \"%s\"", path);
/* XXX set errno */ /* XXX set errno */
return -1; 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.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)); 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); PWRN("rename syscall failed for \"%s\" -> \"%s\"", from_path, to_path);
switch (sysio()->error.rename) { switch (sysio()->error.rename) {
case Noux::Sysio::RENAME_ERR_NO_ENTRY: errno = ENOENT; break; 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)); 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); PWRN("mkdir syscall failed for \"%s\" mode=0x%x", path, (int)mode);
switch (sysio()->error.mkdir) { switch (sysio()->error.mkdir) {
case Noux::Sysio::MKDIR_ERR_EXISTS: errno = EEXIST; break; case Noux::Sysio::MKDIR_ERR_EXISTS: errno = EEXIST; break;
@ -1486,7 +1534,7 @@ namespace {
sysio()->socket_in.type = type; sysio()->socket_in.type = type;
sysio()->socket_in.protocol = protocol; sysio()->socket_in.protocol = protocol;
if (!noux()->syscall(Noux::Session::SYSCALL_SOCKET)) if (!noux_syscall(Noux::Session::SYSCALL_SOCKET))
return 0; return 0;
Libc::Plugin_context *context = noux_context(sysio()->socket_out.fd); Libc::Plugin_context *context = noux_context(sysio()->socket_out.fd);
@ -1507,7 +1555,7 @@ namespace {
Genode::memset(sysio()->getsockopt_in.optval, 0, Genode::memset(sysio()->getsockopt_in.optval, 0,
sizeof (sysio()->getsockopt_in.optval)); sizeof (sysio()->getsockopt_in.optval));
if (!noux()->syscall(Noux::Session::SYSCALL_GETSOCKOPT)) if (!noux_syscall(Noux::Session::SYSCALL_GETSOCKOPT))
return -1; return -1;
Genode::memcpy(optval, sysio()->setsockopt_in.optval, Genode::memcpy(optval, sysio()->setsockopt_in.optval,
@ -1532,7 +1580,7 @@ namespace {
Genode::memcpy(sysio()->setsockopt_in.optval, optval, optlen); Genode::memcpy(sysio()->setsockopt_in.optval, optval, optlen);
if (!noux()->syscall(Noux::Session::SYSCALL_SETSOCKOPT)) { if (!noux_syscall(Noux::Session::SYSCALL_SETSOCKOPT)) {
/* XXX */ /* XXX */
return -1; return -1;
} }
@ -1555,7 +1603,7 @@ namespace {
sysio()->accept_in.addrlen = 0; sysio()->accept_in.addrlen = 0;
} }
if (!noux()->syscall(Noux::Session::SYSCALL_ACCEPT)) { if (!noux_syscall(Noux::Session::SYSCALL_ACCEPT)) {
switch (sysio()->error.accept) { switch (sysio()->error.accept) {
case Noux::Sysio::ACCEPT_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::ACCEPT_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::ACCEPT_ERR_NO_MEMORY: errno = ENOMEM; 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)); Genode::memcpy(&sysio()->bind_in.addr, addr, sizeof (struct sockaddr));
sysio()->bind_in.addrlen = addrlen; sysio()->bind_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_BIND)) { if (!noux_syscall(Noux::Session::SYSCALL_BIND)) {
switch (sysio()->error.bind) { switch (sysio()->error.bind) {
case Noux::Sysio::BIND_ERR_ACCESS: errno = EACCES; break; case Noux::Sysio::BIND_ERR_ACCESS: errno = EACCES; break;
case Noux::Sysio::BIND_ERR_ADDR_IN_USE: errno = EADDRINUSE; break; case Noux::Sysio::BIND_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
@ -1608,7 +1656,7 @@ namespace {
Genode::memcpy(&sysio()->connect_in.addr, addr, sizeof (struct sockaddr)); Genode::memcpy(&sysio()->connect_in.addr, addr, sizeof (struct sockaddr));
sysio()->connect_in.addrlen = addrlen; sysio()->connect_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_CONNECT)) { if (!noux_syscall(Noux::Session::SYSCALL_CONNECT)) {
switch (sysio()->error.connect) { switch (sysio()->error.connect) {
case Noux::Sysio::CONNECT_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::CONNECT_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::CONNECT_ERR_ALREADY: errno = EALREADY; 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.fd = noux_fd(fd->context);
sysio()->getpeername_in.addrlen = *addrlen; sysio()->getpeername_in.addrlen = *addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_GETPEERNAME)) { if (!noux_syscall(Noux::Session::SYSCALL_GETPEERNAME)) {
/* errno */ /* errno */
return -1; return -1;
} }
@ -1648,7 +1696,7 @@ namespace {
sysio()->listen_in.fd = noux_fd(fd->context); sysio()->listen_in.fd = noux_fd(fd->context);
sysio()->listen_in.backlog = backlog; sysio()->listen_in.backlog = backlog;
if (!noux()->syscall(Noux::Session::SYSCALL_LISTEN)) { if (!noux_syscall(Noux::Session::SYSCALL_LISTEN)) {
switch (sysio()->error.listen) { switch (sysio()->error.listen) {
case Noux::Sysio::LISTEN_ERR_ADDR_IN_USE: errno = EADDRINUSE; break; case Noux::Sysio::LISTEN_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
case Noux::Sysio::LISTEN_ERR_NOT_SUPPORTED: errno = EOPNOTSUPP; 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.fd = noux_fd(fd->context);
sysio()->recv_in.len = curr_len; sysio()->recv_in.len = curr_len;
if (!noux()->syscall(Noux::Session::SYSCALL_RECV)) { if (!noux_syscall(Noux::Session::SYSCALL_RECV)) {
switch (sysio()->error.recv) { switch (sysio()->error.recv) {
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break; case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
@ -1717,7 +1765,7 @@ namespace {
else else
sysio()->recvfrom_in.addrlen = *addrlen; sysio()->recvfrom_in.addrlen = *addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_RECVFROM)) { if (!noux_syscall(Noux::Session::SYSCALL_RECVFROM)) {
switch (sysio()->error.recv) { switch (sysio()->error.recv) {
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break; case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
@ -1765,7 +1813,7 @@ namespace {
sysio()->send_in.len = curr_len; sysio()->send_in.len = curr_len;
Genode::memcpy(sysio()->send_in.buf, src, 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); PERR("write error %d", sysio()->error.general);
switch (sysio()->error.send) { switch (sysio()->error.send) {
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
@ -1816,7 +1864,7 @@ namespace {
Genode::memcpy(&sysio()->sendto_in.dest_addr, dest_addr, addrlen); 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) { switch (sysio()->error.send) {
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break; case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
case Noux::Sysio::SEND_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; 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.fd = noux_fd(fd->context);
sysio()->shutdown_in.how = how; sysio()->shutdown_in.how = how;
if (!noux()->syscall(Noux::Session::SYSCALL_SHUTDOWN)) { if (!noux_syscall(Noux::Session::SYSCALL_SHUTDOWN)) {
switch (sysio()->error.shutdown) { switch (sysio()->error.shutdown) {
case Noux::Sysio::SHUTDOWN_ERR_NOT_CONNECTED: errno = ENOTCONN; break; case Noux::Sysio::SHUTDOWN_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
default: errno = 0; break; default: errno = 0; break;

View File

@ -32,6 +32,7 @@
#include <io_receptor_registry.h> #include <io_receptor_registry.h>
#include <destruct_queue.h> #include <destruct_queue.h>
#include <destruct_dispatcher.h> #include <destruct_dispatcher.h>
#include <interrupt_handler.h>
#include <local_cpu_service.h> #include <local_cpu_service.h>
#include <local_ram_service.h> #include <local_ram_service.h>
@ -95,16 +96,22 @@ namespace Noux {
class Child : public Rpc_object<Session>, class Child : public Rpc_object<Session>,
public File_descriptor_registry, public File_descriptor_registry,
public Family_member, public Family_member,
public Destruct_queue::Element<Child> public Destruct_queue::Element<Child>,
public Interrupt_handler
{ {
private: private:
Signal_receiver *_sig_rec; 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; Allocator *_alloc;
Destruct_queue &_destruct_queue; Destruct_queue &_destruct_queue;
@ -196,6 +203,10 @@ namespace Noux {
Attached_ram_dataspace _sysio_ds; Attached_ram_dataspace _sysio_ds;
Sysio * const _sysio; 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; Session_capability const _noux_session_cap;
Local_noux_service _local_noux_service; Local_noux_service _local_noux_service;
@ -242,11 +253,34 @@ namespace Noux {
child->add_io_channel(io_channel_by_fd(fd), fd); 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); Wake_up_notifier notifier(&_blocker);
io->register_wake_up_notifier(&notifier); 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); io->unregister_wake_up_notifier(&notifier);
} }
@ -395,6 +429,78 @@ namespace Noux {
return fd; return fd;
return -1; 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 * \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)) { if ((fd == -1) && !_find_available_fd(&fd)) {
PERR("Could not allocate file descriptor"); PERR("Could not allocate file descriptor");
@ -88,7 +88,7 @@ namespace Noux {
return fd; return fd;
} }
void remove_io_channel(int fd) virtual void remove_io_channel(int fd)
{ {
if (!_is_valid_fd(fd)) if (!_is_valid_fd(fd))
PERR("File descriptor %d is out of range", 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 Shared_pointer<Io_channel> io_channel_by_fd(int fd) const
{ {
if (!fd_in_use(fd)) { if (!fd_in_use(fd))
PWRN("File descriptor %d is not open", fd);
return Shared_pointer<Io_channel>(); return Shared_pointer<Io_channel>();
}
return _fds[fd].io_channel; return _fds[fd].io_channel;
} }
void flush() virtual void flush()
{ {
/* close all file descriptors */ /* close all file descriptors */
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++) 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. * a device and is therefore false by default.
*/ */
virtual bool ioctl(Sysio *sysio, Vfs_handle *vfs_handle) { return false; } 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 <noux_session/sysio.h>
#include <shared_pointer.h> #include <shared_pointer.h>
#include <wake_up_notifier.h> #include <wake_up_notifier.h>
#include <interrupt_handler.h>
namespace Noux { namespace Noux {
@ -52,8 +53,10 @@ namespace Noux {
* List of notifiers (i.e., processes) used by threads that block * List of notifiers (i.e., processes) used by threads that block
* for an I/O-channel event * for an I/O-channel event
*/ */
List<Wake_up_notifier> _notifiers; List<Wake_up_notifier> _notifiers;
Lock _notifiers_lock; Lock _notifiers_lock;
List<Interrupt_handler> _interrupt_handlers;
Lock _interrupt_handlers_lock;
public: public:
@ -130,6 +133,41 @@ namespace Noux {
for (Wake_up_notifier *n = _notifiers.first(); n; n = n->next()) for (Wake_up_notifier *n = _notifiers.first(); n; n = n->next())
n->wake_up(); 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 */ /* Genode includes */
#include <base/lock.h> #include <base/lock.h>
#include <base/semaphore.h>
#include <util/list.h> #include <util/list.h>
@ -26,19 +25,19 @@ namespace Noux {
{ {
private: private:
Semaphore *_sem; Lock *_lock;
public: public:
Io_receptor(Semaphore *semaphore) Io_receptor(Lock *lock)
: :
_sem(semaphore) _lock(lock)
{ } { }
void check_and_wakeup() void check_and_wakeup()
{ {
if (_sem) if (_lock)
_sem->up(); _lock->unlock();
} }
}; };

View File

@ -89,11 +89,11 @@ namespace Noux {
{ {
private: private:
Timeout_state *_state; Timeout_state *_state;
Semaphore *_blocker; Lock *_blocker;
Timeout_scheduler *_scheduler; Timeout_scheduler *_scheduler;
public: 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), _state(st),
_blocker(blocker), _blocker(blocker),
@ -109,7 +109,7 @@ namespace Noux {
bool on_alarm() bool on_alarm()
{ {
_state->timed_out = true; _state->timed_out = true;
_blocker->up(); _blocker->unlock();
return false; return false;
} }
@ -127,6 +127,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Genode::printf("PID %d -> SYSCALL %s\n", Genode::printf("PID %d -> SYSCALL %s\n",
pid(), Noux::Session::syscall_name(sc)); pid(), Noux::Session::syscall_name(sc));
bool result = false;
try { try {
switch (sc) { 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); Shared_pointer<Io_channel> io = _lookup_channel(_sysio->write_in.fd);
if (!io->is_nonblocking()) if (!io->is_nonblocking())
if (!io->check_unblock(false, true, false)) _block_for_io_channel(io, false, true, false);
_block_for_io_channel(io);
/* if (io->check_unblock(false, true, false)) {
* 'io->write' is expected to update 'write_out.count' /*
*/ * 'io->write' is expected to update
if (io->write(_sysio, count) == false) * '_sysio->write_out.count' and 'count'
return false; */
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: 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); Shared_pointer<Io_channel> io = _lookup_channel(_sysio->read_in.fd);
if (!io->is_nonblocking()) if (!io->is_nonblocking())
while (!io->check_unblock(true, false, false)) _block_for_io_channel(io, true, false, false);
_block_for_io_channel(io);
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: case SYSCALL_FTRUNCATE:
{ {
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->ftruncate_in.fd); Shared_pointer<Io_channel> io = _lookup_channel(_sysio->ftruncate_in.fd);
while (!io->check_unblock(true, false, false)) _block_for_io_channel(io, false, true, false);
_block_for_io_channel(io);
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: case SYSCALL_STAT:
@ -225,7 +244,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Shared_pointer<Io_channel> Shared_pointer<Io_channel>
channel(new Vfs_io_channel(_sysio->open_in.path, 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()); Genode::env()->heap());
_sysio->open_out.fd = add_io_channel(channel); _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); _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 */ /* signal main thread to remove ourself */
Genode::Signal_transmitter(_destruct_context_cap).submit(); 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; long timeout_usec = _sysio->select_in.timeout.usec;
bool timeout_reached = false; 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 * Block for one action of the watched file descriptors
*/ */
@ -383,11 +454,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
/* exception fds are currently not considered */ /* exception fds are currently not considered */
_sysio->select_out.fds.num_ex = unblock_ex; _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 (_sysio->select_in.timeout.zero() || 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_wr = 0;
_sysio->select_out.fds.num_ex = 0; _sysio->select_out.fds.num_ex = 0;
return true; result = true;
break;
} }
/* /*
* Register ourself at all watched I/O channels * Return if signals are pending
*
* 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.
*/ */
Wake_up_notifier notifiers[in_fds_total]; if (!_pending_signals.empty()) {
_sysio->error.select = Sysio::SELECT_ERR_INTERRUPT;
for (Genode::size_t i = 0; i < in_fds_total; i++) { break;
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]);
} }
/**
* 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 * 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); Timeout_alarm ta(&ts, &_blocker, Noux::timeout_scheduler(), to_msec);
/* block until timeout is reached or we were unblocked */ /* block until timeout is reached or we were unblocked */
_blocker.down(); _blocker.lock();
if (ts.timed_out) { if (ts.timed_out) {
timeout_reached = 1; timeout_reached = 1;
@ -461,28 +509,28 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
} }
else { else {
/* let's block infinitely */ /* 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: case SYSCALL_FORK:
@ -722,7 +770,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
catch (...) { PERR("Unexpected exception"); } 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; } operator bool () const { return _ptr != 0; }
bool operator== (const Shared_pointer &other)
{
return (_ptr == other._ptr);
}
template<typename To> template<typename To>
Shared_pointer<To> dynamic_pointer_cast() Shared_pointer<To> dynamic_pointer_cast()
{ {

View File

@ -32,8 +32,6 @@ namespace Noux {
private: private:
Terminal::Session_client _terminal; Terminal::Session_client _terminal;
Signal_context _read_avail_sig_ctx;
Signal_receiver _read_avail_sig_rec;
enum { FILENAME_MAX_LEN = 64 }; enum { FILENAME_MAX_LEN = 64 };
char _filename[FILENAME_MAX_LEN]; char _filename[FILENAME_MAX_LEN];
@ -74,11 +72,6 @@ namespace Noux {
/* wati for signal */ /* wati for signal */
sig_rec.wait_for_signal(); sig_rec.wait_for_signal();
sig_rec.dissolve(&sig_ctx); 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) 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); sysio->read_out.count = _terminal.read(sysio->read_out.chunk, sysio->read_in.count);
return true; return true;
} }
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { 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 */ /* Genode includes */
#include <util/string.h> #include <util/string.h>
#include <base/printf.h> #include <base/printf.h>
#include <os/ring_buffer.h>
#include <terminal_session/connection.h> #include <terminal_session/connection.h>
/* Noux includes */ /* Noux includes */
@ -33,6 +34,8 @@ namespace Noux {
enum Type { STDIN, STDOUT, STDERR } type; enum Type { STDIN, STDOUT, STDERR } type;
Ring_buffer<char, Sysio::CHUNK_SIZE + 1> read_buffer;
Terminal_io_channel(Terminal::Session &terminal, Type type, Terminal_io_channel(Terminal::Session &terminal, Type type,
Signal_receiver &sig_rec) Signal_receiver &sig_rec)
: terminal(terminal), sig_rec(sig_rec), eof(false), type(type) : terminal(terminal), sig_rec(sig_rec), eof(false), type(type)
@ -80,29 +83,32 @@ namespace Noux {
min(sysio->read_in.count, min(sysio->read_in.count,
sizeof(sysio->read_out.chunk)); sizeof(sysio->read_out.chunk));
sysio->read_out.count = for (sysio->read_out.count = 0;
terminal.read(sysio->read_out.chunk, max_count); (sysio->read_out.count < max_count) && !read_buffer.empty();
sysio->read_out.count++) {
/* scan received characters for EOF */ char c = read_buffer.get();
for (unsigned i = 0; i < sysio->read_out.count; i++) {
enum { EOF = 4 }; enum { EOF = 4 };
if (sysio->read_out.chunk[i] != EOF)
continue;
/* discard EOF character and everything that follows... */ if (c == EOF) {
sysio->read_out.count = i;
/* /*
* If EOF was the only character of the batch, the count has * If EOF was the only character of the batch, the count
* reached zero. In this case the read result indicates the EOF * has reached zero. In this case the read result indicates
* condition as is. However, if count is greater than zero, we * the EOF condition as is. However, if count is greater
* deliver the previous characters of the batch and return the * than zero, we deliver the previous characters of the
* zero result from the subsequent 'read' call. This condition * batch and return the zero result from the subsequent
* is tracked by the 'eof' variable. * 'read' call. This condition is tracked by the 'eof'
*/ * variable.
if (sysio->read_out.count > 0) */
eof = true; if (sysio->read_out.count > 0)
eof = true;
return true;
}
sysio->read_out.chunk[sysio->read_out.count] = c;
} }
return true; return true;
@ -150,7 +156,7 @@ namespace Noux {
* Unblock I/O channel if the terminal has new user input. Channels * Unblock I/O channel if the terminal has new user input. Channels
* otther than STDIN will never unblock. * otther than STDIN will never unblock.
*/ */
return (rd && (type == STDIN) && terminal.avail()); return (rd && (type == STDIN) && !read_buffer.empty());
} }
bool ioctl(Sysio *sysio) bool ioctl(Sysio *sysio)
@ -194,6 +200,21 @@ namespace Noux {
*/ */
void dispatch(unsigned) 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(); Io_channel::invoke_all_notifiers();
} }
}; };

View File

@ -21,18 +21,29 @@
namespace Noux { namespace Noux {
struct Vfs_io_channel : public Io_channel struct Vfs_io_channel : Io_channel, Signal_dispatcher_base
{ {
Vfs_handle *_fh; Vfs_handle *_fh;
Absolute_path _path; Absolute_path _path;
Absolute_path _leaf_path; Absolute_path _leaf_path;
Vfs_io_channel(char const *path, char const *leaf_path, Signal_receiver &_sig_rec;
Dir_file_system *root_dir, Vfs_handle *vfs_handle)
: _fh(vfs_handle), _path(path), _leaf_path(leaf_path) { }
~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) bool write(Sysio *sysio, size_t &count)
{ {
@ -158,12 +169,21 @@ namespace Noux {
bool check_unblock(bool rd, bool wr, bool ex) const bool check_unblock(bool rd, bool wr, bool ex) const
{ {
/* return _fh->fs()->check_unblock(_fh, rd, wr, ex);
* XXX For now, we use the TAR fs only, which never blocks.
* However, real file systems may block.
*/
return true;
} }
/**************************************
** 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 */ /* Genode includes */
#include <util/list.h> #include <util/list.h>
#include <base/semaphore.h> #include <base/lock.h>
namespace Noux { namespace Noux {
struct Wake_up_notifier : List<Wake_up_notifier>::Element struct Wake_up_notifier : List<Wake_up_notifier>::Element
{ {
Semaphore *semaphore; Lock *lock;
Wake_up_notifier(Semaphore *semaphore = 0) Wake_up_notifier(Lock *lock = 0)
: semaphore(semaphore) { } : lock(lock) { }
void wake_up() void wake_up()
{ {
if (semaphore) if (lock)
semaphore->up(); 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