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 ---");