genode/ports/src/noux/family_member.h
Christian Prochaska 27ff408985 Noux: POSIX signal improvements
- 'kill()' syscall added
- 'wait()' gets unblocked when a signal occurs
- syscalls can get called from a signal handler without corrupting the 'sysio' object
- the child's exit status gets correctly reported to 'wait()'
- SIGCHLD gets ignored by default
- pending signals survive 'execve()'

Fixes #1035.
2014-01-30 10:05:44 +01:00

181 lines
3.9 KiB
C++

/*
* \brief Helper for handling the relationship between Noux processes
* \author Norman Feske
* \date 2012-02-25
*/
/*
* Copyright (C) 2012-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__FAMILY_MEMBER_H_
#define _NOUX__FAMILY_MEMBER_H_
/* Genode includes */
#include <util/list.h>
#include <base/lock.h>
/* Noux includes */
#include <parent_exit.h>
#include <parent_execve.h>
namespace Noux {
class Family_member : public List<Family_member>::Element,
public Parent_exit,
public Parent_execve
{
private:
int const _pid;
Lock _lock;
List<Family_member> _list;
bool _has_exited;
int _exit_status;
protected:
/**
* Lock used for implementing blocking syscalls,
* i.e., select, wait4, ...
*/
Lock _blocker;
public:
Family_member(int pid)
: _pid(pid), _has_exited(false), _exit_status(0)
{ }
virtual ~Family_member() { }
int pid() const { return _pid; }
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);
}
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 exit(int exit_status)
{
_exit_status = exit_status;
_has_exited = true;
}
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()
{
/* reset the blocker lock to the 'locked' state */
_blocker.unlock();
_blocker.lock();
Family_member *result = poll4();
if (result)
return result;
_blocker.lock();
/* either a child exited or a signal occurred */
return poll4();
}
};
}
#endif /* _NOUX__FAMILY_MEMBER_H_ */