diff --git a/ports/run/noux_bash.run b/ports/run/noux_bash.run index 706e667911..cea4989806 100644 --- a/ports/run/noux_bash.run +++ b/ports/run/noux_bash.run @@ -95,8 +95,6 @@ append config { - - diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 34c851594b..fff915994b 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -93,7 +93,6 @@ namespace Noux { /* trigger exit of main event loop */ init_process_exited(); } - /* XXX destroy child */ } }; @@ -119,13 +118,101 @@ namespace Noux { }; - class Child : private Child_policy, - public Rpc_object, - public File_descriptor_registry + class Family_member : public List::Element { private: - int const _pid; + 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(); + } + + public: + + Family_member(int pid, Family_member *parent) + : _pid(pid), _parent(parent), _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; } + + /** + * Called by the parent at creation time of the process + */ + void insert(Family_member *member) + { + Lock::Guard guard(_lock); + _list.insert(member); + } + + /** + * Called by the parent from the return path of the wait4 syscall + */ + void remove(Family_member *member) + { + Lock::Guard guard(_lock); + _list.remove(member); + } + + /** + * Tell the parent that we exited + */ + void wakeup_parent(int exit_status) + { + _exit_status = exit_status; + _has_exited = true; + if (_parent) + _parent->_wakeup_wait4(); + } + + Family_member *poll4() + { + Lock::Guard guard(_lock); + + /* check if any of our children has exited */ + Family_member *curr = _list.first(); + for (; curr; curr = curr->next()) { + if (curr->_has_exited) + return curr; + } + return 0; + } + + /** + * Wait for the exit of any of our children + */ + Family_member *wait4() + { + for (;;) { + Family_member *result = poll4(); + if (result) + return result; + + _wait4_blocker.down(); + } + } + }; + + + class Child : private Child_policy, + public Rpc_object, + public File_descriptor_registry, + public Family_member + { + private: Signal_receiver *_sig_rec; @@ -281,6 +368,7 @@ namespace Noux { * true if the child is a fork from another child */ Child(char const *name, + Family_member *parent, int pid, Signal_receiver *sig_rec, Vfs *vfs, @@ -291,7 +379,7 @@ namespace Noux { Rpc_entrypoint &resources_ep, bool forked) : - _pid(pid), + Family_member(pid, parent), _sig_rec(sig_rec), _exit_dispatcher(this), _exit_context_cap(sig_rec->manage(&_exit_dispatcher)), @@ -324,6 +412,11 @@ namespace Noux { ~Child() { + PDBG("Destructing child %p", this); + + _sig_rec->dissolve(&_execve_cleanup_dispatcher); + _sig_rec->dissolve(&_exit_dispatcher); + /* XXX _binary_ds */ _entrypoint.dissolve(this); @@ -387,7 +480,12 @@ namespace Noux { void exit(int exit_value) { PINF("child %s exited with exit value %d", _name, exit_value); - Signal_transmitter(_exit_context_cap).submit(); + + wakeup_parent(exit_value); + + /* handle exit of the init process */ + if (parent() == 0) + Signal_transmitter(_exit_context_cap).submit(); } Ram_session *ref_ram_session() diff --git a/ports/src/noux/file_descriptor_registry.h b/ports/src/noux/file_descriptor_registry.h index caee66eed1..d4b0b31171 100644 --- a/ports/src/noux/file_descriptor_registry.h +++ b/ports/src/noux/file_descriptor_registry.h @@ -48,7 +48,7 @@ namespace Noux { return false; } - void _assign_fd(int fd, Shared_pointer io_channel) + void _assign_fd(int fd, Shared_pointer &io_channel) { _fds[fd].io_channel = io_channel; _fds[fd].allocated = true; diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 9af6dee967..e498d8d105 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -32,7 +32,7 @@ * - pipe * ;- read init binary from vfs * - import env into child (execve and fork) - * - shell + * ;- shell * - debug 'find' * - stacked file system infrastructure * - TMP file system @@ -75,7 +75,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) { if (verbose_syscall) Genode::printf("PID %d -> SYSCALL %s\n", - _pid, Noux::Session::syscall_name(sc)); + pid(), Noux::Session::syscall_name(sc)); try { switch (sc) { @@ -157,7 +157,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) char const *env = "PWD=\"/\""; char const *filename = _sysio->execve_in.filename; Child *child = new Child(filename, - _pid, + parent(), + pid(), _sig_rec, _vfs, Args(_sysio->execve_in.args, @@ -168,6 +169,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _resources.ep, false); + /* replace ourself by the new child at the parent */ + parent()->remove(this); + parent()->insert(child); + _assign_io_channels_to(child); /* signal main thread to remove ourself */ @@ -290,6 +295,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) * reusing the name of the parent. */ Child *child = new Child(name(), + this, new_pid, _sig_rec, _vfs, @@ -300,6 +306,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _resources.ep, true); + Family_member::insert(child); + _assign_io_channels_to(child); /* copy our address space into the new child */ @@ -319,25 +327,25 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_GETPID: { - _sysio->getpid_out.pid = _pid; + _sysio->getpid_out.pid = pid(); return true; } case SYSCALL_WAIT4: { - PINF("SYSCALL_WAIT4 called"); + Family_member *exited = _sysio->wait4_in.nohang ? poll4() : wait4(); - /* - * XXX check if one of out children exited - */ + if (exited) { + _sysio->wait4_out.pid = exited->pid(); + _sysio->wait4_out.status = exited->exit_status(); + Family_member::remove(exited); - if (!_sysio->wait4_in.nohang) - _blocker.down(); - - _sysio->wait4_out.pid = -1; - _sysio->wait4_out.status = 0; - - PINF("SYSCALL_WAIT4 returning"); + /* destroy 'Noux::Child' */ + destroy(Genode::env()->heap(), exited); + } else { + _sysio->wait4_out.pid = 0; + _sysio->wait4_out.status = 0; + } return true; } @@ -504,6 +512,7 @@ int main(int argc, char **argv) static Genode::Signal_receiver sig_rec; init_child = new Noux::Child(name_of_init_process(), + 0, pid_allocator()->alloc(), &sig_rec, &vfs, @@ -540,8 +549,9 @@ int main(int argc, char **argv) Signal_dispatcher *dispatcher = static_cast(signal.context()); - for (int i = 0; i < signal.num(); i++) - dispatcher->dispatch(); + if (dispatcher) + for (int i = 0; i < signal.num(); i++) + dispatcher->dispatch(); } PINF("-- exiting noux ---");