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.
This commit is contained in:
Christian Prochaska 2014-01-23 17:21:35 +01:00 committed by Christian Helmuth
parent 7876dfcb5e
commit 27ff408985
13 changed files with 589 additions and 144 deletions

View File

@ -76,6 +76,7 @@ namespace Noux {
SYSCALL_CLOCK_GETTIME, SYSCALL_CLOCK_GETTIME,
SYSCALL_UTIMES, SYSCALL_UTIMES,
SYSCALL_SYNC, SYSCALL_SYNC,
SYSCALL_KILL,
SYSCALL_INVALID = -1 SYSCALL_INVALID = -1
}; };
@ -124,6 +125,7 @@ namespace Noux {
NOUX_DECL_SYSCALL_NAME(CLOCK_GETTIME) NOUX_DECL_SYSCALL_NAME(CLOCK_GETTIME)
NOUX_DECL_SYSCALL_NAME(UTIMES) NOUX_DECL_SYSCALL_NAME(UTIMES)
NOUX_DECL_SYSCALL_NAME(SYNC) NOUX_DECL_SYSCALL_NAME(SYNC)
NOUX_DECL_SYSCALL_NAME(KILL)
case SYSCALL_INVALID: return 0; case SYSCALL_INVALID: return 0;
} }
return 0; return 0;

View File

@ -37,6 +37,7 @@ namespace Noux {
/* signal numbers must match with libc signal numbers */ /* signal numbers must match with libc signal numbers */
enum Signal { enum Signal {
SIG_INT = 2, SIG_INT = 2,
SIG_CHLD = 20,
}; };
enum { SIGNAL_QUEUE_SIZE = 32 }; enum { SIGNAL_QUEUE_SIZE = 32 };
@ -369,6 +370,10 @@ namespace Noux {
UTIMES_ERR_NOT_DIRECTORY, UTIMES_ERR_NO_PERM, UTIMES_ERR_NOT_DIRECTORY, UTIMES_ERR_NO_PERM,
UTIMES_ERR_READ_ONLY }; UTIMES_ERR_READ_ONLY };
enum Wait4_error { WAIT4_ERR_INTERRUPT };
enum Kill_error { KILL_ERR_SRCH };
union { union {
General_error general; General_error general;
Stat_error stat; Stat_error stat;
@ -395,6 +400,8 @@ namespace Noux {
Socket_error socket; Socket_error socket;
Clock_error clock; Clock_error clock;
Utimes_error utimes; Utimes_error utimes;
Wait4_error wait4;
Kill_error kill;
} error; } error;
union { union {
@ -508,6 +515,8 @@ namespace Noux {
{ }); { });
SYSIO_DECL(sync, { }, { }); SYSIO_DECL(sync, { }, { });
SYSIO_DECL(kill, { int pid; Signal sig; }, { });
}; };
}; };
}; };

View File

