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_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;

View File

@ -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; }, { });
};
};
};

View File

@ -41,6 +41,7 @@
#include <termios.h>
#include <pwd.h>
#include <string.h>
#include <signal.h>
/**
* There is a off_t typedef clash between sys/socket.h
@ -54,6 +55,7 @@
/* libc-internal includes */
#include <libc_mem_alloc.h>
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()->

View File

@ -33,6 +33,8 @@
#include <destruct_queue.h>
#include <destruct_dispatcher.h>
#include <interrupt_handler.h>
#include <kill_broadcaster.h>
#include <parent_execve.h>
#include <local_cpu_service.h>
#include <local_ram_service.h>
@ -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<Child>(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);
}
};
};

View File

@ -19,6 +19,7 @@
/* Noux includes */
#include <family_member.h>
#include <parent_exit.h>
#include <file_descriptor_registry.h>
#include <local_noux_service.h>
#include <local_rm_service.h>
@ -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()

View File

@ -18,34 +18,42 @@
#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
class Family_member : public List<Family_member>::Element,
public Parent_exit,
public Parent_execve
{
private:
int const _pid;
Lock _lock;
List<Family_member> _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();
}
};
}

View File

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

View File

@ -24,10 +24,14 @@
#include <noux_session/sysio.h>
#include <shared_pointer.h>
#include <wake_up_notifier.h>
#include <interrupt_handler.h>
#include <io_channel_listener.h>
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<Wake_up_notifier> _notifiers;
Lock _notifiers_lock;
List<Interrupt_handler> _interrupt_handlers;
Lock _interrupt_handlers_lock;
List<Wake_up_notifier> _notifiers;
Lock _notifiers_lock;
List<Io_channel_listener> _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();
}
/**

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 <io_receptor_registry.h>
#include <destruct_queue.h>
#include <kill_broadcaster.h>
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_channel> 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_env<sizeof(_sysio->execve_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<Child *>(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.

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