2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief Unix emulation environment for Genode
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2011-02-14
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2012-01-03 15:35:05 +01:00
|
|
|
* Copyright (C) 2011-2012 Genode Labs GmbH
|
2011-12-22 16:19:25 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
|
|
|
#include <cap_session/connection.h>
|
|
|
|
#include <os/config.h>
|
2012-08-20 16:13:21 +02:00
|
|
|
#include <os/alarm.h>
|
|
|
|
#include <timer_session/connection.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/* Noux includes */
|
|
|
|
#include <child.h>
|
2012-07-19 22:09:59 +02:00
|
|
|
#include <child_env.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
#include <vfs_io_channel.h>
|
|
|
|
#include <terminal_io_channel.h>
|
|
|
|
#include <dummy_input_io_channel.h>
|
2012-03-19 22:52:26 +01:00
|
|
|
#include <pipe_io_channel.h>
|
2012-04-25 23:52:49 +02:00
|
|
|
#include <dir_file_system.h>
|
2012-08-20 18:24:11 +02:00
|
|
|
#include <user_info.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
|
2012-08-17 23:40:15 +02:00
|
|
|
static bool trace_syscalls = false;
|
2012-02-18 01:46:54 +01:00
|
|
|
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
namespace Noux {
|
|
|
|
|
|
|
|
static Noux::Child *init_child;
|
|
|
|
|
|
|
|
bool is_init_process(Child *child) { return child == init_child; }
|
|
|
|
void init_process_exited() { init_child = 0; }
|
|
|
|
};
|
|
|
|
|
2012-05-24 17:06:54 +02:00
|
|
|
extern void (*close_socket)(int);
|
|
|
|
|
|
|
|
extern void init_network();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-17 23:40:15 +02:00
|
|
|
|
2012-08-20 16:13:21 +02:00
|
|
|
/**
|
|
|
|
* Timeout thread for SYSCALL_SELECT
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Noux {
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
class Timeout_scheduler : Thread<4096>, public Alarm_scheduler
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
Timer::Connection _timer;
|
|
|
|
Alarm::Time _curr_time;
|
|
|
|
|
|
|
|
enum { TIMER_GRANULARITY_MSEC = 10 };
|
|
|
|
|
|
|
|
void entry()
|
|
|
|
{
|
|
|
|
for (;;) {
|
|
|
|
_timer.msleep(TIMER_GRANULARITY_MSEC);
|
|
|
|
Alarm_scheduler::handle(_curr_time);
|
|
|
|
_curr_time += TIMER_GRANULARITY_MSEC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
Timeout_scheduler() : _curr_time(0) { start(); }
|
|
|
|
|
|
|
|
Alarm::Time curr_time() const { return _curr_time; }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Timeout_state
|
|
|
|
{
|
|
|
|
bool timed_out;
|
|
|
|
|
|
|
|
Timeout_state() : timed_out(false) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
class Timeout_alarm : public Alarm
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
Timeout_state *_state;
|
|
|
|
Semaphore *_blocker;
|
|
|
|
Timeout_scheduler *_scheduler;
|
|
|
|
|
|
|
|
public:
|
|
|
|
Timeout_alarm(Timeout_state *st, Semaphore *blocker, Timeout_scheduler *scheduler, Time timeout)
|
|
|
|
:
|
|
|
|
_state(st),
|
|
|
|
_blocker(blocker),
|
|
|
|
_scheduler(scheduler)
|
|
|
|
{
|
|
|
|
_scheduler->schedule_absolute(this, _scheduler->curr_time() + timeout);
|
|
|
|
_state->timed_out = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void discard() { _scheduler->discard(this); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
bool on_alarm()
|
|
|
|
{
|
|
|
|
_state->timed_out = true;
|
|
|
|
_blocker->up();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/*****************************
|
|
|
|
** Noux syscall dispatcher **
|
|
|
|
*****************************/
|
|
|
|
|
|
|
|
bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
|
|
|
{
|
2012-08-17 23:40:15 +02:00
|
|
|
if (trace_syscalls)
|
2012-02-18 01:46:54 +01:00
|
|
|
Genode::printf("PID %d -> SYSCALL %s\n",
|
2012-02-24 23:43:35 +01:00
|
|
|
pid(), Noux::Session::syscall_name(sc));
|
2012-02-18 01:46:54 +01:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
try {
|
|
|
|
switch (sc) {
|
|
|
|
|
|
|
|
case SYSCALL_GETCWD:
|
|
|
|
|
|
|
|
Genode::strncpy(_sysio->getcwd_out.path, _env.pwd(),
|
|
|
|
sizeof(_sysio->getcwd_out.path));
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case SYSCALL_WRITE:
|
2012-03-19 22:52:26 +01:00
|
|
|
{
|
|
|
|
size_t const count_in = _sysio->write_in.count;
|
|
|
|
|
|
|
|
for (size_t count = 0; count != count_in; ) {
|
|
|
|
|
|
|
|
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->write_in.fd);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-03-19 22:52:26 +01:00
|
|
|
if (!io->check_unblock(false, true, false))
|
|
|
|
_block_for_io_channel(io);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 'io->write' is expected to update 'write_out.count'
|
|
|
|
*/
|
|
|
|
if (io->write(_sysio, count) == false)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
case SYSCALL_READ:
|
2012-03-19 22:52:26 +01:00
|
|
|
{
|
|
|
|
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->read_in.fd);
|
|
|
|
|
|
|
|
while (!io->check_unblock(true, false, false))
|
|
|
|
_block_for_io_channel(io);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-03-19 22:52:26 +01:00
|
|
|
io->read(_sysio);
|
|
|
|
return true;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-02 15:17:24 +02:00
|
|
|
case SYSCALL_FTRUNCATE:
|
|
|
|
{
|
|
|
|
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->ftruncate_in.fd);
|
|
|
|
|
|
|
|
while (!io->check_unblock(true, false, false))
|
|
|
|
_block_for_io_channel(io);
|
|
|
|
|
|
|
|
return io->ftruncate(_sysio);
|
|
|
|
}
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
case SYSCALL_STAT:
|
|
|
|
case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */
|
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
return _root_dir->stat(_sysio, Absolute_path(_sysio->stat_in.path,
|
|
|
|
_env.pwd()).base());
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
case SYSCALL_FSTAT:
|
|
|
|
|
|
|
|
return _lookup_channel(_sysio->fstat_in.fd)->fstat(_sysio);
|
|
|
|
|
|
|
|
case SYSCALL_FCNTL:
|
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
if (_sysio->fcntl_in.cmd == Sysio::FCNTL_CMD_SET_FD_FLAGS) {
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
return _lookup_channel(_sysio->fcntl_in.fd)->fcntl(_sysio);
|
|
|
|
|
|
|
|
case SYSCALL_OPEN:
|
|
|
|
{
|
|
|
|
Absolute_path absolute_path(_sysio->open_in.path, _env.pwd());
|
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
Vfs_handle *vfs_handle = _root_dir->open(_sysio, absolute_path.base());
|
2011-12-22 16:19:25 +01:00
|
|
|
if (!vfs_handle)
|
|
|
|
return false;
|
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
char const *leaf_path = _root_dir->leaf_path(absolute_path.base());
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
/*
|
|
|
|
* File descriptors of opened directories are handled by
|
|
|
|
* '_root_dir'. In this case, we use the absolute path as leaf
|
|
|
|
* path because path operations always refer to the global
|
|
|
|
* root.
|
|
|
|
*/
|
|
|
|
if (vfs_handle->ds() == _root_dir)
|
|
|
|
leaf_path = absolute_path.base();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
Shared_pointer<Io_channel>
|
|
|
|
channel(new Vfs_io_channel(absolute_path.base(),
|
|
|
|
leaf_path, _root_dir, vfs_handle),
|
|
|
|
Genode::env()->heap());
|
|
|
|
|
|
|
|
_sysio->open_out.fd = add_io_channel(channel);
|
2011-12-22 16:19:25 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SYSCALL_CLOSE:
|
|
|
|
{
|
2012-05-24 17:06:54 +02:00
|
|
|
/**
|
|
|
|
* We have to explicitly close Socket_io_channel fd's because
|
|
|
|
* these are currently handled separately.
|
|
|
|
*/
|
|
|
|
if (close_socket)
|
|
|
|
close_socket(_sysio->close_in.fd);
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
remove_io_channel(_sysio->close_in.fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SYSCALL_IOCTL:
|
2012-02-16 21:45:37 +01:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
return _lookup_channel(_sysio->ioctl_in.fd)->ioctl(_sysio);
|
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
case SYSCALL_LSEEK:
|
|
|
|
|
|
|
|
return _lookup_channel(_sysio->lseek_in.fd)->lseek(_sysio);
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
case SYSCALL_DIRENT:
|
|
|
|
|
|
|
|
return _lookup_channel(_sysio->dirent_in.fd)->dirent(_sysio);
|
|
|
|
|
|
|
|
case SYSCALL_FCHDIR:
|
|
|
|
|
|
|
|
return _lookup_channel(_sysio->fchdir_in.fd)->fchdir(_sysio, &_env);
|
|
|
|
|
|
|
|
case SYSCALL_EXECVE:
|
|
|
|
{
|
2012-04-25 23:52:49 +02:00
|
|
|
Absolute_path absolute_path(_sysio->execve_in.filename, _env.pwd());
|
2012-03-19 16:49:54 +01:00
|
|
|
|
2012-07-19 22:09:59 +02:00
|
|
|
Dataspace_capability binary_ds = _root_dir->dataspace(absolute_path.base());
|
|
|
|
|
|
|
|
if (!binary_ds.valid())
|
|
|
|
throw Child::Binary_does_not_exist();
|
|
|
|
|
|
|
|
Child_env<sizeof(_sysio->execve_in.args)> child_env(
|
|
|
|
absolute_path.base(), binary_ds, _sysio->execve_in.args,
|
|
|
|
_sysio->execve_in.env);
|
2012-03-19 16:49:54 +01:00
|
|
|
|
2012-05-18 17:10:54 +02:00
|
|
|
try {
|
2012-07-19 22:09:59 +02:00
|
|
|
Child *child = new Child(child_env.binary_name(),
|
2012-05-18 17:10:54 +02:00
|
|
|
parent(),
|
|
|
|
pid(),
|
|
|
|
_sig_rec,
|
|
|
|
_root_dir,
|
2012-07-19 22:09:59 +02:00
|
|
|
child_env.args(),
|
|
|
|
child_env.env(),
|
2012-05-18 17:10:54 +02:00
|
|
|
_env.pwd(),
|
|
|
|
_cap_session,
|
|
|
|
_parent_services,
|
|
|
|
_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 */
|
|
|
|
Genode::Signal_transmitter(_execve_cleanup_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;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case SYSCALL_SELECT:
|
|
|
|
{
|
|
|
|
Sysio::Select_fds &in_fds = _sysio->select_in.fds;
|
2012-08-20 16:13:21 +02:00
|
|
|
size_t in_fds_total = in_fds.total_fds();
|
|
|
|
|
|
|
|
int _rd_array[in_fds_total];
|
|
|
|
int _wr_array[in_fds_total];
|
|
|
|
|
|
|
|
long timeout_sec = _sysio->select_in.timeout.sec;
|
|
|
|
long timeout_usec = _sysio->select_in.timeout.usec;
|
|
|
|
bool timeout_reached = false;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Block for one action of the watched file descriptors
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check I/O channels of specified file descriptors for
|
|
|
|
* unblock condition. Return if one I/O channel satisfies
|
|
|
|
* the condition.
|
|
|
|
*/
|
2012-08-20 16:13:21 +02:00
|
|
|
size_t unblock_rd = 0;
|
|
|
|
size_t unblock_wr = 0;
|
|
|
|
size_t unblock_ex = 0;
|
|
|
|
|
|
|
|
/* process read fds */
|
|
|
|
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
int fd = in_fds.array[i];
|
|
|
|
if (!fd_in_use(fd)) continue;
|
|
|
|
|
|
|
|
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
|
|
|
|
|
|
|
if (io->check_unblock(in_fds.watch_for_rd(i),
|
2012-08-20 16:13:21 +02:00
|
|
|
in_fds.watch_for_wr(i),
|
|
|
|
in_fds.watch_for_ex(i))) {
|
|
|
|
|
|
|
|
if (io->check_unblock(true, false, false)) {
|
|
|
|
_rd_array[unblock_rd++] = fd;
|
|
|
|
// io->clear_unblock(true, false, false);
|
|
|
|
}
|
|
|
|
if (io->check_unblock(false, true, false)) {
|
|
|
|
_wr_array[unblock_wr++] = fd;
|
|
|
|
// io->clear_unblock(false, true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (io->check_unblock(false, false, true)) {
|
|
|
|
unblock_ex++;
|
|
|
|
// io->clear_unblock(false, false, true);
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-20 16:13:21 +02:00
|
|
|
}
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-20 16:13:21 +02:00
|
|
|
if (unblock_rd || unblock_wr || unblock_ex) {
|
|
|
|
for (size_t i = 0; i < unblock_rd; i++) {
|
|
|
|
_sysio->select_out.fds.array[i] = _rd_array[i];
|
|
|
|
_sysio->select_out.fds.num_rd = unblock_rd;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
2012-08-20 16:13:21 +02:00
|
|
|
for (size_t i = 0; i < unblock_wr; i++) {
|
|
|
|
_sysio->select_out.fds.array[i] = _wr_array[i];
|
|
|
|
_sysio->select_out.fds.num_wr = unblock_wr;
|
|
|
|
}
|
|
|
|
|
|
|
|
_sysio->select_out.fds.num_ex = unblock_ex;
|
|
|
|
|
|
|
|
return true;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return if I/O channel triggered, but timeout exceeded
|
|
|
|
*/
|
2012-08-20 16:13:21 +02:00
|
|
|
|
|
|
|
if (_sysio->select_in.timeout.zero() || timeout_reached) {
|
|
|
|
/*
|
|
|
|
if (timeout_reached) PINF("timeout_reached");
|
|
|
|
else PINF("timeout.zero()");
|
|
|
|
*/
|
2011-12-22 16:19:25 +01:00
|
|
|
_sysio->select_out.fds.num_rd = 0;
|
|
|
|
_sysio->select_out.fds.num_wr = 0;
|
|
|
|
_sysio->select_out.fds.num_ex = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Register ourself at all watched I/O channels
|
|
|
|
*
|
|
|
|
* We instantiate as many notifiers as we have file
|
|
|
|
* descriptors to observe. Each notifier is associated
|
|
|
|
* with the child's blocking semaphore. When any of the
|
|
|
|
* notifiers get woken up, the semaphore gets unblocked.
|
|
|
|
*
|
|
|
|
* XXX However, the semaphore may get unblocked for other
|
|
|
|
* conditions such as the destruction of the child.
|
|
|
|
* ...to be done.
|
|
|
|
*/
|
|
|
|
|
2012-08-20 16:13:21 +02:00
|
|
|
Wake_up_notifier notifiers[in_fds_total];
|
|
|
|
|
|
|
|
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
2011-12-22 16:19:25 +01:00
|
|
|
int fd = in_fds.array[i];
|
|
|
|
if (!fd_in_use(fd)) continue;
|
|
|
|
|
|
|
|
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
|
|
|
notifiers[i].semaphore = &_blocker;
|
|
|
|
io->register_wake_up_notifier(¬ifiers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Block at barrier except when reaching the timeout
|
|
|
|
*/
|
2012-08-20 16:13:21 +02:00
|
|
|
|
|
|
|
if (!_sysio->select_in.timeout.infinite()) {
|
|
|
|
unsigned long to_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
|
|
|
|
Timeout_state ts;
|
|
|
|
Timeout_alarm ta(&ts, &_blocker, Noux::timeout_scheduler(), to_msec);
|
|
|
|
|
|
|
|
/* block until timeout is reached or we were unblocked */
|
|
|
|
_blocker.down();
|
|
|
|
|
|
|
|
if (ts.timed_out) {
|
|
|
|
timeout_reached = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* We woke up before reaching the timeout,
|
|
|
|
* so we discard the alarm
|
|
|
|
*/
|
|
|
|
ta.discard();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* let's block infinitely */
|
|
|
|
_blocker.down();
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Unregister barrier at watched I/O channels
|
|
|
|
*/
|
2012-08-20 16:13:21 +02:00
|
|
|
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
2011-12-22 16:19:25 +01:00
|
|
|
int fd = in_fds.array[i];
|
|
|
|
if (!fd_in_use(fd)) continue;
|
|
|
|
|
|
|
|
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
|
|
|
io->unregister_wake_up_notifier(¬ifiers[i]);
|
|
|
|
}
|
2012-08-20 16:13:21 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-02-15 19:51:13 +01:00
|
|
|
case SYSCALL_FORK:
|
|
|
|
{
|
2012-02-18 01:46:54 +01:00
|
|
|
Genode::addr_t ip = _sysio->fork_in.ip;
|
|
|
|
Genode::addr_t sp = _sysio->fork_in.sp;
|
|
|
|
Genode::addr_t parent_cap_addr = _sysio->fork_in.parent_cap_addr;
|
|
|
|
|
2012-03-19 16:49:54 +01:00
|
|
|
int const new_pid = pid_allocator()->alloc();
|
2012-02-18 01:46:54 +01:00
|
|
|
|
2012-02-22 12:45:13 +01:00
|
|
|
/*
|
|
|
|
* XXX To ease debugging, it would be useful to generate a
|
|
|
|
* unique name that includes the PID instead of just
|
|
|
|
* reusing the name of the parent.
|
|
|
|
*/
|
2012-02-25 20:41:59 +01:00
|
|
|
Child *child = new Child(_child_policy.name(),
|
2012-02-24 23:43:35 +01:00
|
|
|
this,
|
2012-02-18 01:46:54 +01:00
|
|
|
new_pid,
|
|
|
|
_sig_rec,
|
2012-04-25 23:52:49 +02:00
|
|
|
_root_dir,
|
2012-02-18 01:46:54 +01:00
|
|
|
_args,
|
2012-03-19 16:49:54 +01:00
|
|
|
_env.env(),
|
2012-03-16 18:22:06 +01:00
|
|
|
_env.pwd(),
|
2012-02-18 01:46:54 +01:00
|
|
|
_cap_session,
|
|
|
|
_parent_services,
|
|
|
|
_resources.ep,
|
|
|
|
true);
|
|
|
|
|
2012-02-24 23:43:35 +01:00
|
|
|
Family_member::insert(child);
|
|
|
|
|
2012-02-18 01:46:54 +01:00
|
|
|
_assign_io_channels_to(child);
|
|
|
|
|
|
|
|
/* copy our address space into the new child */
|
2012-02-22 12:45:13 +01:00
|
|
|
_resources.rm.replay(child->ram(), child->rm(),
|
|
|
|
child->ds_registry(), _resources.ep);
|
2012-02-18 01:46:54 +01:00
|
|
|
|
|
|
|
/* start executing the main thread of the new process */
|
|
|
|
child->start_forked_main_thread(ip, sp, parent_cap_addr);
|
|
|
|
|
|
|
|
/* activate child entrypoint, thereby starting the new process */
|
|
|
|
child->start();
|
|
|
|
|
|
|
|
_sysio->fork_out.pid = new_pid;
|
|
|
|
|
2012-02-15 19:51:13 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SYSCALL_GETPID:
|
|
|
|
{
|
2012-02-24 23:43:35 +01:00
|
|
|
_sysio->getpid_out.pid = pid();
|
2012-02-15 19:51:13 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-02-24 15:19:38 +01:00
|
|
|
case SYSCALL_WAIT4:
|
|
|
|
{
|
2012-02-24 23:43:35 +01:00
|
|
|
Family_member *exited = _sysio->wait4_in.nohang ? poll4() : wait4();
|
|
|
|
|
|
|
|
if (exited) {
|
|
|
|
_sysio->wait4_out.pid = exited->pid();
|
|
|
|
_sysio->wait4_out.status = exited->exit_status();
|
|
|
|
Family_member::remove(exited);
|
|
|
|
|
2012-03-19 22:52:26 +01:00
|
|
|
PINF("submit exit signal for PID %d", exited->pid());
|
|
|
|
static_cast<Child *>(exited)->submit_exit_signal();
|
2012-02-25 20:41:59 +01:00
|
|
|
|
2012-02-24 23:43:35 +01:00
|
|
|
} else {
|
|
|
|
_sysio->wait4_out.pid = 0;
|
|
|
|
_sysio->wait4_out.status = 0;
|
|
|
|
}
|
2012-02-24 15:19:38 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-19 22:52:26 +01:00
|
|
|
case SYSCALL_PIPE:
|
|
|
|
{
|
|
|
|
Shared_pointer<Pipe> pipe(new Pipe, Genode::env()->heap());
|
|
|
|
|
|
|
|
Shared_pointer<Io_channel> pipe_sink(new Pipe_sink_io_channel(pipe, *_sig_rec),
|
|
|
|
Genode::env()->heap());
|
|
|
|
Shared_pointer<Io_channel> pipe_source(new Pipe_source_io_channel(pipe, *_sig_rec),
|
|
|
|
Genode::env()->heap());
|
|
|
|
|
|
|
|
_sysio->pipe_out.fd[0] = add_io_channel(pipe_source);
|
|
|
|
_sysio->pipe_out.fd[1] = add_io_channel(pipe_sink);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SYSCALL_DUP2:
|
|
|
|
{
|
|
|
|
add_io_channel(io_channel_by_fd(_sysio->dup2_in.fd),
|
|
|
|
_sysio->dup2_in.to_fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-04-25 23:52:49 +02:00
|
|
|
case SYSCALL_UNLINK:
|
|
|
|
|
|
|
|
return _root_dir->unlink(_sysio, Absolute_path(_sysio->unlink_in.path,
|
|
|
|
_env.pwd()).base());
|
|
|
|
|
|
|
|
case SYSCALL_RENAME:
|
|
|
|
|
|
|
|
return _root_dir->rename(_sysio, Absolute_path(_sysio->rename_in.from_path,
|
|
|
|
_env.pwd()).base(),
|
|
|
|
Absolute_path(_sysio->rename_in.to_path,
|
|
|
|
_env.pwd()).base());
|
|
|
|
|
|
|
|
case SYSCALL_MKDIR:
|
|
|
|
|
|
|
|
return _root_dir->mkdir(_sysio, Absolute_path(_sysio->mkdir_in.path,
|
|
|
|
_env.pwd()).base());
|
|
|
|
|
2012-08-20 18:24:11 +02:00
|
|
|
case SYSCALL_USERINFO:
|
|
|
|
{
|
|
|
|
if (_sysio->userinfo_in.request == Sysio::USERINFO_GET_UID
|
|
|
|
|| _sysio->userinfo_in.request == Sysio::USERINFO_GET_GID) {
|
|
|
|
_sysio->userinfo_out.uid = Noux::user_info()->uid;
|
|
|
|
_sysio->userinfo_out.gid = Noux::user_info()->gid;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since NOUX supports exactly one user, return false if we
|
|
|
|
* got a unknown uid.
|
|
|
|
*/
|
|
|
|
if (_sysio->userinfo_in.uid != Noux::user_info()->uid)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-05-24 17:06:54 +02:00
|
|
|
case SYSCALL_SOCKET:
|
|
|
|
case SYSCALL_GETSOCKOPT:
|
|
|
|
case SYSCALL_SETSOCKOPT:
|
|
|
|
case SYSCALL_ACCEPT:
|
|
|
|
case SYSCALL_BIND:
|
|
|
|
case SYSCALL_LISTEN:
|
|
|
|
case SYSCALL_SEND:
|
|
|
|
case SYSCALL_SENDTO:
|
|
|
|
case SYSCALL_RECV:
|
2012-06-08 14:24:38 +02:00
|
|
|
case SYSCALL_RECVFROM:
|
2012-05-24 17:06:54 +02:00
|
|
|
case SYSCALL_GETPEERNAME:
|
|
|
|
case SYSCALL_SHUTDOWN:
|
|
|
|
case SYSCALL_CONNECT:
|
2012-08-17 23:40:15 +02:00
|
|
|
|
|
|
|
return _syscall_net(sc);
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
case SYSCALL_INVALID: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (Invalid_fd) {
|
|
|
|
_sysio->error.general = Sysio::ERR_FD_INVALID;
|
|
|
|
PERR("Invalid file descriptor"); }
|
|
|
|
|
|
|
|
catch (...) { PERR("Unexpected exception"); }
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return name of init process as specified in the config
|
|
|
|
*/
|
|
|
|
static char *name_of_init_process()
|
|
|
|
{
|
|
|
|
enum { INIT_NAME_LEN = 128 };
|
|
|
|
static char buf[INIT_NAME_LEN];
|
|
|
|
Genode::config()->xml_node().sub_node("start").attribute("name").value(buf, sizeof(buf));
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read command-line arguments of init process from config
|
|
|
|
*/
|
|
|
|
static Noux::Args const &args_of_init_process()
|
|
|
|
{
|
|
|
|
static char args_buf[4096];
|
|
|
|
static Noux::Args args(args_buf, sizeof(args_buf));
|
|
|
|
|
|
|
|
Genode::Xml_node start_node = Genode::config()->xml_node().sub_node("start");
|
|
|
|
|
|
|
|
try {
|
|
|
|
/* the first argument is the program name */
|
|
|
|
args.append(name_of_init_process());
|
|
|
|
|
|
|
|
Genode::Xml_node arg_node = start_node.sub_node("arg");
|
|
|
|
for (; ; arg_node = arg_node.next("arg")) {
|
|
|
|
static char buf[512];
|
|
|
|
arg_node.attribute("value").value(buf, sizeof(buf));
|
|
|
|
args.append(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Genode::Xml_node::Nonexistent_sub_node) { }
|
|
|
|
catch (Noux::Args::Overrun) { PERR("Argument buffer overrun"); }
|
|
|
|
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-08 21:15:43 +01:00
|
|
|
static void quote(char *buf, Genode::size_t buf_len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Make sure to leave space at the end of buffer for the finishing '"' and
|
|
|
|
* the null-termination.
|
|
|
|
*/
|
2012-01-08 22:05:26 +01:00
|
|
|
char c = '"';
|
2012-01-08 21:15:43 +01:00
|
|
|
unsigned i = 0;
|
|
|
|
for (; c && (i + 2 < buf_len); i++)
|
|
|
|
{
|
2012-01-08 22:05:26 +01:00
|
|
|
/*
|
|
|
|
* So shouldn't this have a special case for '"' characters inside the
|
|
|
|
* string? This is actually not needed because such a string could
|
|
|
|
* never be constructed via the XML config anyway. You can sneak in '"'
|
|
|
|
* characters only by quoting them in the XML file. Then, however, they
|
|
|
|
* are already quoted.
|
|
|
|
*/
|
2012-01-08 21:15:43 +01:00
|
|
|
char next_c = buf[i];
|
|
|
|
buf[i] = c;
|
|
|
|
c = next_c;
|
|
|
|
}
|
|
|
|
buf[i + 0] = '"';
|
|
|
|
buf[i + 1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/**
|
|
|
|
* Return string containing the environment variables of init
|
|
|
|
*
|
|
|
|
* The string is formatted according to the 'Genode::Arg_string' rules.
|
|
|
|
*/
|
|
|
|
static char const *env_string_of_init_process()
|
|
|
|
{
|
|
|
|
static char env_buf[4096];
|
|
|
|
|
|
|
|
Genode::Arg_string::set_arg(env_buf, sizeof(env_buf), "PWD", "\"/\"");
|
|
|
|
|
|
|
|
/* read environment variables for init process from config */
|
|
|
|
Genode::Xml_node start_node = Genode::config()->xml_node().sub_node("start");
|
|
|
|
try {
|
|
|
|
Genode::Xml_node arg_node = start_node.sub_node("env");
|
|
|
|
for (; ; arg_node = arg_node.next("env")) {
|
|
|
|
static char name_buf[256], value_buf[256];
|
|
|
|
|
|
|
|
arg_node.attribute("name").value(name_buf, sizeof(name_buf));
|
|
|
|
arg_node.attribute("value").value(value_buf, sizeof(value_buf));
|
2012-01-08 21:15:43 +01:00
|
|
|
quote(value_buf, sizeof(value_buf));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
Genode::Arg_string::set_arg(env_buf, sizeof(env_buf),
|
|
|
|
name_buf, value_buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Genode::Xml_node::Nonexistent_sub_node) { }
|
|
|
|
|
|
|
|
return env_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-22 22:24:32 +01:00
|
|
|
Noux::Pid_allocator *Noux::pid_allocator()
|
|
|
|
{
|
|
|
|
static Noux::Pid_allocator inst;
|
|
|
|
return &inst;
|
|
|
|
}
|
|
|
|
|
2012-08-20 18:24:11 +02:00
|
|
|
|
2012-08-20 16:13:21 +02:00
|
|
|
Noux::Timeout_scheduler *Noux::timeout_scheduler()
|
|
|
|
{
|
|
|
|
static Noux::Timeout_scheduler inst;
|
|
|
|
return &inst;
|
|
|
|
}
|
2012-02-22 22:24:32 +01:00
|
|
|
|
2012-08-20 18:24:11 +02:00
|
|
|
|
|
|
|
Noux::User_info* Noux::user_info()
|
|
|
|
{
|
|
|
|
static Noux::User_info inst;
|
|
|
|
return &inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
void *operator new (Genode::size_t size) {
|
|
|
|
return Genode::env()->heap()->alloc(size); }
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
using namespace Noux;
|
|
|
|
PINF("--- noux started ---");
|
|
|
|
|
|
|
|
/* look for dynamic linker */
|
|
|
|
try {
|
|
|
|
static Genode::Rom_connection rom("ld.lib.so");
|
|
|
|
Genode::Process::dynamic_linker(rom.dataspace());
|
|
|
|
} catch (...) { }
|
|
|
|
|
|
|
|
/* whitelist of service requests to be routed to the parent */
|
|
|
|
static Genode::Service_registry parent_services;
|
2012-02-18 01:46:54 +01:00
|
|
|
char const *service_names[] = { "LOG", "ROM", "Timer", 0 };
|
2011-12-22 16:19:25 +01:00
|
|
|
for (unsigned i = 0; service_names[i]; i++)
|
|
|
|
parent_services.insert(new Genode::Parent_service(service_names[i]));
|
|
|
|
|
|
|
|
static Genode::Cap_connection cap;
|
|
|
|
|
2012-08-17 23:40:15 +02:00
|
|
|
/* obtain global configuration */
|
|
|
|
try {
|
|
|
|
trace_syscalls = config()->xml_node().attribute("trace_syscalls").has_value("yes");
|
|
|
|
} catch (Xml_node::Nonexistent_attribute) { }
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/* initialize virtual file system */
|
2012-04-25 23:52:49 +02:00
|
|
|
static Dir_file_system
|
|
|
|
root_dir(config()->xml_node().sub_node("fstab"));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-08-20 18:24:11 +02:00
|
|
|
/* set user information */
|
|
|
|
try {
|
|
|
|
user_info()->set_info(config()->xml_node().sub_node("user"));
|
|
|
|
}
|
|
|
|
catch (...) { }
|
|
|
|
|
2012-05-24 17:06:54 +02:00
|
|
|
/* initialize network */
|
|
|
|
init_network();
|
2012-08-17 23:40:15 +02:00
|
|
|
|
2012-02-15 22:44:05 +01:00
|
|
|
/*
|
|
|
|
* Entrypoint used to virtualize child resources such as RAM, RM
|
|
|
|
*/
|
|
|
|
enum { STACK_SIZE = 1024*sizeof(long) };
|
|
|
|
static Genode::Rpc_entrypoint resources_ep(&cap, STACK_SIZE, "noux_rsc_ep");
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/* create init process */
|
|
|
|
static Genode::Signal_receiver sig_rec;
|
|
|
|
|
2012-02-17 09:27:39 +01:00
|
|
|
init_child = new Noux::Child(name_of_init_process(),
|
2012-02-24 23:43:35 +01:00
|
|
|
0,
|
2012-02-18 01:46:54 +01:00
|
|
|
pid_allocator()->alloc(),
|
2012-02-17 09:27:39 +01:00
|
|
|
&sig_rec,
|
2012-04-25 23:52:49 +02:00
|
|
|
&root_dir,
|
2012-02-17 09:27:39 +01:00
|
|
|
args_of_init_process(),
|
|
|
|
env_string_of_init_process(),
|
2012-03-16 18:22:06 +01:00
|
|
|
"/",
|
2012-02-17 09:27:39 +01:00
|
|
|
&cap,
|
2012-02-25 20:41:59 +01:00
|
|
|
parent_services,
|
2012-02-17 09:27:39 +01:00
|
|
|
resources_ep,
|
|
|
|
false);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
static Terminal::Connection terminal;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* I/O channels must be dynamically allocated to handle cases where the
|
|
|
|
* init program closes one of these.
|
|
|
|
*/
|
|
|
|
typedef Terminal_io_channel Tio; /* just a local abbreviation */
|
|
|
|
Shared_pointer<Io_channel>
|
|
|
|
channel_0(new Tio(terminal, Tio::STDIN, sig_rec), Genode::env()->heap()),
|
|
|
|
channel_1(new Tio(terminal, Tio::STDOUT, sig_rec), Genode::env()->heap()),
|
|
|
|
channel_2(new Tio(terminal, Tio::STDERR, sig_rec), Genode::env()->heap());
|
|
|
|
|
|
|
|
init_child->add_io_channel(channel_0, 0);
|
|
|
|
init_child->add_io_channel(channel_1, 1);
|
|
|
|
init_child->add_io_channel(channel_2, 2);
|
|
|
|
|
|
|
|
init_child->start();
|
|
|
|
|
|
|
|
/* handle asynchronous events */
|
|
|
|
while (init_child) {
|
|
|
|
|
|
|
|
Genode::Signal signal = sig_rec.wait_for_signal();
|
|
|
|
|
|
|
|
Signal_dispatcher *dispatcher =
|
|
|
|
static_cast<Signal_dispatcher *>(signal.context());
|
|
|
|
|
2012-03-19 22:52:26 +01:00
|
|
|
for (int i = 0; i < signal.num(); i++)
|
|
|
|
dispatcher->dispatch();
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PINF("-- exiting noux ---");
|
|
|
|
return 0;
|
|
|
|
}
|