@ -41,6 +41,7 @@
#include <termios.h> #include <termios.h>
#include <pwd.h> #include <pwd.h>
#include <string.h> #include <string.h>
#include <signal.h>
/** /**
* There is a off_t typedef clash between sys/socket.h * There is a off_t typedef clash between sys/socket.h
@ -54,6 +55,7 @@
/* libc-internal includes */ /* libc-internal includes */
#include <libc_mem_alloc.h> #include <libc_mem_alloc.h>
enum { verbose = false }; enum { verbose = false };
enum { verbose_signals = false }; enum { verbose_signals = false };
@ -100,27 +102,57 @@ Noux::Session *noux() { return noux_connection()->session(); }
Noux::Sysio *sysio() { return noux_connection()->sysio(); } 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) 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); bool ret = noux()->syscall(opcode);
/* handle signals */ /* handle signals */
while (!sysio()->pending_signals.empty()) { while (!sysio()->pending_signals.empty()) {
Noux::Sysio::Signal signal = sysio()->pending_signals.get(); Noux::Sysio::Signal signal = sysio()->pending_signals.get();
if (verbose_signals)
PDBG("received signal %d", signal);
if (signal_action[signal].sa_flags & SA_SIGINFO) { if (signal_action[signal].sa_flags & SA_SIGINFO) {
memcpy(&saved_sysio, sysio(), sizeof(Noux::Sysio));
/* TODO: pass siginfo_t struct */ /* TODO: pass siginfo_t struct */
signal_action[signal].sa_sigaction(signal, 0, 0); signal_action[signal].sa_sigaction(signal, 0, 0);
memcpy(sysio(), &saved_sysio, sizeof(Noux::Sysio));
} else { } else {
if (signal_action[signal].sa_handler == SIG_DFL) { 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) { } else if (signal_action[signal].sa_handler == SIG_IGN) {
/* do nothing */ /* do nothing */
} else } else {
memcpy(&saved_sysio, sysio(), sizeof(Noux::Sysio));
signal_action[signal].sa_handler(signal); 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.pid = pid;
sysio()->wait4_in.nohang = !!(options & WNOHANG); 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); switch (sysio()->error.wait4) {
case Noux::Sysio::WAIT4_ERR_INTERRUPT: errno = EINTR; break;
}
return -1; 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) 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; 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 ** ** 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) extern "C" int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
{ {
/* XXX todo */ if (oldset)
errno = ENOSYS; *oldset = signal_mask;
return -1;
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) if (verbose_signals)
PDBG("signum = %d, handler = %p", signum, act ? act->sa_handler : 0); PDBG("signum = %d, handler = %p", signum, act ? act->sa_handler : 0);
if ((signum < 0) || (signum > SIGRTMAX)) { if ((signum < 1) || (signum > NSIG)) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
@ -1951,6 +2042,8 @@ extern char **environ;
__attribute__((constructor)) __attribute__((constructor))
void init_libc_noux(void) void init_libc_noux(void)
{ {
sigemptyset(&signal_mask);
/* copy command-line arguments from 'args' ROM dataspace */ /* copy command-line arguments from 'args' ROM dataspace */
Genode::Rom_connection args_rom("args"); Genode::Rom_connection args_rom("args");
char *args = (char *)Genode::env()->rm_session()-> char *args = (char *)Genode::env()->rm_session()->

View File

@ -33,6 +33,8 @@
#include <destruct_queue.h> #include <destruct_queue.h>
#include <destruct_dispatcher.h> #include <destruct_dispatcher.h>
#include <interrupt_handler.h> #include <interrupt_handler.h>
#include <kill_broadcaster.h>
#include <parent_execve.h>
#include <local_cpu_service.h> #include <local_cpu_service.h>
#include <local_ram_service.h> #include <local_ram_service.h>
@ -88,6 +90,11 @@ namespace Noux {
*/ */
Dataspace_capability ldso_ds_cap(); Dataspace_capability ldso_ds_cap();
/*
* Return lock for protecting the signal queue
*/
Genode::Lock &signal_lock();
class Child; class Child;
bool is_init_process(Child *child); bool is_init_process(Child *child);
@ -101,17 +108,11 @@ namespace Noux {
{ {
private: private:
Signal_receiver *_sig_rec; Parent_exit *_parent_exit;
Kill_broadcaster &_kill_broadcaster;
Parent_execve &_parent_execve;
/** Signal_receiver *_sig_rec;
* 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;
Allocator *_alloc; Allocator *_alloc;
Destruct_queue &_destruct_queue; Destruct_queue &_destruct_queue;
@ -310,7 +311,9 @@ namespace Noux {
* system * system
*/ */
Child(char const *binary_name, Child(char const *binary_name,
Family_member *parent, Parent_exit *parent_exit,
Kill_broadcaster &kill_broadcaster,
Parent_execve &parent_execve,
int pid, int pid,
Signal_receiver *sig_rec, Signal_receiver *sig_rec,
Dir_file_system *root_dir, Dir_file_system *root_dir,
@ -324,8 +327,11 @@ namespace Noux {
Destruct_queue &destruct_queue, Destruct_queue &destruct_queue,
bool verbose) bool verbose)
: :
Family_member(pid, parent), Family_member(pid),
Destruct_queue::Element<Child>(destruct_alloc), Destruct_queue::Element<Child>(destruct_alloc),
_parent_exit(parent_exit),
_kill_broadcaster(kill_broadcaster),
_parent_execve(parent_execve),
_sig_rec(sig_rec), _sig_rec(sig_rec),
_destruct_queue(destruct_queue), _destruct_queue(destruct_queue),
_destruct_dispatcher(_destruct_queue, this), _destruct_dispatcher(_destruct_queue, this),
@ -354,7 +360,7 @@ namespace Noux {
_entrypoint, _local_noux_service, _entrypoint, _local_noux_service,
_local_rm_service, _local_rom_service, _local_rm_service, _local_rom_service,
_parent_services, _parent_services,
*this, *this, _destruct_context_cap, *this, parent_exit, *this, _destruct_context_cap,
_resources.ram, verbose), _resources.ram, verbose),
_child(forked ? Dataspace_capability() : _elf._binary_ds, _child(forked ? Dataspace_capability() : _elf._binary_ds,
_resources.ram.cap(), _resources.cpu.cap(), _resources.ram.cap(), _resources.cpu.cap(),
@ -457,8 +463,10 @@ namespace Noux {
fd = File_descriptor_registry::add_io_channel(io_channel, fd); fd = File_descriptor_registry::add_io_channel(io_channel, fd);
/* Register the interrupt handler only once per IO channel */ /* Register the interrupt handler only once per IO channel */
if (_is_the_only_fd_for_io_channel(fd, io_channel)) if (_is_the_only_fd_for_io_channel(fd, io_channel)) {
io_channel->register_interrupt_handler(this); Io_channel_listener *l = new (env()->heap()) Io_channel_listener(this);
io_channel->register_interrupt_handler(l);
}
return fd; return fd;
} }
@ -471,8 +479,11 @@ namespace Noux {
* Unregister the interrupt handler only if there are no other * Unregister the interrupt handler only if there are no other
* file descriptors associated with the IO channel. * file descriptors associated with the IO channel.
*/ */
if (_is_the_only_fd_for_io_channel(fd, io_channel)) if (_is_the_only_fd_for_io_channel(fd, io_channel)) {
io_channel->unregister_interrupt_handler(this); 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); 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 { try {
_pending_signals.add(Sysio::SIG_INT); _pending_signals.add(sig);
} catch (Signal_queue::Overflow) { } catch (Signal_queue::Overflow) {
PERR("signal queue is full - signal dropped"); PERR("signal queue is full - signal dropped");
} }
@ -501,6 +512,67 @@ namespace Noux {
_blocker.unlock(); _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);
}
}; };
}; };

View File

@ -19,6 +19,7 @@
/* Noux includes */ /* Noux includes */
#include <family_member.h> #include <family_member.h>
#include <parent_exit.h>
#include <file_descriptor_registry.h> #include <file_descriptor_registry.h>
#include <local_noux_service.h> #include <local_noux_service.h>
#include <local_rm_service.h> #include <local_rm_service.h>
@ -40,6 +41,7 @@ namespace Noux {
Local_rom_service &_local_rom_service; Local_rom_service &_local_rom_service;
Service_registry &_parent_services; Service_registry &_parent_services;
Family_member &_family_member; Family_member &_family_member;
Parent_exit *_parent_exit;
File_descriptor_registry &_file_descriptor_registry; File_descriptor_registry &_file_descriptor_registry;
Signal_context_capability _destruct_context_cap; Signal_context_capability _destruct_context_cap;
Ram_session &_ref_ram_session; Ram_session &_ref_ram_session;
@ -57,6 +59,7 @@ namespace Noux {
Local_rom_service &local_rom_service, Local_rom_service &local_rom_service,
Service_registry &parent_services, Service_registry &parent_services,
Family_member &family_member, Family_member &family_member,
Parent_exit *parent_exit,
File_descriptor_registry &file_descriptor_registry, File_descriptor_registry &file_descriptor_registry,
Signal_context_capability destruct_context_cap, Signal_context_capability destruct_context_cap,
Ram_session &ref_ram_session, Ram_session &ref_ram_session,
@ -72,6 +75,7 @@ namespace Noux {
_local_rom_service(local_rom_service), _local_rom_service(local_rom_service),
_parent_services(parent_services), _parent_services(parent_services),
_family_member(family_member), _family_member(family_member),
_parent_exit(parent_exit),
_file_descriptor_registry(file_descriptor_registry), _file_descriptor_registry(file_descriptor_registry),
_destruct_context_cap(destruct_context_cap), _destruct_context_cap(destruct_context_cap),
_ref_ram_session(ref_ram_session), _ref_ram_session(ref_ram_session),
@ -130,11 +134,15 @@ namespace Noux {
*/ */
_file_descriptor_registry.flush(); _file_descriptor_registry.flush();
_family_member.wakeup_parent(exit_value); _family_member.exit(exit_value);
/* handle exit of the init process */ /* notify the parent */
if (_family_member.parent() == 0) if (_parent_exit)
_parent_exit->exit_child();
else {
/* handle exit of the init process */
Signal_transmitter(_destruct_context_cap).submit(); Signal_transmitter(_destruct_context_cap).submit();
}
} }
Ram_session *ref_ram_session() Ram_session *ref_ram_session()

View File

@ -18,34 +18,42 @@
#include <util/list.h> #include <util/list.h>
#include <base/lock.h> #include <base/lock.h>
/* Noux includes */
#include <parent_exit.h>
#include <parent_execve.h>
namespace Noux { namespace Noux {
class Family_member : public List<Family_member>::Element class Family_member : public List<Family_member>::Element,
public Parent_exit,
public Parent_execve
{ {
private: private:
int const _pid; int const _pid;
Lock _lock; Lock _lock;
List<Family_member> _list; List<Family_member> _list;
Family_member * const _parent;
bool _has_exited; bool _has_exited;
int _exit_status; 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: public:
Family_member(int pid, Family_member *parent) Family_member(int pid)
: _pid(pid), _parent(parent), _has_exited(false), _exit_status(0) : _pid(pid), _has_exited(false), _exit_status(0)
{ } { }
virtual ~Family_member() { } virtual ~Family_member() { }
int pid() const { return _pid; } int pid() const { return _pid; }
Family_member *parent() { return _parent; }
int exit_status() const { return _exit_status; } int exit_status() const { return _exit_status; }
/** /**
@ -66,15 +74,73 @@ namespace Noux {
_list.remove(member); _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 * Tell the parent that we exited
*/ */
void wakeup_parent(int exit_status) void exit(int exit_status)
{ {
_exit_status = exit_status; _exit_status = exit_status;
_has_exited = true; _has_exited = true;
if (_parent)
_parent->_wakeup_wait4();
} }
Family_member *poll4() Family_member *poll4()
@ -95,13 +161,18 @@ namespace Noux {
*/ */
Family_member *wait4() Family_member *wait4()
{ {
for (;;) { /* reset the blocker lock to the 'locked' state */
Family_member *result = poll4(); _blocker.unlock();
if (result) _blocker.lock();
return result;
_wait4_blocker.down(); Family_member *result = poll4();
} if (result)
return result;
_blocker.lock();
/* either a child exited or a signal occurred */
return poll4();
} }
}; };
} }

View File

@ -14,12 +14,9 @@
#ifndef _NOUX__INTERRUPT_HANDLER__H_ #ifndef _NOUX__INTERRUPT_HANDLER__H_
#define _NOUX__INTERRUPT_HANDLER__H_ #define _NOUX__INTERRUPT_HANDLER__H_
/* Genode includes */
#include <util/list.h>
namespace Noux { namespace Noux {
struct Interrupt_handler : List<Interrupt_handler>::Element struct Interrupt_handler
{ {
virtual void handle_interrupt() = 0; virtual void handle_interrupt() = 0;
}; };

View File

@ -24,10 +24,14 @@
#include <noux_session/sysio.h> #include <noux_session/sysio.h>
#include <shared_pointer.h> #include <shared_pointer.h>
#include <wake_up_notifier.h> #include <wake_up_notifier.h>
#include <interrupt_handler.h> #include <io_channel_listener.h>
namespace Noux { namespace Noux {
class Terminal_io_channel;
extern Genode::Lock &signal_lock();
/** /**
* Input/output channel backend that is used for calling * Input/output channel backend that is used for calling
* different methos which does not belong to the original * 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 * List of notifiers (i.e., processes) used by threads that block
* for an I/O-channel event * for an I/O-channel event
*/ */
List<Wake_up_notifier> _notifiers; List<Wake_up_notifier> _notifiers;
Lock _notifiers_lock; Lock _notifiers_lock;
List<Interrupt_handler> _interrupt_handlers; List<Io_channel_listener> _interrupt_handlers;
Lock _interrupt_handlers_lock; Lock _interrupt_handlers_lock;
public: public:
@ -140,7 +144,7 @@ namespace Noux {
* This function is called by Child objects to get woken up if the * This function is called by Child objects to get woken up if the
* terminal sends, for example, Ctrl-C. * 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); Lock::Guard guard(_interrupt_handlers_lock);
@ -150,23 +154,37 @@ namespace Noux {
/** /**
* Unregister interrupt handler * Unregister interrupt handler
*/ */
void unregister_interrupt_handler(Interrupt_handler *handler) void unregister_interrupt_handler(Io_channel_listener *handler)
{ {
Lock::Guard guard(_interrupt_handlers_lock); Lock::Guard guard(_interrupt_handlers_lock);
_interrupt_handlers.remove(handler); _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 * Tell all registered handlers about an interrupt event
*/ */
void invoke_all_interrupt_handlers() void invoke_all_interrupt_handlers()
{ {
Lock::Guard signal_lock_guard(signal_lock());
Lock::Guard guard(_interrupt_handlers_lock); Lock::Guard guard(_interrupt_handlers_lock);
for (Interrupt_handler *h = _interrupt_handlers.first(); for (Io_channel_listener *l = _interrupt_handlers.first();
h; h = h->next()) l; l = l->next())
h->handle_interrupt(); l->object()->handle_interrupt();
} }
/** /**

View File

@ -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 <util/list.h>
/* Noux includes */
#include <interrupt_handler.h>
namespace Noux {
typedef List_element<Interrupt_handler> Io_channel_listener;
}
#endif /* _NOUX__IO_CHANNEL_LISTENER__H_ */

View File

@ -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 <noux_session/sysio.h>
namespace Noux {
struct Kill_broadcaster
{
virtual bool kill(int pid, Noux::Sysio::Signal sig) = 0;
};
};
#endif /* _NOUX__KILL_BROADCASTER__H_ */

View File

@ -29,6 +29,7 @@
#include <user_info.h> #include <user_info.h>
#include <io_receptor_registry.h> #include <io_receptor_registry.h>
#include <destruct_queue.h> #include <destruct_queue.h>
#include <kill_broadcaster.h>
static const bool verbose_quota = false; static const bool verbose_quota = false;
@ -46,7 +47,6 @@ namespace Noux {
extern void init_network(); extern void init_network();
/** /**
* Timeout thread for SYSCALL_SELECT * 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); size_t path_len = strlen(_sysio->stat_in.path);
uint32_t path_hash = hash_path(_sysio->stat_in.path, path_len); 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 * 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; _sysio->stat_out.st.inode = path_hash;
} }
return result; break;
} }
case SYSCALL_FSTAT: case SYSCALL_FSTAT:
{ {
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->fstat_in.fd); Shared_pointer<Io_channel> io = _lookup_channel(_sysio->fstat_in.fd);
bool result = io->fstat(_sysio); result = io->fstat(_sysio);
if (result) { if (result) {
Sysio::Path path; Sysio::Path path;
@ -255,7 +255,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
} }
} }
return result; break;
} }
case SYSCALL_FCNTL: 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 */ /* we assume that there is only the close-on-execve flag */
_lookup_channel(_sysio->fcntl_in.fd)->close_on_execve = _lookup_channel(_sysio->fcntl_in.fd)->close_on_execve =
!!_sysio->fcntl_in.long_arg; !!_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: case SYSCALL_OPEN:
{ {
Vfs_handle *vfs_handle = root_dir()->open(_sysio, _sysio->open_in.path); Vfs_handle *vfs_handle = root_dir()->open(_sysio, _sysio->open_in.path);
if (!vfs_handle) if (!vfs_handle)
return false; break;
char const *leaf_path = root_dir()->leaf_path(_sysio->open_in.path); 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()); Genode::env()->heap());
_sysio->open_out.fd = add_io_channel(channel); _sysio->open_out.fd = add_io_channel(channel);
return true; result = true;
break;
} }
case SYSCALL_CLOSE: case SYSCALL_CLOSE:
{ {
remove_io_channel(_sysio->close_in.fd); remove_io_channel(_sysio->close_in.fd);
return true; result = true;
break;
} }
case SYSCALL_IOCTL: 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: 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: 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: case SYSCALL_EXECVE:
{ {
@ -327,7 +334,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
if (!binary_ds.valid()) { if (!binary_ds.valid()) {
_sysio->error.execve = Sysio::EXECVE_NONEXISTENT; _sysio->error.execve = Sysio::EXECVE_NONEXISTENT;
return false; break;
} }
Child_env<sizeof(_sysio->execve_in.args)> Child_env<sizeof(_sysio->execve_in.args)>
@ -340,57 +347,28 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
if (!binary_ds.valid()) { if (!binary_ds.valid()) {
_sysio->error.execve = Sysio::EXECVE_NONEXISTENT; _sysio->error.execve = Sysio::EXECVE_NONEXISTENT;
return false; break;
} }
root_dir()->release(child_env.binary_name(), binary_ds); root_dir()->release(child_env.binary_name(), binary_ds);
try { try {
Child *child = new Child(child_env.binary_name(), _parent_execve.execve_child(*this,
parent(), child_env.binary_name(),
pid(), child_env.args(),
_sig_rec, child_env.env(),
root_dir(), verbose);
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);
/* /*
* Close all open files. * 'return' instead of 'break' to skip possible signal delivery,
* * which might cause the old child process to exit itself
* 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 true; return true;
} }
catch (Child::Binary_does_not_exist) { catch (Child::Binary_does_not_exist) {
_sysio->error.execve = Sysio::EXECVE_NONEXISTENT; } _sysio->error.execve = Sysio::EXECVE_NONEXISTENT; }
return false; break;
} }
case SYSCALL_SELECT: case SYSCALL_SELECT:
@ -593,6 +571,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
*/ */
Child *child = new Child(_child_policy.name(), Child *child = new Child(_child_policy.name(),
this, this,
_kill_broadcaster,
*this,
new_pid, new_pid,
_sig_rec, _sig_rec,
root_dir(), root_dir(),
@ -622,7 +602,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_sysio->fork_out.pid = new_pid; _sysio->fork_out.pid = new_pid;
return true; result = true;
break;
} }
case SYSCALL_GETPID: case SYSCALL_GETPID:
@ -645,10 +626,16 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
static_cast<Child *>(exited)->submit_exit_signal(); static_cast<Child *>(exited)->submit_exit_signal();
} else { } else {
_sysio->wait4_out.pid = 0; if (_sysio->wait4_in.nohang) {
_sysio->wait4_out.status = 0; _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: 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[0] = add_io_channel(pipe_source);
_sysio->pipe_out.fd[1] = add_io_channel(pipe_sink); _sysio->pipe_out.fd[1] = add_io_channel(pipe_sink);
return true; result = true;
break;
} }
case SYSCALL_DUP2: case SYSCALL_DUP2:
@ -673,30 +661,36 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_sysio->dup2_out.fd = fd; _sysio->dup2_out.fd = fd;
return true; result = true;
break;
} }
case SYSCALL_UNLINK: 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: 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: case SYSCALL_RENAME:
return root_dir()->rename(_sysio, _sysio->rename_in.from_path, result = root_dir()->rename(_sysio, _sysio->rename_in.from_path,
_sysio->rename_in.to_path); _sysio->rename_in.to_path);
break;
case SYSCALL_MKDIR: 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: 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: 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.uid = Noux::user_info()->uid;
_sysio->userinfo_out.gid = Noux::user_info()->gid; _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. * got a unknown uid.
*/ */
if (_sysio->userinfo_in.uid != Noux::user_info()->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.name,
Genode::memcpy(_sysio->userinfo_out.shell, Noux::user_info()->shell, sizeof(Noux::user_info()->shell)); Noux::user_info()->name,
Genode::memcpy(_sysio->userinfo_out.home, Noux::user_info()->home, sizeof(Noux::user_info()->home)); 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.uid = user_info()->uid;
_sysio->userinfo_out.gid = user_info()->gid; _sysio->userinfo_out.gid = user_info()->gid;
return true; result = true;
break;
} }
case SYSCALL_GETTIMEOFDAY: case SYSCALL_GETTIMEOFDAY:
@ -741,7 +743,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_sysio->gettimeofday_out.sec = (time / 1000); _sysio->gettimeofday_out.sec = (time / 1000);
_sysio->gettimeofday_out.usec = (time % 1000) * 1000; _sysio->gettimeofday_out.usec = (time % 1000) * 1000;
return true; result = true;
break;
} }
case SYSCALL_CLOCK_GETTIME: 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.sec = (time / 1000);
_sysio->clock_gettime_out.nsec = 0; _sysio->clock_gettime_out.nsec = 0;
return true; result = true;
break;
} }
default: default:
@ -768,12 +772,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_sysio->clock_gettime_out.nsec = 0; _sysio->clock_gettime_out.nsec = 0;
_sysio->error.clock = Sysio::CLOCK_ERR_INVALID; _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 * But we return true anyway to keep certain programs, e.g. make
* happy. * happy.
*/ */
return true; result = true;
break;
} }
case SYSCALL_SYNC: case SYSCALL_SYNC:
{ {
root_dir()->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: case SYSCALL_SOCKET:
@ -809,7 +826,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_SHUTDOWN: case SYSCALL_SHUTDOWN:
case SYSCALL_CONNECT: case SYSCALL_CONNECT:
return _syscall_net(sc); result = _syscall_net(sc);
break;
case SYSCALL_INVALID: break; case SYSCALL_INVALID: break;
} }
@ -823,8 +841,9 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
/* handle signals which might have occured */ /* handle signals which might have occured */
while (!_pending_signals.empty() && while (!_pending_signals.empty() &&
(_sysio->pending_signals.avail_capacity() > 0)) (_sysio->pending_signals.avail_capacity() > 0)) {
_sysio->pending_signals.add(_pending_signals.get()); _sysio->pending_signals.add(_pending_signals.get());
}
return result; 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) { void *operator new (Genode::size_t size) {
return Genode::env()->heap()->alloc(size); } return Genode::env()->heap()->alloc(size); }
@ -1007,8 +1040,22 @@ int main(int argc, char **argv)
static Genode::Signal_receiver sig_rec; static Genode::Signal_receiver sig_rec;
static Destruct_queue destruct_queue; 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(), init_child = new Noux::Child(name_of_init_process(),
0, 0,
kill_broadcaster,
*init_child,
pid_allocator()->alloc(), pid_allocator()->alloc(),
&sig_rec, &sig_rec,
&root_dir, &root_dir,
@ -1022,6 +1069,8 @@ int main(int argc, char **argv)
destruct_queue, destruct_queue,
verbose); verbose);
kill_broadcaster.init_process = init_child;
/* /*
* I/O channels must be dynamically allocated to handle cases where the * I/O channels must be dynamically allocated to handle cases where the
* init program closes one of these. * init program closes one of these.

View File

@ -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 <noux_session/sysio.h>
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_ */

View File

@ -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_ */