genode/repos/ports/src/noux/syscall.cc

1024 lines
27 KiB
C++

/*
* \brief Noux syscall dispatcher
* \author Norman Feske
* \date 2011-02-14
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Noux includes */
#include <child.h>
#include <child_env.h>
#include <vfs_io_channel.h>
#include <pipe_io_channel.h>
namespace Noux {
/**
* This function is used to generate inode values from the given
* path using the FNV-1a algorithm.
*/
inline uint32_t hash_path(const char *path, size_t len)
{
const unsigned char * p = reinterpret_cast<const unsigned char*>(path);
uint32_t hash = 2166136261U;
for (size_t i = 0; i < len; i++) {
hash ^= p[i];
hash *= 16777619;
}
return hash;
}
};
bool Noux::Child::syscall(Noux::Session::Syscall sc)
{
if (_verbose.syscalls())
log("PID ", pid(), " -> SYSCALL ", Noux::Session::syscall_name(sc));
bool result = false;
try {
switch (sc) {
case SYSCALL_WRITE:
{
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.write_in.fd);
if (!io->nonblocking())
_block_for_io_channel(io, false, true, false);
if (io->check_unblock(false, true, false)) {
/* 'io->write' is expected to update '_sysio.write_out.count' */
result = io->write(_sysio);
} else {
if (io->nonblocking())
_sysio.error.write = Vfs::File_io_service::WRITE_ERR_WOULD_BLOCK;
else
_sysio.error.write = Vfs::File_io_service::WRITE_ERR_INTERRUPT;
}
break;
}
case SYSCALL_READ:
{
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.read_in.fd);
if (!io->nonblocking())
_block_for_io_channel(io, true, false, false);
if (io->check_unblock(true, false, false))
result = io->read(_sysio);
else {
if (io->nonblocking())
_sysio.error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK;
else
_sysio.error.read = Vfs::File_io_service::READ_ERR_INTERRUPT;
}
break;
}
case SYSCALL_FTRUNCATE:
{
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.ftruncate_in.fd);
_block_for_io_channel(io, false, true, false);
if (io->check_unblock(false, true, false))
result = io->ftruncate(_sysio);
else
_sysio.error.ftruncate = Vfs::File_io_service::FTRUNCATE_ERR_INTERRUPT;
break;
}
case SYSCALL_STAT:
case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */
{
/**
* We calculate the inode by hashing the path because there is
* no inode registry in noux.
*/
size_t path_len = strlen(_sysio.stat_in.path);
uint32_t path_hash = hash_path(_sysio.stat_in.path, path_len);
Vfs::Directory_service::Stat stat_out;
_sysio.error.stat = _root_dir.stat(_sysio.stat_in.path, stat_out);
result = (_sysio.error.stat == Vfs::Directory_service::STAT_OK);
/*
* Instead of using the uid/gid given by the actual file system
* we use the ones specificed in the config.
*/
if (result) {
stat_out.inode = path_hash;
}
_sysio.stat_out.st = stat_out;
break;
}
case SYSCALL_FSTAT:
{
Shared_pointer<Io_channel> io = _lookup_channel(_sysio.fstat_in.fd);
result = io->fstat(_sysio);
if (result) {
Sysio::Path path;
/**
* Only actual fd's are valid fstat targets.
*/
if (io->path(path, sizeof (path))) {
size_t path_len = strlen(path);
uint32_t path_hash = hash_path(path, path_len);
_sysio.stat_out.st.inode = path_hash;
}
}
break;
}
case SYSCALL_FCNTL:
if (_sysio.fcntl_in.cmd == Sysio::FCNTL_CMD_SET_FD_FLAGS) {
/* we assume that there is only the close-on-execve flag */
close_fd_on_execve(_sysio.fcntl_in.fd, !!_sysio.fcntl_in.long_arg);
result = true;
break;
}
if (_sysio.fcntl_in.cmd == Sysio::FCNTL_CMD_GET_FD_FLAGS) {
/* we assume that there is only the close-on-execve flag */
_sysio.fcntl_out.result = close_fd_on_execve(_sysio.fcntl_in.fd);
result = true;
break;
}
result = _lookup_channel(_sysio.fcntl_in.fd)->fcntl(_sysio);
break;
case SYSCALL_OPEN:
{
Vfs::Vfs_handle *vfs_handle = 0;
if (_root_dir.directory(_sysio.open_in.path)) {
typedef Vfs::Directory_service::Opendir_result Opendir_result;
typedef Vfs::Directory_service::Open_result Open_result;
Opendir_result opendir_result =
_root_dir.opendir(_sysio.open_in.path, false,
&vfs_handle, _heap);
switch (opendir_result) {
case Opendir_result::OPENDIR_OK:
_sysio.error.open = Open_result::OPEN_OK;
break;
case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED:
_sysio.error.open = Open_result::OPEN_ERR_UNACCESSIBLE;
break;
case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG:
_sysio.error.open = Open_result::OPEN_ERR_NAME_TOO_LONG;
break;
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS:
_sysio.error.open = Open_result::OPEN_ERR_EXISTS;
break;
case Opendir_result::OPENDIR_ERR_NO_SPACE:
_sysio.error.open = Open_result::OPEN_ERR_NO_SPACE;
break;
case Opendir_result::OPENDIR_ERR_OUT_OF_RAM:
case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS:
case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED:
_sysio.error.open = Open_result::OPEN_ERR_NO_PERM;
break;
}
} else {
_sysio.error.open = _root_dir.open(_sysio.open_in.path,
_sysio.open_in.mode,
&vfs_handle, _heap);
}
if (!vfs_handle)
break;
char const *leaf_path = _root_dir.leaf_path(_sysio.open_in.path);
/*
* 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 = _sysio.open_in.path;
Shared_pointer<Io_channel>
channel(new (_heap) Vfs_io_channel(_sysio.open_in.path,
leaf_path,
vfs_handle,
_vfs_io_waiter_registry,
_env.ep(),
_time_info,
_timer_connection),
_heap);
_sysio.open_out.fd = add_io_channel(channel);
result = true;
break;
}
case SYSCALL_CLOSE:
{
remove_io_channel(_sysio.close_in.fd);
result = true;
break;
}
case SYSCALL_IOCTL:
result = _lookup_channel(_sysio.ioctl_in.fd)->ioctl(_sysio);
break;
case SYSCALL_LSEEK:
result = _lookup_channel(_sysio.lseek_in.fd)->lseek(_sysio);
break;
case SYSCALL_DIRENT:
result = _lookup_channel(_sysio.dirent_in.fd)->dirent(_sysio);
break;
case SYSCALL_EXECVE:
typedef Child_env<sizeof(_sysio.execve_in.args)> Execve_child_env;
try {
Execve_child_env child_env(_sysio.execve_in.filename,
_sysio.execve_in.args,
_sysio.execve_in.env,
_root_dir, _vfs_io_waiter_registry,
_env.ram(), _env.rm(), _heap);
_parent_execve.execve_child(*this,
child_env.binary_name(),
child_env.args(),
child_env.env());
/*
* 'return' instead of 'break' to skip possible signal delivery,
* which might cause the old child process to exit itself
*/
return true;
}
catch (Execve_child_env::Binary_does_not_exist) {
_sysio.error.execve = Sysio::EXECVE_ERR_NO_ENTRY; }
catch (Execve_child_env::Binary_is_not_accessible) {
_sysio.error.execve = Sysio::EXECVE_ERR_ACCESS; }
catch (Execve_child_env::Binary_is_not_executable) {
_sysio.error.execve = Sysio::EXECVE_ERR_NO_EXEC; }
catch (Child::Insufficient_memory) {
_sysio.error.execve = Sysio::EXECVE_ERR_NO_MEMORY; }
break;
case SYSCALL_SELECT:
{
size_t in_fds_total = _sysio.select_in.fds.total_fds();
Sysio::Select_fds in_fds;
for (Genode::size_t i = 0; i < in_fds_total; i++)
in_fds.array[i] = _sysio.select_in.fds.array[i];
in_fds.num_rd = _sysio.select_in.fds.num_rd;
in_fds.num_wr = _sysio.select_in.fds.num_wr;
in_fds.num_ex = _sysio.select_in.fds.num_ex;
int _rd_array[in_fds_total];
int _wr_array[in_fds_total];
Genode::uint64_t timeout_sec = _sysio.select_in.timeout.sec;
Genode::uint64_t timeout_usec = _sysio.select_in.timeout.usec;
bool timeout_reached = false;
/* reset the blocker lock to the 'locked' state */
_blocker.unlock();
_blocker.lock();
/*
* 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 blocker may get unblocked for other
* conditions such as the destruction of the child.
* ...to be done.
*/
Wake_up_notifier notifiers[in_fds_total];
for (Genode::size_t i = 0; i < in_fds_total; i++) {
int fd = in_fds.array[i];
if (!fd_in_use(fd)) continue;
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
notifiers[i].lock = &_blocker;
io->register_wake_up_notifier(&notifiers[i]);
}
/**
* Register ourself at the Io_receptor_registry
*
* Each entry in the registry will be unblocked if an external
* event has happend, e.g. network I/O.
*/
Io_receptor receptor(&_blocker);
io_receptor_registry()->register_receptor(&receptor);
/*
* 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.
*/
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++) {
int fd = in_fds.array[i];
if (!fd_in_use(fd)) continue;
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
if (in_fds.watch_for_rd(i))
if (io->check_unblock(true, false, false)) {
_rd_array[unblock_rd++] = fd;
}
if (in_fds.watch_for_wr(i))
if (io->check_unblock(false, true, false)) {
_wr_array[unblock_wr++] = fd;
}
if (in_fds.watch_for_ex(i))
if (io->check_unblock(false, false, true)) {
unblock_ex++;
}
}
if (unblock_rd || unblock_wr || unblock_ex) {
/**
* Merge the fd arrays in one output array
*/
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;
/* XXX could use a pointer to select_out.fds.array instead */
for (size_t j = unblock_rd, i = 0; i < unblock_wr; i++, j++) {
_sysio.select_out.fds.array[j] = _wr_array[i];
}
_sysio.select_out.fds.num_wr = unblock_wr;
/* exception fds are currently not considered */
_sysio.select_out.fds.num_ex = unblock_ex;
result = true;
break;
}
/*
* Return if timeout is zero or timeout exceeded
*/
if (_sysio.select_in.timeout.zero() || timeout_reached) {
/*
if (timeout_reached) log("timeout_reached");
else log("timeout.zero()");
*/
_sysio.select_out.fds.num_rd = 0;
_sysio.select_out.fds.num_wr = 0;
_sysio.select_out.fds.num_ex = 0;
result = true;
break;
}
/*
* Return if signals are pending
*/
if (!_pending_signals.empty()) {
_sysio.error.select = Sysio::SELECT_ERR_INTERRUPT;
break;
}
/*
* Block at barrier except when reaching the timeout
*/
if (!_sysio.select_in.timeout.infinite()) {
Microseconds const us { timeout_sec*1000U*1000U + timeout_usec };
Armed_timeout::State ts { };
Armed_timeout timeout(ts, _blocker, _timer_connection, us);
/* block until timeout is reached or we were unblocked */
_blocker.lock();
if (ts.timed_out) {
timeout_reached = 1;
}
else {
/*
* We woke up before reaching the timeout,
* so we discard the alarm
*/
timeout.discard();
}
}
else {
/* let's block infinitely */
_blocker.lock();
}
}
/*
* Unregister barrier at watched I/O channels
*/
for (Genode::size_t i = 0; i < in_fds_total; i++) {
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(&notifiers[i]);
}
/*
* Unregister receptor
*/
io_receptor_registry()->unregister_receptor(&receptor);
break;
}
case SYSCALL_FORK:
{
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;
int const new_pid = _pid_allocator.alloc();
Child * child = nullptr;
try {
/*
* 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.
*/
child = new (_heap) Child(_child_policy.name(),
_verbose,
_user_info,
_time_info,
this,
_kill_broadcaster,
_timer_connection,
*this,
_pid_allocator,
new_pid,
_env,
_root_dir,
_vfs_io_waiter_registry,
_args,
_sysio_env.env(),
_heap,
_ref_pd, _ref_pd_cap,
_parent_services,
true,
_destruct_queue);
} catch (Child::Insufficient_memory) {
_sysio.error.fork = Sysio::FORK_NOMEM;
break;
}
Family_member::insert(child);
_assign_io_channels_to(child, false);
/* copy our address space into the new child */
try {
_pd.replay(child->pd(), _env.rm(), _heap,
child->ds_registry(), _ep);
/* 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;
result = true;
}
catch (Region_map::Region_conflict) {
error("region conflict while replaying the address space"); }
break;
}
case SYSCALL_GETPID:
{
_sysio.getpid_out.pid = pid();
return true;
}
case SYSCALL_WAIT4:
{
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);
static_cast<Child *>(exited)->submit_exit_signal();
} else {
if (_sysio.wait4_in.nohang) {
_sysio.wait4_out.pid = 0;
_sysio.wait4_out.status = 0;
} else {
_sysio.error.wait4 = Sysio::WAIT4_ERR_INTERRUPT;
break;
}
}
result = true;
break;
}
case SYSCALL_PIPE:
{
Shared_pointer<Pipe> pipe (new (_heap) Pipe, _heap);
Shared_pointer<Io_channel> pipe_sink (new (_heap) Pipe_sink_io_channel (pipe, _env.ep()), _heap);
Shared_pointer<Io_channel> pipe_source(new (_heap) Pipe_source_io_channel(pipe, _env.ep()), _heap);
_sysio.pipe_out.fd[0] = add_io_channel(pipe_source);
_sysio.pipe_out.fd[1] = add_io_channel(pipe_sink);
result = true;
break;
}
case SYSCALL_DUP2:
{
int fd = add_io_channel(io_channel_by_fd(_sysio.dup2_in.fd),
_sysio.dup2_in.to_fd);
_sysio.dup2_out.fd = fd;
result = true;
break;
}
case SYSCALL_UNLINK:
_sysio.error.unlink = _root_dir.unlink(_sysio.unlink_in.path);
result = (_sysio.error.unlink == Vfs::Directory_service::UNLINK_OK);
break;
case SYSCALL_READLINK:
{
Vfs::Vfs_handle *symlink_handle { 0 };
Vfs::Directory_service::Openlink_result openlink_result =
_root_dir.openlink(_sysio.readlink_in.path, false, &symlink_handle, _heap);
switch (openlink_result) {
case Vfs::Directory_service::OPENLINK_OK:
result = true;
break;
case Vfs::Directory_service::OPENLINK_ERR_LOOKUP_FAILED:
_sysio.error.readlink = Sysio::READLINK_ERR_NO_ENTRY;
break;
case Vfs::Directory_service::OPENLINK_ERR_NAME_TOO_LONG:
case Vfs::Directory_service::OPENLINK_ERR_NODE_ALREADY_EXISTS:
case Vfs::Directory_service::OPENLINK_ERR_NO_SPACE:
case Vfs::Directory_service::OPENLINK_ERR_OUT_OF_RAM:
case Vfs::Directory_service::OPENLINK_ERR_OUT_OF_CAPS:
case Vfs::Directory_service::OPENLINK_ERR_PERMISSION_DENIED:
_sysio.error.readlink = Sysio::READLINK_ERR_NO_PERM;
}
if (openlink_result != Vfs::Directory_service::OPENLINK_OK)
break;
Vfs::file_size count = min(_sysio.readlink_in.bufsiz,
sizeof(_sysio.readlink_out.chunk));
Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(_vfs_io_waiter_registry);
while (!symlink_handle->fs().queue_read(symlink_handle, count))
vfs_io_waiter.wait_for_io();
Vfs_handle_context read_context;
Vfs::Vfs_handle::Guard guard(symlink_handle);
symlink_handle->handler(&read_context);
Vfs::file_size out_count = 0;
Vfs::File_io_service::Read_result read_result;
for (;;) {
read_result = symlink_handle->fs().complete_read(symlink_handle,
_sysio.readlink_out.chunk,
count,
out_count);
if (read_result != Vfs::File_io_service::READ_QUEUED)
break;
read_context.vfs_io_waiter.wait_for_io();
}
/* wake up threads blocking for 'queue_*()' or 'write()' */
_vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
r.wakeup();
});
_sysio.readlink_out.count = out_count;
break;
}
case SYSCALL_RENAME:
_sysio.error.rename = _root_dir.rename(_sysio.rename_in.from_path,
_sysio.rename_in.to_path);
result = (_sysio.error.rename == Vfs::Directory_service::RENAME_OK);
break;
case SYSCALL_MKDIR:
{
Vfs::Vfs_handle *dir_handle { 0 };
typedef Vfs::Directory_service::Opendir_result Opendir_result;
Opendir_result opendir_result =
_root_dir.opendir(_sysio.mkdir_in.path, true, &dir_handle, _heap);
switch (opendir_result) {
case Opendir_result::OPENDIR_OK:
dir_handle->ds().close(dir_handle);
result = true;
break;
case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED:
_sysio.error.mkdir = Sysio::MKDIR_ERR_NO_ENTRY;
break;
case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG:
_sysio.error.mkdir = Sysio::MKDIR_ERR_NAME_TOO_LONG;
break;
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS:
_sysio.error.mkdir = Sysio::MKDIR_ERR_EXISTS;
break;
case Opendir_result::OPENDIR_ERR_NO_SPACE:
_sysio.error.mkdir = Sysio::MKDIR_ERR_NO_SPACE;
break;
case Opendir_result::OPENDIR_ERR_OUT_OF_RAM:
case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS:
case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED:
_sysio.error.mkdir = Sysio::MKDIR_ERR_NO_PERM;
break;
}
break;
}
case SYSCALL_SYMLINK:
{
Vfs::Vfs_handle *symlink_handle { 0 };
typedef Vfs::Directory_service::Openlink_result Openlink_result;
Openlink_result openlink_result =
_root_dir.openlink(_sysio.symlink_in.newpath, true,
&symlink_handle, _heap);
switch (openlink_result) {
case Openlink_result::OPENLINK_OK:
result = true;
break;
case Openlink_result::OPENLINK_ERR_LOOKUP_FAILED:
_sysio.error.symlink = Sysio::SYMLINK_ERR_NO_ENTRY;
break;
case Openlink_result::OPENLINK_ERR_NAME_TOO_LONG:
case Openlink_result::OPENLINK_ERR_NODE_ALREADY_EXISTS:
case Openlink_result::OPENLINK_ERR_NO_SPACE:
case Openlink_result::OPENLINK_ERR_OUT_OF_RAM:
case Openlink_result::OPENLINK_ERR_OUT_OF_CAPS:
case Openlink_result::OPENLINK_ERR_PERMISSION_DENIED:
_sysio.error.symlink = Sysio::SYMLINK_ERR_NO_PERM;
}
if (openlink_result != Openlink_result::OPENLINK_OK)
break;
Vfs::file_size count = strlen(_sysio.symlink_in.oldpath) + 1;
Vfs::file_size out_count;
Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(_vfs_io_waiter_registry);
for (;;) {
try {
symlink_handle->fs().write(symlink_handle, _sysio.symlink_in.oldpath,
strlen(_sysio.symlink_in.oldpath) + 1,
out_count);
break;
} catch (Vfs::File_io_service::Insufficient_buffer) {
vfs_io_waiter.wait_for_io();
}
}
/* wake up threads blocking for 'queue_*()' or 'write()' */
_vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
r.wakeup();
});
if (out_count != count) {
_sysio.error.symlink = Sysio::SYMLINK_ERR_NAME_TOO_LONG;
result = false;
}
while (!symlink_handle->fs().queue_sync(symlink_handle))
vfs_io_waiter.wait_for_io();
Vfs_handle_context sync_context;
Vfs::Vfs_handle::Guard guard(symlink_handle);
symlink_handle->handler(&sync_context);
while (symlink_handle->fs().complete_sync(symlink_handle) ==
Vfs::File_io_service::SYNC_QUEUED)
sync_context.vfs_io_waiter.wait_for_io();
/* wake up threads blocking for 'queue_*()' or 'write()' */
_vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
r.wakeup();
});
break;
}
case SYSCALL_USERINFO:
{
if (_sysio.userinfo_in.request == Sysio::USERINFO_GET_UID
|| _sysio.userinfo_in.request == Sysio::USERINFO_GET_GID) {
_sysio.userinfo_out.uid = _user_info.uid();
_sysio.userinfo_out.gid = _user_info.gid();
result = true;
break;
}
/*
* Since NOUX supports exactly one user, return false if we
* got a unknown uid.
*/
if (_sysio.userinfo_in.uid != _user_info.uid())
break;
Genode::memcpy(_sysio.userinfo_out.name,
_user_info.name().string(),
sizeof(User_info::Name));
Genode::memcpy(_sysio.userinfo_out.shell,
_user_info.shell().string(),
sizeof(User_info::Shell));
Genode::memcpy(_sysio.userinfo_out.home,
_user_info.home().string(),
sizeof(User_info::Home));
_sysio.userinfo_out.uid = _user_info.uid();
_sysio.userinfo_out.gid = _user_info.gid();
result = true;
break;
}
case SYSCALL_GETTIMEOFDAY:
{
Milliseconds const ms =
_timer_connection.curr_time().trunc_to_plain_ms();
_sysio.gettimeofday_out.sec = _time_info.initial_time();
_sysio.gettimeofday_out.sec += (ms.value / 1000);
_sysio.gettimeofday_out.usec = (ms.value % 1000) * 1000;
result = true;
break;
}
case SYSCALL_CLOCK_GETTIME:
{
/**
* It's the same procedure as in SYSCALL_GETTIMEOFDAY.
*/
Milliseconds const ms =
_timer_connection.curr_time().trunc_to_plain_ms();
switch (_sysio.clock_gettime_in.clock_id) {
/* CLOCK_SECOND is used by time(3) in the libc. */
case Sysio::CLOCK_ID_SECOND:
{
_sysio.clock_gettime_out.sec = _time_info.initial_time();
_sysio.clock_gettime_out.sec += (ms.value / 1000);
_sysio.clock_gettime_out.nsec = 0;
result = true;
break;
}
default:
{
_sysio.clock_gettime_out.sec = 0;
_sysio.clock_gettime_out.nsec = 0;
_sysio.error.clock = Sysio::CLOCK_ERR_INVALID;
break;
}
}
break;
}
case SYSCALL_UTIMES:
{
/**
* Always return true, even if 'update_modification_timestamp'
* failed to keep programs, e.g. make, happy.
*/
result = true;
char const *path = (char const *)_sysio.utimes_in.path;
unsigned long sec = _sysio.utimes_in.sec;
unsigned long usec = _sysio.utimes_in.usec;
(void)usec;
Vfs::Vfs_handle *vfs_handle = 0;
_root_dir.open(path, 0, &vfs_handle, _heap);
if (!vfs_handle) { break; }
if (!sec) {
Milliseconds const ms =
_timer_connection.curr_time().trunc_to_plain_ms();
sec = _time_info.initial_time();
sec += (ms.value / 1000);
usec = (ms.value % 1000) * 1000;
}
Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(_vfs_io_waiter_registry);
Vfs::Timestamp ts { .value = (long long)sec };
for (;;) {
if (vfs_handle->fs().update_modification_timestamp(vfs_handle, ts)) {
break;
} else {
vfs_io_waiter.wait_for_io();
}
}
_root_dir.close(vfs_handle);
break;
}
case SYSCALL_SYNC:
{
/* no errors supported at this time */
result = true;
Vfs::Vfs_handle *sync_handle;
Vfs::Directory_service::Opendir_result opendir_result =
_root_dir.opendir("/", false, &sync_handle, _heap);
if (opendir_result != Vfs::Directory_service::OPENDIR_OK)
break;
Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(_vfs_io_waiter_registry);
while (!sync_handle->fs().queue_sync(sync_handle))
vfs_io_waiter.wait_for_io();
Vfs_handle_context sync_context;
Vfs::Vfs_handle::Guard guard(sync_handle);
sync_handle->handler(&sync_context);
while (sync_handle->fs().complete_sync(sync_handle) ==
Vfs::File_io_service::SYNC_QUEUED)
sync_context.vfs_io_waiter.wait_for_io();
/* wake up threads blocking for 'queue_*()' or 'write()' */
_vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
r.wakeup();
});
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_GETDTABLESIZE:
{
_sysio.getdtablesize_out.n =
Noux::File_descriptor_registry::MAX_FILE_DESCRIPTORS;
result = true;
break;
}
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:
case SYSCALL_RECVFROM:
case SYSCALL_GETPEERNAME:
case SYSCALL_SHUTDOWN:
case SYSCALL_CONNECT:
/* network support was removed */
result = false;
break;
case SYSCALL_INVALID: break;
}
}
catch (Invalid_fd) {
_sysio.error.general = Vfs::Directory_service::ERR_FD_INVALID;
error("invalid file descriptor"); }
catch (...) { error("unexpected exception"); }
/* handle signals which might have occured */
while (!_pending_signals.empty() &&
(_sysio.pending_signals.avail_capacity() > 0)) {
_sysio.pending_signals.add(_pending_signals.get());
}
return result;
}