diff --git a/ports/include/noux_session/noux_session.h b/ports/include/noux_session/noux_session.h index 197bf56db..106108134 100644 --- a/ports/include/noux_session/noux_session.h +++ b/ports/include/noux_session/noux_session.h @@ -76,6 +76,7 @@ namespace Noux { SYSCALL_CLOCK_GETTIME, SYSCALL_UTIMES, SYSCALL_SYNC, + SYSCALL_KILL, SYSCALL_INVALID = -1 }; @@ -124,6 +125,7 @@ namespace Noux { NOUX_DECL_SYSCALL_NAME(CLOCK_GETTIME) NOUX_DECL_SYSCALL_NAME(UTIMES) NOUX_DECL_SYSCALL_NAME(SYNC) + NOUX_DECL_SYSCALL_NAME(KILL) case SYSCALL_INVALID: return 0; } return 0; diff --git a/ports/include/noux_session/sysio.h b/ports/include/noux_session/sysio.h index 4345a1f86..359eaf795 100644 --- a/ports/include/noux_session/sysio.h +++ b/ports/include/noux_session/sysio.h @@ -37,6 +37,7 @@ namespace Noux { /* signal numbers must match with libc signal numbers */ enum Signal { SIG_INT = 2, + SIG_CHLD = 20, }; enum { SIGNAL_QUEUE_SIZE = 32 }; @@ -369,6 +370,10 @@ namespace Noux { UTIMES_ERR_NOT_DIRECTORY, UTIMES_ERR_NO_PERM, UTIMES_ERR_READ_ONLY }; + enum Wait4_error { WAIT4_ERR_INTERRUPT }; + + enum Kill_error { KILL_ERR_SRCH }; + union { General_error general; Stat_error stat; @@ -395,6 +400,8 @@ namespace Noux { Socket_error socket; Clock_error clock; Utimes_error utimes; + Wait4_error wait4; + Kill_error kill; } error; union { @@ -508,6 +515,8 @@ namespace Noux { { }); SYSIO_DECL(sync, { }, { }); + + SYSIO_DECL(kill, { int pid; Signal sig; }, { }); }; }; }; diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index f1e22e886..67b75d082 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -41,6 +41,7 @@ #include #include #include +#include /** * There is a off_t typedef clash between sys/socket.h @@ -54,6 +55,7 @@ /* libc-internal includes */ #include + enum { verbose = false }; enum { verbose_signals = false }; @@ -100,27 +102,57 @@ 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]; +/* + * Array of signal handlers, initialized with 0 (SIG_DFL) + * TODO: preserve ignored signals across 'execve()' + */ +static struct sigaction signal_action[NSIG+1]; + +/* + * Signal mask functionality is not fully implemented yet. + * TODO: - actually block delivery of to be blocked signals + * - preserve signal mask across 'execve()' + */ +static sigset_t signal_mask; static bool noux_syscall(Noux::Session::Syscall opcode) { + /* + * Signal handlers might do syscalls themselves, so the 'sysio' object + * needs to get saved before and restored after calling the signal handler. + */ + Noux::Sysio saved_sysio; + bool ret = noux()->syscall(opcode); /* handle signals */ while (!sysio()->pending_signals.empty()) { Noux::Sysio::Signal signal = sysio()->pending_signals.get(); + if (verbose_signals) + PDBG("received signal %d", signal); if (signal_action[signal].sa_flags & SA_SIGINFO) { + memcpy(&saved_sysio, sysio(), sizeof(Noux::Sysio)); /* TODO: pass siginfo_t struct */ signal_action[signal].sa_sigaction(signal, 0, 0); + memcpy(sysio(), &saved_sysio, sizeof(Noux::Sysio)); } else { if (signal_action[signal].sa_handler == SIG_DFL) { - /* do nothing */ + switch (signal) { + case SIGCHLD: + /* ignore */ + break; + default: + /* terminate the process */ + exit((signal << 8) | EXIT_FAILURE); + } } else if (signal_action[signal].sa_handler == SIG_IGN) { /* do nothing */ - } else + } else { + memcpy(&saved_sysio, sysio(), sizeof(Noux::Sysio)); signal_action[signal].sa_handler(signal); + memcpy(sysio(), &saved_sysio, sizeof(Noux::Sysio)); + } } } @@ -532,12 +564,19 @@ 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)) { - PERR("wait4 error %d", sysio()->error.general); + switch (sysio()->error.wait4) { + case Noux::Sysio::WAIT4_ERR_INTERRUPT: errno = EINTR; break; + } return -1; } + /* + * The libc expects status information in bits 0..6 and the exit value + * in bits 8..15 (according to 'wait.h'). + */ if (status) - *status = sysio()->wait4_out.status; + *status = ((sysio()->wait4_out.status >> 8) & 0177) | + ((sysio()->wait4_out.status & 0xff) << 8); return sysio()->wait4_out.pid; } @@ -566,6 +605,25 @@ extern "C" void sync(void) } +extern "C" int kill(int pid, int sig) +{ + if (verbose_signals) + PDBG("pid = %d, sig = %d", pid, sig); + + sysio()->kill_in.pid = pid; + sysio()->kill_in.sig = Noux::Sysio::Signal(sig); + + if (!noux_syscall(Noux::Session::SYSCALL_KILL)) { + switch (sysio()->error.kill) { + case Noux::Sysio::KILL_ERR_SRCH: errno = ESRCH; break; + } + return -1; + } + + return 0; +} + + /******************** ** Time functions ** ********************/ @@ -636,9 +694,42 @@ extern "C" int utimes(const char* path, const struct timeval *times) extern "C" int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { - /* XXX todo */ - errno = ENOSYS; - return -1; + if (oldset) + *oldset = signal_mask; + + if (!set) + return 0; + + switch (how) { + case SIG_BLOCK: + for (int sig = 1; sig < NSIG; sig++) + if (sigismember(set, sig)) { + if (verbose_signals) + PDBG("signal %d requested to get blocked", sig); + sigaddset(&signal_mask, sig); + } + break; + case SIG_UNBLOCK: + for (int sig = 1; sig < NSIG; sig++) + if (sigismember(set, sig)) { + if (verbose_signals) + PDBG("signal %d requested to get unblocked", sig); + sigdelset(&signal_mask, sig); + } + break; + case SIG_SETMASK: + if (verbose_signals) + for (int sig = 1; sig < NSIG; sig++) + if (sigismember(set, sig)) + PDBG("signal %d requested to get blocked", sig); + signal_mask = *set; + break; + default: + errno = EINVAL; + return -1; + } + + return 0; } @@ -653,7 +744,7 @@ extern "C" int _sigaction(int signum, const struct sigaction *act, struct sigact if (verbose_signals) PDBG("signum = %d, handler = %p", signum, act ? act->sa_handler : 0); - if ((signum < 0) || (signum > SIGRTMAX)) { + if ((signum < 1) || (signum > NSIG)) { errno = EINVAL; return -1; } @@ -1951,6 +2042,8 @@ extern char **environ; __attribute__((constructor)) void init_libc_noux(void) { + sigemptyset(&signal_mask); + /* copy command-line arguments from 'args' ROM dataspace */ Genode::Rom_connection args_rom("args"); char *args = (char *)Genode::env()->rm_session()-> diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index f1594f781..5b2a8b21e 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -88,6 +90,11 @@ namespace Noux { */ Dataspace_capability ldso_ds_cap(); + /* + * Return lock for protecting the signal queue + */ + Genode::Lock &signal_lock(); + class Child; bool is_init_process(Child *child); @@ -101,17 +108,11 @@ namespace Noux { { private: - Signal_receiver *_sig_rec; + Parent_exit *_parent_exit; + Kill_broadcaster &_kill_broadcaster; + Parent_execve &_parent_execve; - /** - * 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. - */ - Lock _blocker; + Signal_receiver *_sig_rec; Allocator *_alloc; Destruct_queue &_destruct_queue; @@ -310,7 +311,9 @@ namespace Noux { * system */ Child(char const *binary_name, - Family_member *parent, + Parent_exit *parent_exit, + Kill_broadcaster &kill_broadcaster, + Parent_execve &parent_execve, int pid, Signal_receiver *sig_rec, Dir_file_system *root_dir, @@ -324,8 +327,11 @@ namespace Noux { Destruct_queue &destruct_queue, bool verbose) : - Family_member(pid, parent), + Family_member(pid), Destruct_queue::Element(destruct_alloc), + _parent_exit(parent_exit), + _kill_broadcaster(kill_broadcaster), + _parent_execve(parent_execve), _sig_rec(sig_rec), _destruct_queue(destruct_queue), _destruct_dispatcher(_destruct_queue, this), @@ -354,7 +360,7 @@ namespace Noux { _entrypoint, _local_noux_service, _local_rm_service, _local_rom_service, _parent_services, - *this, *this, _destruct_context_cap, + *this, parent_exit, *this, _destruct_context_cap, _resources.ram, verbose), _child(forked ? Dataspace_capability() : _elf._binary_ds, _resources.ram.cap(), _resources.cpu.cap(), @@ -457,8 +463,10 @@ namespace Noux { 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); + if (_is_the_only_fd_for_io_channel(fd, io_channel)) { + Io_channel_listener *l = new (env()->heap()) Io_channel_listener(this); + io_channel->register_interrupt_handler(l); + } return fd; } @@ -471,8 +479,11 @@ namespace Noux { * 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); + if (_is_the_only_fd_for_io_channel(fd, io_channel)) { + Io_channel_listener *l = io_channel->lookup_io_channel_listener(this); + io_channel->unregister_interrupt_handler(l); + Genode::destroy(env()->heap(), l); + } File_descriptor_registry::remove_io_channel(fd); } @@ -486,14 +497,14 @@ namespace Noux { } - /********************************* - ** Interrupt_handler interface ** - *********************************/ + /***************************** + ** Family_member interface ** + *****************************/ - void handle_interrupt() + void submit_signal(Noux::Sysio::Signal sig) { try { - _pending_signals.add(Sysio::SIG_INT); + _pending_signals.add(sig); } catch (Signal_queue::Overflow) { PERR("signal queue is full - signal dropped"); } @@ -501,6 +512,67 @@ namespace Noux { _blocker.unlock(); } + Family_member *do_execve(const char *filename, + Args const &args, + Sysio::Env const &env, + bool verbose) + { + Lock::Guard signal_lock_guard(signal_lock()); + + Child *child = new Child(filename, + _parent_exit, + _kill_broadcaster, + _parent_execve, + pid(), + _sig_rec, + root_dir(), + args, + env, + _cap_session, + _parent_services, + _resources.ep, + false, + Genode::env()->heap(), + _destruct_queue, + verbose); + + _assign_io_channels_to(child); + + /* move the signal queue */ + while (!_pending_signals.empty()) + child->_pending_signals.add(_pending_signals.get()); + + /* + * 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(); + + /* start executing the new process */ + child->start(); + + /* this child will be removed by the execve_finalization_dispatcher */ + + return child; + } + + /********************************* + ** Interrupt_handler interface ** + *********************************/ + + void handle_interrupt() + { + submit_signal(Sysio::SIG_INT); + } + }; }; diff --git a/ports/src/noux/child_policy.h b/ports/src/noux/child_policy.h index 17d17dbea..fe387596d 100644 --- a/ports/src/noux/child_policy.h +++ b/ports/src/noux/child_policy.h @@ -19,6 +19,7 @@ /* Noux includes */ #include +#include #include #include #include @@ -40,6 +41,7 @@ namespace Noux { Local_rom_service &_local_rom_service; Service_registry &_parent_services; Family_member &_family_member; + Parent_exit *_parent_exit; File_descriptor_registry &_file_descriptor_registry; Signal_context_capability _destruct_context_cap; Ram_session &_ref_ram_session; @@ -57,6 +59,7 @@ namespace Noux { Local_rom_service &local_rom_service, Service_registry &parent_services, Family_member &family_member, + Parent_exit *parent_exit, File_descriptor_registry &file_descriptor_registry, Signal_context_capability destruct_context_cap, Ram_session &ref_ram_session, @@ -72,6 +75,7 @@ namespace Noux { _local_rom_service(local_rom_service), _parent_services(parent_services), _family_member(family_member), + _parent_exit(parent_exit), _file_descriptor_registry(file_descriptor_registry), _destruct_context_cap(destruct_context_cap), _ref_ram_session(ref_ram_session), @@ -130,11 +134,15 @@ namespace Noux { */ _file_descriptor_registry.flush(); - _family_member.wakeup_parent(exit_value); + _family_member.exit(exit_value); - /* handle exit of the init process */ - if (_family_member.parent() == 0) + /* notify the parent */ + if (_parent_exit) + _parent_exit->exit_child(); + else { + /* handle exit of the init process */ Signal_transmitter(_destruct_context_cap).submit(); + } } Ram_session *ref_ram_session() diff --git a/ports/src/noux/family_member.h b/ports/src/noux/family_member.h index 66ed930e1..73aff942e 100644 --- a/ports/src/noux/family_member.h +++ b/ports/src/noux/family_member.h @@ -18,34 +18,42 @@ #include #include +/* Noux includes */ +#include +#include + namespace Noux { - class Family_member : public List::Element + class Family_member : public List::Element, + public Parent_exit, + public Parent_execve { private: int const _pid; Lock _lock; List _list; - Family_member * const _parent; bool _has_exited; int _exit_status; - Semaphore _wait4_blocker; - void _wakeup_wait4() { _wait4_blocker.up(); } + protected: + + /** + * Lock used for implementing blocking syscalls, + * i.e., select, wait4, ... + */ + Lock _blocker; public: - Family_member(int pid, Family_member *parent) - : _pid(pid), _parent(parent), _has_exited(false), _exit_status(0) + Family_member(int pid) + : _pid(pid), _has_exited(false), _exit_status(0) { } virtual ~Family_member() { } int pid() const { return _pid; } - Family_member *parent() { return _parent; } - int exit_status() const { return _exit_status; } /** @@ -66,15 +74,73 @@ namespace Noux { _list.remove(member); } + virtual void submit_signal(Noux::Sysio::Signal sig) = 0; + + /** + * Called by the parent (originates from Kill_broadcaster) + */ + bool deliver_kill(int pid, Noux::Sysio::Signal sig) + { + Lock::Guard guard(_lock); + + if (pid == _pid) { + submit_signal(sig); + return true; + } + + bool result = false; + + for (Family_member *child = _list.first(); child; child = child->next()) + if (child->deliver_kill(pid, sig)) + result = true; + + return result; + } + + /** + * Parent_exit interface + */ + + /* Called by the child on the parent (via Parent_exit) */ + void exit_child() + { + submit_signal(Sysio::Signal::SIG_CHLD); + } + + /** + * Parent_execve interface + */ + + /* Called by the parent from 'execve_child()' */ + virtual Family_member *do_execve(const char *filename, + Args const &args, + Sysio::Env const &env, + bool verbose) = 0; + + /* Called by the child on the parent (via Parent_execve) */ + void execve_child(Family_member &child, + const char *filename, + Args const &args, + Sysio::Env const &env, + bool verbose) + { + Lock::Guard guard(_lock); + Family_member *new_child = child.do_execve(filename, + args, + env, + verbose); + _list.insert(new_child); + _list.remove(&child); + } + + /** * Tell the parent that we exited */ - void wakeup_parent(int exit_status) + void exit(int exit_status) { _exit_status = exit_status; _has_exited = true; - if (_parent) - _parent->_wakeup_wait4(); } Family_member *poll4() @@ -95,13 +161,18 @@ namespace Noux { */ Family_member *wait4() { - for (;;) { - Family_member *result = poll4(); - if (result) - return result; + /* reset the blocker lock to the 'locked' state */ + _blocker.unlock(); + _blocker.lock(); - _wait4_blocker.down(); - } + Family_member *result = poll4(); + if (result) + return result; + + _blocker.lock(); + + /* either a child exited or a signal occurred */ + return poll4(); } }; } diff --git a/ports/src/noux/interrupt_handler.h b/ports/src/noux/interrupt_handler.h index d42ac6be6..e900aceb3 100644 --- a/ports/src/noux/interrupt_handler.h +++ b/ports/src/noux/interrupt_handler.h @@ -14,12 +14,9 @@ #ifndef _NOUX__INTERRUPT_HANDLER__H_ #define _NOUX__INTERRUPT_HANDLER__H_ -/* Genode includes */ -#include - namespace Noux { - struct Interrupt_handler : List::Element + struct Interrupt_handler { virtual void handle_interrupt() = 0; }; diff --git a/ports/src/noux/io_channel.h b/ports/src/noux/io_channel.h index 38778453f..52cd12c01 100644 --- a/ports/src/noux/io_channel.h +++ b/ports/src/noux/io_channel.h @@ -24,10 +24,14 @@ #include #include #include -#include +#include namespace Noux { + class Terminal_io_channel; + + extern Genode::Lock &signal_lock(); + /** * Input/output channel backend that is used for calling * different methos which does not belong to the original @@ -53,10 +57,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 _interrupt_handlers; - Lock _interrupt_handlers_lock; + List _notifiers; + Lock _notifiers_lock; + List _interrupt_handlers; + Lock _interrupt_handlers_lock; public: @@ -140,7 +144,7 @@ namespace Noux { * 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) + void register_interrupt_handler(Io_channel_listener *handler) { Lock::Guard guard(_interrupt_handlers_lock); @@ -150,23 +154,37 @@ namespace Noux { /** * Unregister interrupt handler */ - void unregister_interrupt_handler(Interrupt_handler *handler) + void unregister_interrupt_handler(Io_channel_listener *handler) { Lock::Guard guard(_interrupt_handlers_lock); _interrupt_handlers.remove(handler); } + /** + * Find the 'Io_channel_listener' object which contains the given + * 'Interrupt_handler' pointer + */ + Io_channel_listener *lookup_io_channel_listener(Interrupt_handler *handler) + { + for (Io_channel_listener *l = _interrupt_handlers.first(); + l; l = l->next()) + if (l->object() == handler) + return l; + return 0; + } + /** * Tell all registered handlers about an interrupt event */ void invoke_all_interrupt_handlers() { + Lock::Guard signal_lock_guard(signal_lock()); Lock::Guard guard(_interrupt_handlers_lock); - for (Interrupt_handler *h = _interrupt_handlers.first(); - h; h = h->next()) - h->handle_interrupt(); + for (Io_channel_listener *l = _interrupt_handlers.first(); + l; l = l->next()) + l->object()->handle_interrupt(); } /** diff --git a/ports/src/noux/io_channel_listener.h b/ports/src/noux/io_channel_listener.h new file mode 100644 index 000000000..f2cdf1013 --- /dev/null +++ b/ports/src/noux/io_channel_listener.h @@ -0,0 +1,28 @@ +/* + * \brief IO channel listener + * \author Christian Prochaska + * \date 2014-01-23 + */ + +/* + * Copyright (C) 2014 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__IO_CHANNEL_LISTENER__H_ +#define _NOUX__IO_CHANNEL_LISTENER__H_ + +/* Genode includes */ +#include + +/* Noux includes */ +#include + +namespace Noux { + typedef List_element Io_channel_listener; +} + +#endif /* _NOUX__IO_CHANNEL_LISTENER__H_ */ + diff --git a/ports/src/noux/kill_broadcaster.h b/ports/src/noux/kill_broadcaster.h new file mode 100644 index 000000000..0902ada03 --- /dev/null +++ b/ports/src/noux/kill_broadcaster.h @@ -0,0 +1,29 @@ +/* + * \brief Kill_broadcaster interface + * \author Christian Prochaska + * \date 2014-01-13 + */ + +/* + * Copyright (C) 2014 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__KILL_BROADCASTER__H_ +#define _NOUX__KILL_BROADCASTER__H_ + +/* Noux includes */ +#include + +namespace Noux { + + struct Kill_broadcaster + { + virtual bool kill(int pid, Noux::Sysio::Signal sig) = 0; + }; +}; + +#endif /* _NOUX__KILL_BROADCASTER__H_ */ + diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index b60008c52..7c4adad59 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -29,6 +29,7 @@ #include #include #include +#include static const bool verbose_quota = false; @@ -46,7 +47,6 @@ namespace Noux { extern void init_network(); - /** * Timeout thread for SYSCALL_SELECT */ @@ -219,7 +219,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) size_t path_len = strlen(_sysio->stat_in.path); uint32_t path_hash = hash_path(_sysio->stat_in.path, path_len); - bool result = root_dir()->stat(_sysio, _sysio->stat_in.path); + result = root_dir()->stat(_sysio, _sysio->stat_in.path); /** * Instead of using the uid/gid given by the actual file system @@ -232,14 +232,14 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->stat_out.st.inode = path_hash; } - return result; + break; } case SYSCALL_FSTAT: { Shared_pointer io = _lookup_channel(_sysio->fstat_in.fd); - bool result = io->fstat(_sysio); + result = io->fstat(_sysio); if (result) { Sysio::Path path; @@ -255,7 +255,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) } } - return result; + break; } case SYSCALL_FCNTL: @@ -265,16 +265,18 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) /* we assume that there is only the close-on-execve flag */ _lookup_channel(_sysio->fcntl_in.fd)->close_on_execve = !!_sysio->fcntl_in.long_arg; - return true; + result = true; + break; } - return _lookup_channel(_sysio->fcntl_in.fd)->fcntl(_sysio); + result = _lookup_channel(_sysio->fcntl_in.fd)->fcntl(_sysio); + break; case SYSCALL_OPEN: { Vfs_handle *vfs_handle = root_dir()->open(_sysio, _sysio->open_in.path); if (!vfs_handle) - return false; + break; char const *leaf_path = root_dir()->leaf_path(_sysio->open_in.path); @@ -294,26 +296,31 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) Genode::env()->heap()); _sysio->open_out.fd = add_io_channel(channel); - return true; + result = true; + break; } case SYSCALL_CLOSE: { remove_io_channel(_sysio->close_in.fd); - return true; + result = true; + break; } case SYSCALL_IOCTL: - return _lookup_channel(_sysio->ioctl_in.fd)->ioctl(_sysio); + result = _lookup_channel(_sysio->ioctl_in.fd)->ioctl(_sysio); + break; case SYSCALL_LSEEK: - return _lookup_channel(_sysio->lseek_in.fd)->lseek(_sysio); + result = _lookup_channel(_sysio->lseek_in.fd)->lseek(_sysio); + break; case SYSCALL_DIRENT: - return _lookup_channel(_sysio->dirent_in.fd)->dirent(_sysio); + result = _lookup_channel(_sysio->dirent_in.fd)->dirent(_sysio); + break; case SYSCALL_EXECVE: { @@ -327,7 +334,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) if (!binary_ds.valid()) { _sysio->error.execve = Sysio::EXECVE_NONEXISTENT; - return false; + break; } Child_envexecve_in.args)> @@ -340,57 +347,28 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) if (!binary_ds.valid()) { _sysio->error.execve = Sysio::EXECVE_NONEXISTENT; - return false; + break; } root_dir()->release(child_env.binary_name(), binary_ds); try { - Child *child = new Child(child_env.binary_name(), - parent(), - pid(), - _sig_rec, - root_dir(), - child_env.args(), - child_env.env(), - _cap_session, - _parent_services, - _resources.ep, - false, - env()->heap(), - _destruct_queue, - verbose); - - /* replace ourself by the new child at the parent */ - parent()->remove(this); - parent()->insert(child); - - _assign_io_channels_to(child); + _parent_execve.execve_child(*this, + child_env.binary_name(), + child_env.args(), + child_env.env(), + verbose); /* - * 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. + * 'return' instead of 'break' to skip possible signal delivery, + * which might cause the old child process to exit itself */ - flush(); - - /* signal main thread to remove ourself */ - Genode::Signal_transmitter(_destruct_context_cap).submit(); - - /* start executing the new process */ - child->start(); - - /* this child will be removed by the execve_finalization_dispatcher */ return true; } catch (Child::Binary_does_not_exist) { _sysio->error.execve = Sysio::EXECVE_NONEXISTENT; } - return false; + break; } case SYSCALL_SELECT: @@ -593,6 +571,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) */ Child *child = new Child(_child_policy.name(), this, + _kill_broadcaster, + *this, new_pid, _sig_rec, root_dir(), @@ -622,7 +602,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->fork_out.pid = new_pid; - return true; + result = true; + break; } case SYSCALL_GETPID: @@ -645,10 +626,16 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) static_cast(exited)->submit_exit_signal(); } else { - _sysio->wait4_out.pid = 0; - _sysio->wait4_out.status = 0; + if (_sysio->wait4_in.nohang) { + _sysio->wait4_out.pid = 0; + _sysio->wait4_out.status = 0; + } else { + _sysio->error.wait4 = Sysio::WAIT4_ERR_INTERRUPT; + break; + } } - return true; + result = true; + break; } case SYSCALL_PIPE: @@ -663,7 +650,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->pipe_out.fd[0] = add_io_channel(pipe_source); _sysio->pipe_out.fd[1] = add_io_channel(pipe_sink); - return true; + result = true; + break; } case SYSCALL_DUP2: @@ -673,30 +661,36 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->dup2_out.fd = fd; - return true; + result = true; + break; } case SYSCALL_UNLINK: - return root_dir()->unlink(_sysio, _sysio->unlink_in.path); + result = root_dir()->unlink(_sysio, _sysio->unlink_in.path); + break; case SYSCALL_READLINK: - return root_dir()->readlink(_sysio, _sysio->readlink_in.path); + result = root_dir()->readlink(_sysio, _sysio->readlink_in.path); + break; case SYSCALL_RENAME: - return root_dir()->rename(_sysio, _sysio->rename_in.from_path, - _sysio->rename_in.to_path); + result = root_dir()->rename(_sysio, _sysio->rename_in.from_path, + _sysio->rename_in.to_path); + break; case SYSCALL_MKDIR: - return root_dir()->mkdir(_sysio, _sysio->mkdir_in.path); + result = root_dir()->mkdir(_sysio, _sysio->mkdir_in.path); + break; case SYSCALL_SYMLINK: - return root_dir()->symlink(_sysio, _sysio->symlink_in.newpath); + result = root_dir()->symlink(_sysio, _sysio->symlink_in.newpath); + break; case SYSCALL_USERINFO: { @@ -705,7 +699,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->userinfo_out.uid = Noux::user_info()->uid; _sysio->userinfo_out.gid = Noux::user_info()->gid; - return true; + result = true; + break; } /* @@ -713,16 +708,23 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) * got a unknown uid. */ if (_sysio->userinfo_in.uid != Noux::user_info()->uid) - return false; + break; - Genode::memcpy(_sysio->userinfo_out.name, Noux::user_info()->name, sizeof(Noux::user_info()->name)); - Genode::memcpy(_sysio->userinfo_out.shell, Noux::user_info()->shell, sizeof(Noux::user_info()->shell)); - Genode::memcpy(_sysio->userinfo_out.home, Noux::user_info()->home, sizeof(Noux::user_info()->home)); + Genode::memcpy(_sysio->userinfo_out.name, + Noux::user_info()->name, + sizeof(Noux::user_info()->name)); + Genode::memcpy(_sysio->userinfo_out.shell, + Noux::user_info()->shell, + sizeof(Noux::user_info()->shell)); + Genode::memcpy(_sysio->userinfo_out.home, + Noux::user_info()->home, + sizeof(Noux::user_info()->home)); _sysio->userinfo_out.uid = user_info()->uid; _sysio->userinfo_out.gid = user_info()->gid; - return true; + result = true; + break; } case SYSCALL_GETTIMEOFDAY: @@ -741,7 +743,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->gettimeofday_out.sec = (time / 1000); _sysio->gettimeofday_out.usec = (time % 1000) * 1000; - return true; + result = true; + break; } case SYSCALL_CLOCK_GETTIME: @@ -759,7 +762,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->clock_gettime_out.sec = (time / 1000); _sysio->clock_gettime_out.nsec = 0; - return true; + result = true; + break; } default: @@ -768,12 +772,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _sysio->clock_gettime_out.nsec = 0; _sysio->error.clock = Sysio::CLOCK_ERR_INVALID; - return false; + break; } } - return false; + break; } @@ -786,13 +790,26 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) * But we return true anyway to keep certain programs, e.g. make * happy. */ - return true; + result = true; + break; } case SYSCALL_SYNC: { root_dir()->sync(); - return true; + result = true; + break; + } + + case SYSCALL_KILL: + { + if (_kill_broadcaster.kill(_sysio->kill_in.pid, + _sysio->kill_in.sig)) + result = true; + else + _sysio->error.kill = Sysio::KILL_ERR_SRCH; + + break; } case SYSCALL_SOCKET: @@ -809,7 +826,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_SHUTDOWN: case SYSCALL_CONNECT: - return _syscall_net(sc); + result = _syscall_net(sc); + break; case SYSCALL_INVALID: break; } @@ -823,8 +841,9 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) /* handle signals which might have occured */ while (!_pending_signals.empty() && - (_sysio->pending_signals.avail_capacity() > 0)) + (_sysio->pending_signals.avail_capacity() > 0)) { _sysio->pending_signals.add(_pending_signals.get()); + } return result; } @@ -956,6 +975,20 @@ Genode::Dataspace_capability Noux::ldso_ds_cap() } +/* + * This lock is needed to delay the insertion of signals into a child object. + * This is necessary during an 'execve()' syscall, when signals get copied from + * the old child object to the new one. Without the lock, an IO channel could + * insert a signal into both objects, which could lead to a duplicated signal + * in the new child object. + */ +Genode::Lock &Noux::signal_lock() +{ + static Genode::Lock inst; + return inst; +} + + void *operator new (Genode::size_t size) { return Genode::env()->heap()->alloc(size); } @@ -1007,8 +1040,22 @@ int main(int argc, char **argv) static Genode::Signal_receiver sig_rec; static Destruct_queue destruct_queue; + struct Kill_broadcaster_implementation : Kill_broadcaster + { + Family_member *init_process; + + bool kill(int pid, Noux::Sysio::Signal sig) + { + return init_process->deliver_kill(pid, sig); + } + }; + + static Kill_broadcaster_implementation kill_broadcaster; + init_child = new Noux::Child(name_of_init_process(), 0, + kill_broadcaster, + *init_child, pid_allocator()->alloc(), &sig_rec, &root_dir, @@ -1022,6 +1069,8 @@ int main(int argc, char **argv) destruct_queue, verbose); + kill_broadcaster.init_process = init_child; + /* * I/O channels must be dynamically allocated to handle cases where the * init program closes one of these. diff --git a/ports/src/noux/parent_execve.h b/ports/src/noux/parent_execve.h new file mode 100644 index 000000000..d8adbe141 --- /dev/null +++ b/ports/src/noux/parent_execve.h @@ -0,0 +1,36 @@ +/* + * \brief Parent_execve interface + * \author Christian Prochaska + * \date 2014-01-13 + */ + +/* + * Copyright (C) 2014 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__PARENT_EXECVE__H_ +#define _NOUX__PARENT_EXECVE__H_ + +/* Noux includes */ +#include + +namespace Noux { + + struct Family_member; + + struct Parent_execve + { + virtual void execve_child(Family_member &child, + const char *filename, + Args const &args, + Sysio::Env const &env, + bool verbose) = 0; + }; + +}; + +#endif /* _NOUX__PARENT_EXECVE__H_ */ + diff --git a/ports/src/noux/parent_exit.h b/ports/src/noux/parent_exit.h new file mode 100644 index 000000000..c6eaabb43 --- /dev/null +++ b/ports/src/noux/parent_exit.h @@ -0,0 +1,33 @@ +/* + * \brief Parent_exit interface + * \author Christian Prochaska + * \date 2014-01-16 + */ + +/* + * Copyright (C) 2014 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__PARENT_EXIT__H_ +#define _NOUX__PARENT_EXIT__H_ + +namespace Noux { + + struct Family_member; + + struct Parent_exit + { + /* + * Handle the exiting of a child + */ + + virtual void exit_child() = 0; + }; + +}; + +#endif /* _NOUX__PARENT_EXIT__H_ */ +