diff --git a/ports/include/noux_session/sysio.h b/ports/include/noux_session/sysio.h index 5329a0846a..5355253899 100644 --- a/ports/include/noux_session/sysio.h +++ b/ports/include/noux_session/sysio.h @@ -19,6 +19,7 @@ #define _INCLUDE__NOUX_SESSION__SYSIO_H_ /* Genode includes */ +#include #include @@ -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 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; diff --git a/ports/run/noux_signals.run b/ports/run/noux_signals.run new file mode 100644 index 0000000000..d4c486130a --- /dev/null +++ b/ports/run/noux_signals.run @@ -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 { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +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 + diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index e9bddefc0c..12910942e9 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -45,6 +45,7 @@ #include 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; diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index a41271d2f6..f1594f7819 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -95,16 +96,22 @@ namespace Noux { class Child : public Rpc_object, public File_descriptor_registry, public Family_member, - public Destruct_queue::Element + public Destruct_queue::Element, + 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 + 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) + /** + * 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, + 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(¬ifier); - _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(¬ifier); } @@ -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) + { + 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, 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 = _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(); + } + }; }; diff --git a/ports/src/noux/file_descriptor_registry.h b/ports/src/noux/file_descriptor_registry.h index d15aba0871..74746e3c40 100644 --- a/ports/src/noux/file_descriptor_registry.h +++ b/ports/src/noux/file_descriptor_registry.h @@ -72,7 +72,7 @@ namespace Noux { * * \return noux file descriptor used for the I/O channel */ - int add_io_channel(Shared_pointer io_channel, int fd = -1) + virtual int add_io_channel(Shared_pointer 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_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(); - } + return _fds[fd].io_channel; } - void flush() + virtual void flush() { /* close all file descriptors */ for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++) diff --git a/ports/src/noux/file_io_service.h b/ports/src/noux/file_io_service.h index 41a06886ad..adc7cfd2e4 100644 --- a/ports/src/noux/file_io_service.h +++ b/ports/src/noux/file_io_service.h @@ -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) + { } }; } diff --git a/ports/src/noux/interrupt_handler.h b/ports/src/noux/interrupt_handler.h new file mode 100644 index 0000000000..d42ac6be61 --- /dev/null +++ b/ports/src/noux/interrupt_handler.h @@ -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 + +namespace Noux { + + struct Interrupt_handler : List::Element + { + virtual void handle_interrupt() = 0; + }; +}; + +#endif /* _NOUX__INTERRUPT_HANDLER__H_ */ + diff --git a/ports/src/noux/io_channel.h b/ports/src/noux/io_channel.h index 0042dd6f8b..e803353b7b 100644 --- a/ports/src/noux/io_channel.h +++ b/ports/src/noux/io_channel.h @@ -24,6 +24,7 @@ #include #include #include +#include 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 _notifiers; - Lock _notifiers_lock; + List _notifiers; + Lock _notifiers_lock; + List _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(); + } }; } diff --git a/ports/src/noux/io_receptor_registry.h b/ports/src/noux/io_receptor_registry.h index e34dea5759..7d5c9f10a0 100644 --- a/ports/src/noux/io_receptor_registry.h +++ b/ports/src/noux/io_receptor_registry.h @@ -16,7 +16,6 @@ /* Genode includes */ #include -#include #include @@ -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(); } }; diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 53c339c067..9394ce77f9 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -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 = _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 = _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 = _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 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 = io_channel_by_fd(fd); + notifiers[i].lock = &_blocker; + + io->register_wake_up_notifier(¬ifiers[i]); + } + + /** + * Register ourself at the Io_receptor_registry + * + * Each entry in the registry will be unblocked if an external + * event has happend, e.g. network I/O. + */ + + Io_receptor receptor(&_blocker); + io_receptor_registry()->register_receptor(&receptor); + + /* * Block for one action of the watched file descriptors */ @@ -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 = io_channel_by_fd(fd); - notifiers[i].semaphore = &_blocker; - io->register_wake_up_notifier(¬ifiers[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 = io_channel_by_fd(fd); - io->unregister_wake_up_notifier(¬ifiers[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 = io_channel_by_fd(fd); + io->unregister_wake_up_notifier(¬ifiers[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; } diff --git a/ports/src/noux/shared_pointer.h b/ports/src/noux/shared_pointer.h index e3fd11b670..d9b40822fd 100644 --- a/ports/src/noux/shared_pointer.h +++ b/ports/src/noux/shared_pointer.h @@ -145,6 +145,11 @@ namespace Noux { operator bool () const { return _ptr != 0; } + bool operator== (const Shared_pointer &other) + { + return (_ptr == other._ptr); + } + template Shared_pointer dynamic_pointer_cast() { diff --git a/ports/src/noux/terminal_file_system.h b/ports/src/noux/terminal_file_system.h index 2a8252b55e..378de17d34 100644 --- a/ports/src/noux/terminal_file_system.h +++ b/ports/src/noux/terminal_file_system.h @@ -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); + } }; } diff --git a/ports/src/noux/terminal_io_channel.h b/ports/src/noux/terminal_io_channel.h index 37edcf6d2c..8f3df1e338 100644 --- a/ports/src/noux/terminal_io_channel.h +++ b/ports/src/noux/terminal_io_channel.h @@ -17,6 +17,7 @@ /* Genode includes */ #include #include +#include #include /* Noux includes */ @@ -33,6 +34,8 @@ namespace Noux { enum Type { STDIN, STDOUT, STDERR } type; + Ring_buffer 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(); } }; diff --git a/ports/src/noux/vfs_io_channel.h b/ports/src/noux/vfs_io_channel.h index 7b6e9b0cdd..e7b42e0b89 100644 --- a/ports/src/noux/vfs_io_channel.h +++ b/ports/src/noux/vfs_io_channel.h @@ -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(); + } + }; } diff --git a/ports/src/noux/wake_up_notifier.h b/ports/src/noux/wake_up_notifier.h index f2533d4d88..002616172d 100644 --- a/ports/src/noux/wake_up_notifier.h +++ b/ports/src/noux/wake_up_notifier.h @@ -16,21 +16,21 @@ /* Genode includes */ #include -#include +#include namespace Noux { struct Wake_up_notifier : List::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(); } }; }; diff --git a/ports/src/test/noux_signals/main.cc b/ports/src/test/noux_signals/main.cc new file mode 100644 index 0000000000..d7f64ea7f2 --- /dev/null +++ b/ports/src/test/noux_signals/main.cc @@ -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 +#include +#include +#include +#include +#include + +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; +} + diff --git a/ports/src/test/noux_signals/target.mk b/ports/src/test/noux_signals/target.mk new file mode 100644 index 0000000000..b2c69b5746 --- /dev/null +++ b/ports/src/test/noux_signals/target.mk @@ -0,0 +1,3 @@ +TARGET = test-noux_signals +SRC_CC = main.cc +LIBS = libc libc_noux