Remove noux runtime
The feature set of noux is fully covered by the regular C runtime now. Fixes #3696
This commit is contained in:
parent
d6bdeed38f
commit
4dd110ce5e
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux-session capability type
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-15
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INCLUDE__NOUX_SESSION__CAPABILITY_H_
|
|
||||||
#define _INCLUDE__NOUX_SESSION__CAPABILITY_H_
|
|
||||||
|
|
||||||
#include <base/capability.h>
|
|
||||||
#include <noux_session/noux_session.h>
|
|
||||||
|
|
||||||
namespace Noux { typedef Capability<Session> Session_capability; }
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__NOUX_SESSION__CAPABILITY_H_ */
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux-session client interface
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-15
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INCLUDE__NOUX_SESSION__CLIENT_H_
|
|
||||||
#define _INCLUDE__NOUX_SESSION__CLIENT_H_
|
|
||||||
|
|
||||||
#include <noux_session/noux_session.h>
|
|
||||||
#include <noux_session/capability.h>
|
|
||||||
#include <base/rpc_client.h>
|
|
||||||
#include <base/log.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
struct Session_client : Rpc_client<Session>
|
|
||||||
{
|
|
||||||
explicit Session_client(Session_capability session)
|
|
||||||
: Rpc_client<Session>(session) { }
|
|
||||||
|
|
||||||
|
|
||||||
Dataspace_capability sysio_dataspace()
|
|
||||||
{
|
|
||||||
return call<Rpc_sysio_dataspace>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool syscall(Syscall sc)
|
|
||||||
{
|
|
||||||
static bool verbose = false;
|
|
||||||
|
|
||||||
bool result = call<Rpc_syscall>(sc);
|
|
||||||
|
|
||||||
if ((result == false) && verbose)
|
|
||||||
error("syscall ", syscall_name(sc), " failed");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int next_open_fd(int start_fd)
|
|
||||||
{
|
|
||||||
return call<Rpc_next_open_fd>(start_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
Capability<Region_map> lookup_region_map(addr_t const addr)
|
|
||||||
{
|
|
||||||
return call<Rpc_lookup_region_map>(addr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__NOUX_SESSION__CLIENT_H_ */
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux connection
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-15
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INCLUDE__NOUX_SESSION__CONNECTION_H_
|
|
||||||
#define _INCLUDE__NOUX_SESSION__CONNECTION_H_
|
|
||||||
|
|
||||||
#include <noux_session/client.h>
|
|
||||||
#include <base/connection.h>
|
|
||||||
|
|
||||||
namespace Noux { struct Connection; }
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Connection : Genode::Connection<Session>, Session_client
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
Connection(Genode::Env &env)
|
|
||||||
:
|
|
||||||
Genode::Connection<Session>(env, session(env.parent(), "")),
|
|
||||||
Session_client(cap())
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove session ID of the noux session from the ID space.
|
|
||||||
*
|
|
||||||
* This must by done before reinitializing the noux connection in a
|
|
||||||
* freshly forked process. Otherwise, an overwritten 'Noux::Connection'
|
|
||||||
* object would still be referenced by the AVL tree of the the ID space.
|
|
||||||
*/
|
|
||||||
void discard_session_id()
|
|
||||||
{
|
|
||||||
_id_space_element.~Element();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__NOUX_SESSION__CONNECTION_H_ */
|
|
|
@ -1,182 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux session interface
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-15
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INCLUDE__NOUX_SESSION__NOUX_SESSION_H_
|
|
||||||
#define _INCLUDE__NOUX_SESSION__NOUX_SESSION_H_
|
|
||||||
|
|
||||||
#include <base/stdint.h>
|
|
||||||
#include <session/session.h>
|
|
||||||
#include <dataspace/capability.h>
|
|
||||||
#include <region_map/region_map.h>
|
|
||||||
|
|
||||||
#define NOUX_DECL_SYSCALL_NAME(name) \
|
|
||||||
case SYSCALL_##name: return #name;
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
struct Session : Genode::Session
|
|
||||||
{
|
|
||||||
static const char *service_name() { return "Noux"; }
|
|
||||||
|
|
||||||
enum { CAP_QUOTA = 3 };
|
|
||||||
|
|
||||||
virtual ~Session() { }
|
|
||||||
|
|
||||||
virtual Dataspace_capability sysio_dataspace() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return leaf region map that covers a given address
|
|
||||||
*
|
|
||||||
* \param addr address that is covered by the requested region map
|
|
||||||
*/
|
|
||||||
virtual Capability<Region_map> lookup_region_map(addr_t const addr) = 0;
|
|
||||||
|
|
||||||
enum Syscall {
|
|
||||||
SYSCALL_WRITE,
|
|
||||||
SYSCALL_READ,
|
|
||||||
SYSCALL_STAT,
|
|
||||||
SYSCALL_LSTAT,
|
|
||||||
SYSCALL_FSTAT,
|
|
||||||
SYSCALL_FTRUNCATE,
|
|
||||||
SYSCALL_FCNTL,
|
|
||||||
SYSCALL_OPEN,
|
|
||||||
SYSCALL_CLOSE,
|
|
||||||
SYSCALL_IOCTL,
|
|
||||||
SYSCALL_LSEEK,
|
|
||||||
SYSCALL_DIRENT,
|
|
||||||
SYSCALL_EXECVE,
|
|
||||||
SYSCALL_SELECT,
|
|
||||||
SYSCALL_FORK,
|
|
||||||
SYSCALL_GETPID,
|
|
||||||
SYSCALL_WAIT4,
|
|
||||||
SYSCALL_PIPE,
|
|
||||||
SYSCALL_DUP2,
|
|
||||||
SYSCALL_UNLINK,
|
|
||||||
SYSCALL_READLINK,
|
|
||||||
SYSCALL_RENAME,
|
|
||||||
SYSCALL_MKDIR,
|
|
||||||
SYSCALL_SYMLINK,
|
|
||||||
SYSCALL_SOCKET,
|
|
||||||
SYSCALL_GETSOCKOPT,
|
|
||||||
SYSCALL_SETSOCKOPT,
|
|
||||||
SYSCALL_ACCEPT,
|
|
||||||
SYSCALL_BIND,
|
|
||||||
SYSCALL_LISTEN,
|
|
||||||
SYSCALL_SEND,
|
|
||||||
SYSCALL_SENDTO,
|
|
||||||
SYSCALL_RECV,
|
|
||||||
SYSCALL_RECVFROM,
|
|
||||||
SYSCALL_GETPEERNAME,
|
|
||||||
SYSCALL_SHUTDOWN,
|
|
||||||
SYSCALL_CONNECT,
|
|
||||||
SYSCALL_USERINFO,
|
|
||||||
SYSCALL_GETTIMEOFDAY,
|
|
||||||
SYSCALL_CLOCK_GETTIME,
|
|
||||||
SYSCALL_UTIMES,
|
|
||||||
SYSCALL_SYNC,
|
|
||||||
SYSCALL_KILL,
|
|
||||||
SYSCALL_GETDTABLESIZE,
|
|
||||||
SYSCALL_INVALID = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
static char const *syscall_name(Syscall sc)
|
|
||||||
{
|
|
||||||
switch (sc) {
|
|
||||||
NOUX_DECL_SYSCALL_NAME(WRITE)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(READ)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(STAT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(LSTAT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(FSTAT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(FTRUNCATE)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(FCNTL)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(OPEN)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(CLOSE)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(IOCTL)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(LSEEK)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(DIRENT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(EXECVE)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SELECT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(FORK)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(GETPID)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(WAIT4)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(PIPE)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(DUP2)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(UNLINK)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(READLINK)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(RENAME)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(MKDIR)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SYMLINK)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SOCKET)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(GETSOCKOPT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SETSOCKOPT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(ACCEPT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(BIND)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(LISTEN)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SEND)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SENDTO)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(RECV)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(RECVFROM)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(GETPEERNAME)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SHUTDOWN)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(CONNECT)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(USERINFO)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(GETTIMEOFDAY)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(CLOCK_GETTIME)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(UTIMES)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(SYNC)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(KILL)
|
|
||||||
NOUX_DECL_SYSCALL_NAME(GETDTABLESIZE)
|
|
||||||
case SYSCALL_INVALID: return 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform syscall
|
|
||||||
*
|
|
||||||
* The syscall arguments and results are communicated via the shared
|
|
||||||
* sysio dataspace.
|
|
||||||
*
|
|
||||||
* \return true on success
|
|
||||||
*/
|
|
||||||
virtual bool syscall(Syscall syscall) = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the next open file descriptor, starting from (and including)
|
|
||||||
* 'start_fd'.
|
|
||||||
*
|
|
||||||
* \return the next open file descriptor or -1
|
|
||||||
*/
|
|
||||||
virtual int next_open_fd(int start_fd) = 0;
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
** RPC declaration **
|
|
||||||
*********************/
|
|
||||||
|
|
||||||
GENODE_RPC(Rpc_sysio_dataspace, Dataspace_capability, sysio_dataspace);
|
|
||||||
GENODE_RPC(Rpc_lookup_region_map, Capability<Region_map>,
|
|
||||||
lookup_region_map, addr_t);
|
|
||||||
GENODE_RPC(Rpc_syscall, bool, syscall, Syscall);
|
|
||||||
GENODE_RPC(Rpc_next_open_fd, int, next_open_fd, int);
|
|
||||||
|
|
||||||
GENODE_RPC_INTERFACE(Rpc_sysio_dataspace, Rpc_lookup_region_map,
|
|
||||||
Rpc_syscall, Rpc_next_open_fd);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef NOUX_DECL_SYSCALL_NAME
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__NOUX_SESSION__NOUX_SESSION_H_ */
|
|
||||||
|
|
|
@ -1,495 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Facility for passing system-call arguments
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-15
|
|
||||||
*
|
|
||||||
* The 'Sysio' data structure is shared between the noux environment
|
|
||||||
* and the child. It is used to pass system-call arguments that would
|
|
||||||
* traditionally be transferred via 'copy_from_user' and 'copy_to_user'.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _INCLUDE__NOUX_SESSION__SYSIO_H_
|
|
||||||
#define _INCLUDE__NOUX_SESSION__SYSIO_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <os/ring_buffer.h>
|
|
||||||
#include <util/misc_math.h>
|
|
||||||
#include <vfs/file_system.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define SYSIO_DECL(syscall_name, args, results) \
|
|
||||||
struct args syscall_name##_in; \
|
|
||||||
struct results syscall_name##_out;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
using namespace Genode;
|
|
||||||
struct Sysio;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Sysio
|
|
||||||
{
|
|
||||||
/* signal numbers must match with libc signal numbers */
|
|
||||||
enum Signal {
|
|
||||||
SIG_INT = 2,
|
|
||||||
SIG_CHLD = 20,
|
|
||||||
SIG_WINCH = 28
|
|
||||||
};
|
|
||||||
|
|
||||||
enum { SIGNAL_QUEUE_SIZE = 32 };
|
|
||||||
Ring_buffer<enum Signal, SIGNAL_QUEUE_SIZE,
|
|
||||||
Ring_buffer_unsynchronized> pending_signals;
|
|
||||||
|
|
||||||
enum { MAX_PATH_LEN = 512 };
|
|
||||||
typedef char Path[MAX_PATH_LEN];
|
|
||||||
|
|
||||||
enum { CHUNK_SIZE = 64*1024 };
|
|
||||||
typedef char Chunk[CHUNK_SIZE];
|
|
||||||
|
|
||||||
enum { ARGS_MAX_LEN = 16*1024 };
|
|
||||||
typedef char Args[ARGS_MAX_LEN];
|
|
||||||
|
|
||||||
enum { ENV_MAX_LEN = 6*1024 };
|
|
||||||
typedef char Env[ENV_MAX_LEN];
|
|
||||||
|
|
||||||
typedef __SIZE_TYPE__ size_t;
|
|
||||||
typedef long int ssize_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flags of 'mode' argument of open syscall
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
OPEN_MODE_RDONLY = 0,
|
|
||||||
OPEN_MODE_WRONLY = 1,
|
|
||||||
OPEN_MODE_RDWR = 2,
|
|
||||||
OPEN_MODE_ACCMODE = 3,
|
|
||||||
OPEN_MODE_CREATE = 0x0800, /* libc O_EXCL */
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef Vfs::Directory_service::Stat Stat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Argument structure used for ioctl syscall
|
|
||||||
*/
|
|
||||||
struct Ioctl_in
|
|
||||||
{
|
|
||||||
typedef Vfs::File_io_service::Ioctl_opcode Opcode;
|
|
||||||
|
|
||||||
typedef Vfs::File_io_service::Ioctl_value Value;
|
|
||||||
|
|
||||||
Opcode request;
|
|
||||||
int argp;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structure carrying the result values of 'ioctl' syscalls
|
|
||||||
*/
|
|
||||||
typedef Vfs::File_io_service::Ioctl_out Ioctl_out;
|
|
||||||
|
|
||||||
enum Lseek_whence { LSEEK_SET, LSEEK_CUR, LSEEK_END };
|
|
||||||
|
|
||||||
enum { DIRENT_MAX_NAME_LEN = Vfs::Directory_service::Dirent::Name::MAX_LEN };
|
|
||||||
|
|
||||||
typedef Vfs::Directory_service::Dirent_type Dirent_type;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Must be POD (in contrast to the VFS type) because it's used in a union
|
|
||||||
*/
|
|
||||||
struct Dirent
|
|
||||||
{
|
|
||||||
unsigned long fileno;
|
|
||||||
Dirent_type type;
|
|
||||||
char name[DIRENT_MAX_NAME_LEN];
|
|
||||||
|
|
||||||
Dirent & operator= (Vfs::Directory_service::Dirent const &dirent)
|
|
||||||
{
|
|
||||||
fileno = dirent.fileno;
|
|
||||||
type = dirent.type;
|
|
||||||
memcpy(name, dirent.name.buf, DIRENT_MAX_NAME_LEN);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Fcntl_cmd {
|
|
||||||
FCNTL_CMD_GET_FILE_STATUS_FLAGS,
|
|
||||||
FCNTL_CMD_SET_FILE_STATUS_FLAGS,
|
|
||||||
FCNTL_CMD_SET_FD_FLAGS,
|
|
||||||
FCNTL_CMD_GET_FD_FLAGS
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
FCNTL_FILE_STATUS_FLAG_NONBLOCK = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input and output argument type of select syscall
|
|
||||||
*/
|
|
||||||
struct Select_fds
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Maximum number of file descriptors supported
|
|
||||||
*/
|
|
||||||
enum { MAX_FDS = 32U };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of file descriptors to watch for read operations (rd),
|
|
||||||
* write operations (wr), or exceptions (ex).
|
|
||||||
*/
|
|
||||||
size_t num_rd,
|
|
||||||
num_wr,
|
|
||||||
num_ex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array containing the file descriptors, starting with those
|
|
||||||
* referring to rd, followed by wr, and finally ex
|
|
||||||
*/
|
|
||||||
int array[MAX_FDS];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return total number of file descriptors contained in the array
|
|
||||||
*/
|
|
||||||
size_t total_fds() const {
|
|
||||||
return min(num_rd + num_wr + num_ex, (size_t)MAX_FDS); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for maximum population of fds array
|
|
||||||
*/
|
|
||||||
bool max_fds_exceeded() const
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Note that even though the corner case of num_rd + num_wr +
|
|
||||||
* num_ex == MAX_FDS is technically valid, this condition hints
|
|
||||||
* at a possible attempt to over popupate the array (see the
|
|
||||||
* implementation of 'select' in the Noux libc plugin). Hence,
|
|
||||||
* we regard this case as an error, too.
|
|
||||||
*/
|
|
||||||
return total_fds() >= MAX_FDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true of fd set index should be watched for reading
|
|
||||||
*/
|
|
||||||
bool watch_for_rd(unsigned i) const { return i < num_rd; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if fd set index should be watched for writing
|
|
||||||
*/
|
|
||||||
bool watch_for_wr(unsigned i) const {
|
|
||||||
return (i >= num_rd) && (i < num_rd + num_wr); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if fd set index should be watched for exceptions
|
|
||||||
*/
|
|
||||||
bool watch_for_ex(unsigned i) const {
|
|
||||||
return (i >= num_rd + num_wr) && (i < total_fds()); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Select_timeout
|
|
||||||
{
|
|
||||||
long sec, usec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set timeout to infinity
|
|
||||||
*/
|
|
||||||
void set_infinite() { sec = -1; usec = -1; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the timeout is infinite
|
|
||||||
*/
|
|
||||||
bool infinite() const { return (sec == -1) && (usec == -1); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the timeout is zero
|
|
||||||
*/
|
|
||||||
bool zero() const { return (sec == 0) && (usec == 0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Socket related structures
|
|
||||||
*/
|
|
||||||
enum { MAX_HOSTNAME_LEN = 255 };
|
|
||||||
typedef char Hostname[MAX_HOSTNAME_LEN];
|
|
||||||
|
|
||||||
enum { MAX_SERVNAME_LEN = 255 };
|
|
||||||
typedef char Servname[MAX_SERVNAME_LEN];
|
|
||||||
|
|
||||||
enum { MAX_ADDRINFO_RESULTS = 4 };
|
|
||||||
|
|
||||||
struct in_addr
|
|
||||||
{
|
|
||||||
unsigned int s_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sockaddr
|
|
||||||
{
|
|
||||||
unsigned char sa_len;
|
|
||||||
unsigned char sa_family;
|
|
||||||
char sa_data[14];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sockaddr_in {
|
|
||||||
unsigned char sin_len;
|
|
||||||
unsigned char sin_family;
|
|
||||||
unsigned short sin_port;
|
|
||||||
struct in_addr sin_addr;
|
|
||||||
char sin_zero[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef unsigned socklen_t;
|
|
||||||
|
|
||||||
struct addrinfo {
|
|
||||||
int ai_flags;
|
|
||||||
int ai_family;
|
|
||||||
int ai_socktype;
|
|
||||||
int ai_protocol;
|
|
||||||
socklen_t ai_addrlen;
|
|
||||||
struct sockaddr *ai_addr;
|
|
||||||
char *ai_canonname;
|
|
||||||
struct addrinfo *ai_next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Addrinfo {
|
|
||||||
struct addrinfo addrinfo;
|
|
||||||
struct sockaddr ai_addr;
|
|
||||||
char ai_canonname[255];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* user info defintions
|
|
||||||
*/
|
|
||||||
enum { USERINFO_GET_ALL = 0, USERINFO_GET_UID, USERINFO_GET_GID };
|
|
||||||
enum { MAX_USERNAME_LEN = 32 };
|
|
||||||
typedef char User[MAX_USERNAME_LEN];
|
|
||||||
enum { MAX_SHELL_LEN = 16 };
|
|
||||||
typedef char Shell[MAX_SHELL_LEN];
|
|
||||||
enum { MAX_HOME_LEN = 128 };
|
|
||||||
typedef char Home[MAX_HOME_LEN];
|
|
||||||
typedef unsigned int Uid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* time/clock definitions
|
|
||||||
*/
|
|
||||||
enum Clock_Id { CLOCK_ID_SECOND };
|
|
||||||
|
|
||||||
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
|
||||||
enum Mkdir_error { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY,
|
|
||||||
MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM,
|
|
||||||
MKDIR_ERR_NAME_TOO_LONG };
|
|
||||||
enum Readlink_error { READLINK_ERR_NO_ENTRY, READLINK_ERR_NO_PERM };
|
|
||||||
enum Symlink_error { SYMLINK_ERR_EXISTS, SYMLINK_ERR_NO_ENTRY,
|
|
||||||
SYMLINK_ERR_NO_SPACE, SYMLINK_ERR_NO_PERM,
|
|
||||||
SYMLINK_ERR_NAME_TOO_LONG };
|
|
||||||
|
|
||||||
enum Execve_error { EXECVE_ERR_NO_ENTRY = Vfs::Directory_service::NUM_GENERAL_ERRORS,
|
|
||||||
EXECVE_ERR_NO_MEMORY,
|
|
||||||
EXECVE_ERR_NO_EXEC,
|
|
||||||
EXECVE_ERR_ACCESS};
|
|
||||||
enum Fork_error { FORK_NOMEM = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
|
||||||
enum Select_error { SELECT_ERR_INTERRUPT };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Socket related errors
|
|
||||||
*/
|
|
||||||
enum Accept_error { ACCEPT_ERR_AGAIN, ACCEPT_ERR_WOULD_BLOCK,
|
|
||||||
ACCEPT_ERR_INVALID, ACCEPT_ERR_NO_MEMORY,
|
|
||||||
ACCEPT_ERR_NOT_SUPPORTED };
|
|
||||||
|
|
||||||
enum Bind_error { BIND_ERR_ACCESS, BIND_ERR_ADDR_IN_USE,
|
|
||||||
BIND_ERR_INVALID, BIND_ERR_NO_MEMORY };
|
|
||||||
|
|
||||||
enum Connect_error { CONNECT_ERR_ACCESS, CONNECT_ERR_AGAIN,
|
|
||||||
CONNECT_ERR_ALREADY, CONNECT_ERR_CONN_REFUSED,
|
|
||||||
CONNECT_ERR_NO_PERM, CONNECT_ERR_ADDR_IN_USE,
|
|
||||||
CONNECT_ERR_IN_PROGRESS, CONNECT_ERR_IS_CONNECTED,
|
|
||||||
CONNECT_ERR_RESET, CONNECT_ERR_ABORTED,
|
|
||||||
CONNECT_ERR_NO_ROUTE };
|
|
||||||
|
|
||||||
enum Listen_error { LISTEN_ERR_ADDR_IN_USE, LISTEN_ERR_NOT_SUPPORTED };
|
|
||||||
|
|
||||||
enum Recv_error { RECV_ERR_AGAIN, RECV_ERR_WOULD_BLOCK,
|
|
||||||
RECV_ERR_CONN_REFUSED, RECV_ERR_INVALID,
|
|
||||||
RECV_ERR_NOT_CONNECTED, RECV_ERR_NO_MEMORY };
|
|
||||||
|
|
||||||
enum Send_error { SEND_ERR_AGAIN, SEND_ERR_WOULD_BLOCK,
|
|
||||||
SEND_ERR_CONNECTION_RESET, SEND_ERR_INVALID,
|
|
||||||
SEND_ERR_IS_CONNECTED, SEND_ERR_NO_MEMORY };
|
|
||||||
|
|
||||||
enum Shutdown_error { SHUTDOWN_ERR_NOT_CONNECTED };
|
|
||||||
|
|
||||||
enum Socket_error { SOCKET_ERR_ACCESS, SOCKET_ERR_NO_AF_SUPPORT,
|
|
||||||
SOCKET_ERR_INVALID, SOCKET_ERR_NO_MEMORY };
|
|
||||||
|
|
||||||
enum Clock_error { CLOCK_ERR_INVALID, CLOCK_ERR_FAULT, CLOCK_ERR_NO_PERM };
|
|
||||||
|
|
||||||
enum Utimes_error { UTIMES_ERR_ACCESS, UTIMES_ERR_FAUL, UTIMES_ERR_EIO,
|
|
||||||
UTIMES_ERR_NAME_TOO_LONG, UTIMES_ERR_NO_ENTRY,
|
|
||||||
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 {
|
|
||||||
Vfs::Directory_service::General_error general;
|
|
||||||
Vfs::Directory_service::Stat_result stat;
|
|
||||||
Vfs::File_io_service::Ftruncate_result ftruncate;
|
|
||||||
Vfs::Directory_service::Open_result open;
|
|
||||||
Vfs::Directory_service::Unlink_result unlink;
|
|
||||||
Vfs::Directory_service::Rename_result rename;
|
|
||||||
Vfs::File_io_service::Read_result read;
|
|
||||||
Vfs::File_io_service::Write_result write;
|
|
||||||
Vfs::File_io_service::Ioctl_result ioctl;
|
|
||||||
|
|
||||||
Fcntl_error fcntl;
|
|
||||||
Mkdir_error mkdir;
|
|
||||||
Readlink_error readlink;
|
|
||||||
Symlink_error symlink;
|
|
||||||
Execve_error execve;
|
|
||||||
Select_error select;
|
|
||||||
Accept_error accept;
|
|
||||||
Bind_error bind;
|
|
||||||
Connect_error connect;
|
|
||||||
Listen_error listen;
|
|
||||||
Recv_error recv;
|
|
||||||
Send_error send;
|
|
||||||
Shutdown_error shutdown;
|
|
||||||
Socket_error socket;
|
|
||||||
Clock_error clock;
|
|
||||||
Utimes_error utimes;
|
|
||||||
Wait4_error wait4;
|
|
||||||
Kill_error kill;
|
|
||||||
Fork_error fork;
|
|
||||||
|
|
||||||
} error;
|
|
||||||
|
|
||||||
union {
|
|
||||||
|
|
||||||
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; },
|
|
||||||
{ size_t count; });
|
|
||||||
|
|
||||||
SYSIO_DECL(stat, { Path path; }, { Stat st; });
|
|
||||||
|
|
||||||
SYSIO_DECL(symlink, { Path oldpath; Path newpath; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(fstat, { int fd; }, { Stat st; });
|
|
||||||
|
|
||||||
SYSIO_DECL(ftruncate, { int fd; off_t length; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(fcntl, { int fd; long long_arg; Fcntl_cmd cmd; },
|
|
||||||
{ int result; });
|
|
||||||
|
|
||||||
SYSIO_DECL(open, { Path path; int mode; }, { int fd; });
|
|
||||||
|
|
||||||
SYSIO_DECL(close, { int fd; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(ioctl, : Ioctl_in { int fd; }, : Ioctl_out { });
|
|
||||||
|
|
||||||
SYSIO_DECL(lseek, { int fd; off_t offset; Lseek_whence whence; },
|
|
||||||
{ off_t offset; });
|
|
||||||
|
|
||||||
SYSIO_DECL(dirent, { int fd; }, { Dirent entry; });
|
|
||||||
|
|
||||||
SYSIO_DECL(read, { int fd; size_t count; },
|
|
||||||
{ Chunk chunk; size_t count; });
|
|
||||||
|
|
||||||
SYSIO_DECL(readlink, { Path path; size_t bufsiz; },
|
|
||||||
{ Chunk chunk; size_t count; });
|
|
||||||
|
|
||||||
SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(select, { Select_fds fds; Select_timeout timeout; },
|
|
||||||
{ Select_fds fds; });
|
|
||||||
|
|
||||||
SYSIO_DECL(fork, { addr_t ip; addr_t sp;
|
|
||||||
addr_t parent_cap_addr; },
|
|
||||||
{ int pid; });
|
|
||||||
|
|
||||||
SYSIO_DECL(getpid, { }, { int pid; });
|
|
||||||
|
|
||||||
SYSIO_DECL(wait4, { int pid; bool nohang; },
|
|
||||||
{ int pid; int status; });
|
|
||||||
SYSIO_DECL(pipe, { }, { int fd[2]; });
|
|
||||||
|
|
||||||
SYSIO_DECL(dup2, { int fd; int to_fd; }, { int fd; });
|
|
||||||
|
|
||||||
SYSIO_DECL(unlink, { Path path; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(socket, { int domain; int type; int protocol; },
|
|
||||||
{ int fd; });
|
|
||||||
|
|
||||||
/* XXX for now abuse Chunk for passing optval */
|
|
||||||
SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval;
|
|
||||||
socklen_t optlen; }, { int result; });
|
|
||||||
|
|
||||||
SYSIO_DECL(setsockopt, { int fd; int level;
|
|
||||||
int optname; Chunk optval;
|
|
||||||
socklen_t optlen; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; },
|
|
||||||
{ int fd; });
|
|
||||||
|
|
||||||
SYSIO_DECL(bind, { int fd; struct sockaddr addr;
|
|
||||||
socklen_t addrlen; }, { int result; });
|
|
||||||
|
|
||||||
SYSIO_DECL(getpeername, { int fd; struct sockaddr addr;
|
|
||||||
socklen_t addrlen; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(listen, { int fd; int type; int backlog; },
|
|
||||||
{ int result; });
|
|
||||||
|
|
||||||
SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; },
|
|
||||||
{ ssize_t len; });
|
|
||||||
|
|
||||||
SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags;
|
|
||||||
struct sockaddr dest_addr; socklen_t addrlen; },
|
|
||||||
{ ssize_t len; });
|
|
||||||
|
|
||||||
SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; },
|
|
||||||
{ size_t len; });
|
|
||||||
|
|
||||||
SYSIO_DECL(recvfrom, { int fd; Chunk buf; size_t len; int flags;
|
|
||||||
struct sockaddr src_addr; socklen_t addrlen; },
|
|
||||||
{ size_t len; });
|
|
||||||
|
|
||||||
SYSIO_DECL(shutdown, { int fd; int how; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; },
|
|
||||||
{ int result; });
|
|
||||||
|
|
||||||
SYSIO_DECL(userinfo, { int request; Uid uid; },
|
|
||||||
{ User name; Uid uid; Uid gid; Shell shell;
|
|
||||||
Home home; });
|
|
||||||
|
|
||||||
SYSIO_DECL(gettimeofday, { }, { unsigned long sec; unsigned int usec; });
|
|
||||||
|
|
||||||
SYSIO_DECL(clock_gettime, { Clock_Id clock_id; },
|
|
||||||
{ unsigned long sec; unsigned long nsec; });
|
|
||||||
|
|
||||||
SYSIO_DECL(utimes, { Path path; unsigned long sec; unsigned long usec; },
|
|
||||||
{ });
|
|
||||||
|
|
||||||
SYSIO_DECL(sync, { }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(kill, { int pid; Signal sig; }, { });
|
|
||||||
|
|
||||||
SYSIO_DECL(getdtablesize, { }, { int n; });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#undef SYSIO_DECL
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__NOUX_SESSION__SYSIO_H_ */
|
|
|
@ -1,11 +0,0 @@
|
||||||
SRC_CC = plugin.cc
|
|
||||||
|
|
||||||
LIBS += libc
|
|
||||||
|
|
||||||
REP_INC_DIR += src/lib/libc
|
|
||||||
|
|
||||||
vpath %.cc $(REP_DIR)/src/lib/libc_noux
|
|
||||||
|
|
||||||
SHARED_LIB = yes
|
|
||||||
|
|
||||||
CC_CXX_WARN_STRICT =
|
|
|
@ -1,35 +0,0 @@
|
||||||
_Z14init_libc_nouxv T
|
|
||||||
_Z15noux_connectionv T
|
|
||||||
_Z4nouxv T
|
|
||||||
_Z5sysiov T
|
|
||||||
_ZN4Libc10vfs_configEv T
|
|
||||||
_ZN4Libc6configEv T
|
|
||||||
_sigaction T
|
|
||||||
_sigprocmask T
|
|
||||||
_wait4 T
|
|
||||||
chmod T
|
|
||||||
clock_gettime T
|
|
||||||
endpwent T
|
|
||||||
fork T
|
|
||||||
fork_trampoline T
|
|
||||||
getdtablesize T
|
|
||||||
getegid T
|
|
||||||
geteuid T
|
|
||||||
getgid T
|
|
||||||
getpid T
|
|
||||||
getppid T
|
|
||||||
getpwuid T
|
|
||||||
getrlimit T
|
|
||||||
getrusage T
|
|
||||||
gettimeofday T
|
|
||||||
getuid T
|
|
||||||
kill T
|
|
||||||
nanosleep T
|
|
||||||
sbrk T
|
|
||||||
select T
|
|
||||||
sigaction T
|
|
||||||
sigprocmask T
|
|
||||||
sleep T
|
|
||||||
sync T
|
|
||||||
utimes T
|
|
||||||
vfork T
|
|
|
@ -1,4 +1,4 @@
|
||||||
MIRROR_FROM_REP_DIR := lib/symbols/libc_noux mk/noux.mk mk/gnu_build.mk
|
MIRROR_FROM_REP_DIR := mk/noux.mk mk/gnu_build.mk
|
||||||
|
|
||||||
content:$(MIRROR_FROM_REP_DIR) LICENSE
|
content:$(MIRROR_FROM_REP_DIR) LICENSE
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
MIRRORED_FROM_REP_DIR := include/noux_session
|
|
||||||
include $(GENODE_DIR)/repos/os/recipes/api/session.inc
|
|
|
@ -1 +0,0 @@
|
||||||
2020-03-25 5b839717ad8acc5742ac2e7f78f2c50d3d77b1ca
|
|
|
@ -1,5 +1,4 @@
|
||||||
_/raw/system_shell
|
_/raw/system_shell
|
||||||
_/src/noux
|
|
||||||
_/src/bash-minimal
|
_/src/bash-minimal
|
||||||
_/src/vim-minimal
|
_/src/vim-minimal
|
||||||
_/src/coreutils-minimal
|
_/src/coreutils-minimal
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
MIRROR_FROM_REP_DIR := src/noux src/lib/libc_noux lib/mk/libc_noux.mk
|
|
||||||
|
|
||||||
content: $(MIRROR_FROM_REP_DIR)
|
|
||||||
|
|
||||||
$(MIRROR_FROM_REP_DIR):
|
|
||||||
$(mirror_from_rep_dir)
|
|
||||||
|
|
||||||
MIRROR_FROM_LIBPORTS := include/libc-plugin \
|
|
||||||
src/lib/libc/internal/mem_alloc.h \
|
|
||||||
src/lib/libc/internal/types.h \
|
|
||||||
src/lib/libc/internal/legacy.h
|
|
||||||
|
|
||||||
content: $(MIRROR_FROM_LIBPORTS)
|
|
||||||
|
|
||||||
$(MIRROR_FROM_LIBPORTS):
|
|
||||||
mkdir -p $(dir $@)
|
|
||||||
cp -r $(GENODE_DIR)/repos/libports/$@ $@
|
|
||||||
|
|
||||||
MIRROR_FROM_OS := include/init/child_policy.h
|
|
||||||
|
|
||||||
content: $(MIRROR_FROM_OS)
|
|
||||||
|
|
||||||
$(MIRROR_FROM_OS):
|
|
||||||
mkdir -p $(dir $@)
|
|
||||||
cp -r $(GENODE_DIR)/repos/os/$@ $@
|
|
||||||
|
|
||||||
content: LICENSE
|
|
||||||
|
|
||||||
LICENSE:
|
|
||||||
cp $(GENODE_DIR)/LICENSE $@
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
2020-03-25 d13b8d86250ac0f22c9ea71fc4cfce2759aeba18
|
|
|
@ -1,10 +0,0 @@
|
||||||
base
|
|
||||||
os
|
|
||||||
vfs
|
|
||||||
libc
|
|
||||||
noux
|
|
||||||
noux_session
|
|
||||||
terminal_session
|
|
||||||
timer_session
|
|
||||||
posix
|
|
||||||
rtc_session
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +0,0 @@
|
||||||
TARGET = libc_noux
|
|
||||||
LIBS = posix libc_noux
|
|
||||||
|
|
||||||
CC_CXX_WARN_STRICT =
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Handling command-line for Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-01-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__ARGS_H_
|
|
||||||
#define _NOUX__ARGS_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/attached_ram_dataspace.h>
|
|
||||||
#include <util/string.h>
|
|
||||||
#include <base/log.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Args;
|
|
||||||
class Args_dataspace;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Args
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
char * const _buf;
|
|
||||||
size_t const _buf_size;
|
|
||||||
size_t _len;
|
|
||||||
|
|
||||||
size_t _free_size() const
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Keep space for two trailing zeros, indicating the end of the
|
|
||||||
* stream of strings.
|
|
||||||
*/
|
|
||||||
return _buf_size - _len - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
class Overrun { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param buf argument buffer
|
|
||||||
* \param buf_size size of argument buffer in character,
|
|
||||||
* must be at least 2
|
|
||||||
*/
|
|
||||||
Args(char *buf, size_t buf_size)
|
|
||||||
: _buf(buf), _buf_size(buf_size), _len(0)
|
|
||||||
{
|
|
||||||
if (buf_size <= 2)
|
|
||||||
throw Overrun();
|
|
||||||
|
|
||||||
/* search end of string stream (two subsequent zeros) */
|
|
||||||
for (; (_buf[_len] || _buf[_len + 1]) && (_len < _buf_size - 2); _len++);
|
|
||||||
|
|
||||||
/* ensure termination of argument buffer */
|
|
||||||
_buf[_buf_size - 1] = 0;
|
|
||||||
_buf[_buf_size - 2] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Args() : _buf(0), _buf_size(0), _len(0) { }
|
|
||||||
|
|
||||||
bool valid() const { return _buf_size > 0; }
|
|
||||||
|
|
||||||
size_t len() const { return _len; }
|
|
||||||
|
|
||||||
char const *base() const { return _buf; }
|
|
||||||
|
|
||||||
void append(char const *arg)
|
|
||||||
{
|
|
||||||
size_t const arg_len = strlen(arg);
|
|
||||||
|
|
||||||
if (arg_len > _free_size())
|
|
||||||
throw Overrun();
|
|
||||||
|
|
||||||
strncpy(_buf + _len, arg, _buf_size - _len);
|
|
||||||
|
|
||||||
_len += arg_len + 1; /* keep null termination between strings */
|
|
||||||
|
|
||||||
/* mark end of stream of strings */
|
|
||||||
_buf[_len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0, j = 0; _buf[i] && (i < _buf_size - 2);
|
|
||||||
i += strlen(&_buf[i]) + 1, j++)
|
|
||||||
log("arg(", j, "): \"", Cstring(&_buf[i]), "\"");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Args_dataspace : private Attached_ram_dataspace, Args
|
|
||||||
{
|
|
||||||
Args_dataspace(Ram_allocator &ram, Region_map &rm,
|
|
||||||
size_t size, Args const &from = Args())
|
|
||||||
:
|
|
||||||
Attached_ram_dataspace(ram, rm, size),
|
|
||||||
Args(local_addr<char>(), size)
|
|
||||||
{
|
|
||||||
if (from.len() > size - 2)
|
|
||||||
throw Overrun();
|
|
||||||
|
|
||||||
memcpy(_buf, from.base(), from.len() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
using Attached_ram_dataspace::cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__ARGS_H_ */
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Timeout mechanism for 'select'
|
|
||||||
* \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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__ARMED_TIMEOUT_H_
|
|
||||||
#define _NOUX__ARMED_TIMEOUT_H_
|
|
||||||
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
|
|
||||||
namespace Noux { class Armed_timeout; }
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Armed_timeout : Noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct State : Noncopyable
|
|
||||||
{
|
|
||||||
State() { }
|
|
||||||
bool timed_out { };
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
State &_state;
|
|
||||||
Lock &_blocker;
|
|
||||||
|
|
||||||
Timer::One_shot_timeout<Armed_timeout> _one_shot_timeout;
|
|
||||||
|
|
||||||
void _handle_one_shot_timeout(Duration)
|
|
||||||
{
|
|
||||||
_state.timed_out = true;
|
|
||||||
_blocker.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Armed_timeout(State &state, Lock &blocker,
|
|
||||||
Timer::Connection &timer, Microseconds microseconds)
|
|
||||||
:
|
|
||||||
_state(state), _blocker(blocker),
|
|
||||||
_one_shot_timeout(timer, *this, &Armed_timeout::_handle_one_shot_timeout)
|
|
||||||
{
|
|
||||||
_state.timed_out = false;
|
|
||||||
_one_shot_timeout.schedule(microseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void discard() { _one_shot_timeout.discard(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__ARMED_TIMEOUT_H_ */
|
|
|
@ -1,586 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux child process
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__CHILD_H_
|
|
||||||
#define _NOUX__CHILD_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/attached_ram_dataspace.h>
|
|
||||||
#include <base/attached_rom_dataspace.h>
|
|
||||||
#include <vfs/dir_file_system.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <file_descriptor_registry.h>
|
|
||||||
#include <noux_session/capability.h>
|
|
||||||
#include <args.h>
|
|
||||||
#include <environment.h>
|
|
||||||
#include <cpu_session_component.h>
|
|
||||||
#include <pd_session_component.h>
|
|
||||||
#include <child_policy.h>
|
|
||||||
#include <io_receptor_registry.h>
|
|
||||||
#include <destruct_queue.h>
|
|
||||||
#include <interrupt_handler.h>
|
|
||||||
#include <kill_broadcaster.h>
|
|
||||||
#include <parent_execve.h>
|
|
||||||
#include <empty_rom_service.h>
|
|
||||||
#include <local_rom_service.h>
|
|
||||||
#include <verbose.h>
|
|
||||||
#include <user_info.h>
|
|
||||||
#include <armed_timeout.h>
|
|
||||||
#include <time_info.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
class Pid_allocator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return singleton instance of Io_receptor_registry
|
|
||||||
*/
|
|
||||||
Io_receptor_registry *io_receptor_registry();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return lock for protecting the signal queue
|
|
||||||
*/
|
|
||||||
Genode::Lock &signal_lock();
|
|
||||||
|
|
||||||
class Child_config;
|
|
||||||
class Child;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true is child is the init process
|
|
||||||
*/
|
|
||||||
bool init_process(Child *child);
|
|
||||||
void init_process_exited(int);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocator for process IDs
|
|
||||||
*/
|
|
||||||
class Noux::Pid_allocator
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Lock _lock { };
|
|
||||||
int _num_pids { 0 };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
int alloc()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
return _num_pids++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Child_config : Attached_ram_dataspace
|
|
||||||
{
|
|
||||||
enum { CONFIG_DS_SIZE = 4096 };
|
|
||||||
|
|
||||||
Child_config(Ram_allocator &ram, Region_map &local_rm, Verbose const &verbose)
|
|
||||||
:
|
|
||||||
Attached_ram_dataspace(ram, local_rm, CONFIG_DS_SIZE)
|
|
||||||
{
|
|
||||||
Xml_generator xml(local_addr<char>(), CONFIG_DS_SIZE, "config", [&] ()
|
|
||||||
{
|
|
||||||
if (verbose.ld())
|
|
||||||
xml.attribute("ld_verbose", "yes");
|
|
||||||
|
|
||||||
xml.node("ld", [&] () {
|
|
||||||
xml.node("library", [&] () {
|
|
||||||
xml.attribute("rom", "libc_noux.lib.so"); }); });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Child : public Rpc_object<Session>,
|
|
||||||
public File_descriptor_registry,
|
|
||||||
public Family_member,
|
|
||||||
public Destruct_queue::Element<Child>,
|
|
||||||
public Interrupt_handler
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Noncopyable
|
|
||||||
*/
|
|
||||||
Child(Child const &);
|
|
||||||
Child &operator = (Child const &);
|
|
||||||
|
|
||||||
Child_policy::Name const _name;
|
|
||||||
|
|
||||||
Verbose const &_verbose;
|
|
||||||
|
|
||||||
User_info const &_user_info;
|
|
||||||
|
|
||||||
Time_info const &_time_info;
|
|
||||||
|
|
||||||
Parent_exit *_parent_exit;
|
|
||||||
Kill_broadcaster &_kill_broadcaster;
|
|
||||||
Timer::Connection &_timer_connection;
|
|
||||||
Parent_execve &_parent_execve;
|
|
||||||
Pid_allocator &_pid_allocator;
|
|
||||||
|
|
||||||
Env &_env;
|
|
||||||
|
|
||||||
Vfs::File_system &_root_dir;
|
|
||||||
|
|
||||||
Vfs_io_waiter_registry &_vfs_io_waiter_registry;
|
|
||||||
Vfs_handle_context _vfs_handle_context { };
|
|
||||||
|
|
||||||
Destruct_queue &_destruct_queue;
|
|
||||||
|
|
||||||
void _handle_destruct() { _destruct_queue.insert(this); }
|
|
||||||
|
|
||||||
Signal_handler<Child> _destruct_handler {
|
|
||||||
_env.ep(), *this, &Child::_handle_destruct };
|
|
||||||
|
|
||||||
Allocator &_heap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entrypoint used to serve the RPC interfaces of the
|
|
||||||
* locally-provided services
|
|
||||||
*/
|
|
||||||
enum { STACK_SIZE = 64*1024 };
|
|
||||||
Rpc_entrypoint _ep { &_env.pd(), STACK_SIZE, "noux_process", false };
|
|
||||||
|
|
||||||
Pd_session &_ref_pd;
|
|
||||||
Pd_session_capability const _ref_pd_cap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry of dataspaces owned by the Noux process
|
|
||||||
*/
|
|
||||||
Dataspace_registry _ds_registry { _heap };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locally-provided PD service
|
|
||||||
*/
|
|
||||||
typedef Local_service<Pd_session_component> Pd_service;
|
|
||||||
Pd_session_component _pd { _heap, _env, _ep, _name, _ds_registry };
|
|
||||||
Pd_service::Single_session_factory _pd_factory { _pd };
|
|
||||||
Pd_service _pd_service { _pd_factory };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locally-provided CPU service
|
|
||||||
*/
|
|
||||||
typedef Local_service<Cpu_session_component> Cpu_service;
|
|
||||||
Cpu_session_component _cpu { _env, _ep, _name, false, _ds_registry };
|
|
||||||
Cpu_service::Single_session_factory _cpu_factory { _cpu };
|
|
||||||
Cpu_service _cpu_service { _cpu_factory };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Locally-provided Noux service
|
|
||||||
*/
|
|
||||||
Capability_guard _cap_guard { _ep, *this };
|
|
||||||
|
|
||||||
typedef Local_service<Rpc_object<Session> > Noux_service;
|
|
||||||
Noux_service::Single_session_factory _noux_factory { *this };
|
|
||||||
Noux_service _noux_service { _noux_factory };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Locally-provided ROM service
|
|
||||||
*/
|
|
||||||
Empty_rom_factory _empty_rom_factory { _heap, _ep };
|
|
||||||
Empty_rom_service _empty_rom_service { _empty_rom_factory };
|
|
||||||
Local_rom_factory _rom_factory { _heap, _env, _ep, _root_dir,
|
|
||||||
_vfs_io_waiter_registry, _ds_registry };
|
|
||||||
Local_rom_service _rom_service { _rom_factory };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command line arguments
|
|
||||||
*/
|
|
||||||
enum { ARGS_DS_SIZE = sizeof(Sysio::Args) };
|
|
||||||
|
|
||||||
Args_dataspace _args;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Environment variables
|
|
||||||
*/
|
|
||||||
Environment _sysio_env;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Child configuration
|
|
||||||
*/
|
|
||||||
Child_config _config { _ref_pd, _env.rm(), _verbose };
|
|
||||||
|
|
||||||
enum { PAGE_SIZE = 4096, PAGE_MASK = ~(PAGE_SIZE - 1) };
|
|
||||||
enum { SYSIO_DS_SIZE = PAGE_MASK & (sizeof(Sysio) + PAGE_SIZE - 1) };
|
|
||||||
|
|
||||||
Attached_ram_dataspace _sysio_ds { _ref_pd, _env.rm(), SYSIO_DS_SIZE };
|
|
||||||
Sysio &_sysio = *_sysio_ds.local_addr<Sysio>();
|
|
||||||
|
|
||||||
typedef Ring_buffer<enum Sysio::Signal, Sysio::SIGNAL_QUEUE_SIZE>
|
|
||||||
Signal_queue;
|
|
||||||
Signal_queue _pending_signals { };
|
|
||||||
|
|
||||||
Parent_services &_parent_services;
|
|
||||||
|
|
||||||
Static_dataspace_info _sysio_ds_info;
|
|
||||||
Static_dataspace_info _args_ds_info;
|
|
||||||
Static_dataspace_info _sysio_env_ds_info;
|
|
||||||
Static_dataspace_info _config_ds_info;
|
|
||||||
|
|
||||||
Child_policy _child_policy;
|
|
||||||
|
|
||||||
Genode::Child _child;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception type for failed file-descriptor lookup
|
|
||||||
*/
|
|
||||||
class Invalid_fd { };
|
|
||||||
|
|
||||||
Shared_pointer<Io_channel> _lookup_channel(int fd) const
|
|
||||||
{
|
|
||||||
Shared_pointer<Io_channel> channel = io_channel_by_fd(fd);
|
|
||||||
|
|
||||||
if (channel)
|
|
||||||
return channel;
|
|
||||||
|
|
||||||
throw Invalid_fd();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Let specified child inherit our file descriptors
|
|
||||||
*/
|
|
||||||
void _assign_io_channels_to(Child *child, bool skip_when_close_on_execve_set)
|
|
||||||
{
|
|
||||||
for (int fd = 0; fd < MAX_FILE_DESCRIPTORS; fd++)
|
|
||||||
if (fd_in_use(fd)) {
|
|
||||||
if (skip_when_close_on_execve_set && close_fd_on_execve(fd))
|
|
||||||
continue;
|
|
||||||
child->add_io_channel(io_channel_by_fd(fd), fd);
|
|
||||||
child->close_fd_on_execve(fd, close_fd_on_execve(fd));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Block until the IO channel is ready for reading or writing or an
|
|
||||||
* exception occured.
|
|
||||||
*
|
|
||||||
* \param io the IO channel
|
|
||||||
* \param rd check for data available for reading
|
|
||||||
* \param wr check for readiness for writing
|
|
||||||
* \param ex check for exceptions
|
|
||||||
*/
|
|
||||||
void _block_for_io_channel(Shared_pointer<Io_channel> &io,
|
|
||||||
bool rd, bool wr, bool ex)
|
|
||||||
{
|
|
||||||
/* reset the blocker lock to the 'locked' state */
|
|
||||||
_blocker.unlock();
|
|
||||||
_blocker.lock();
|
|
||||||
|
|
||||||
Wake_up_notifier notifier(&_blocker);
|
|
||||||
io->register_wake_up_notifier(¬ifier);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (io->check_unblock(rd, wr, ex) ||
|
|
||||||
!_pending_signals.empty())
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* block (unless the lock got unlocked in the meantime) */
|
|
||||||
_blocker.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
io->unregister_wake_up_notifier(¬ifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _destruct()
|
|
||||||
{
|
|
||||||
_ep.dissolve(this);
|
|
||||||
|
|
||||||
if (init_process(this))
|
|
||||||
init_process_exited(_child_policy.exit_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct Insufficient_memory : Exception { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param forked false if the child is spawned directly from
|
|
||||||
* an executable binary (i.e., the init process,
|
|
||||||
* or children created via execve, or
|
|
||||||
* true if the child is a fork from another child
|
|
||||||
*
|
|
||||||
* \throw Insufficent_memory if the child could not be started by
|
|
||||||
* the parent
|
|
||||||
*/
|
|
||||||
Child(Child_policy::Name const &name,
|
|
||||||
Verbose const &verbose,
|
|
||||||
User_info const &user_info,
|
|
||||||
Time_info const &time_info,
|
|
||||||
Parent_exit *parent_exit,
|
|
||||||
Kill_broadcaster &kill_broadcaster,
|
|
||||||
Timer::Connection &timer_connection,
|
|
||||||
Parent_execve &parent_execve,
|
|
||||||
Pid_allocator &pid_allocator,
|
|
||||||
int pid,
|
|
||||||
Env &env,
|
|
||||||
Vfs::File_system &root_dir,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Args const &args,
|
|
||||||
Sysio::Env const &sysio_env,
|
|
||||||
Allocator &heap,
|
|
||||||
Pd_session &ref_pd,
|
|
||||||
Pd_session_capability ref_pd_cap,
|
|
||||||
Parent_services &parent_services,
|
|
||||||
bool forked,
|
|
||||||
Destruct_queue &destruct_queue)
|
|
||||||
:
|
|
||||||
Family_member(pid),
|
|
||||||
Destruct_queue::Element<Child>(heap),
|
|
||||||
_name(name),
|
|
||||||
_verbose(verbose),
|
|
||||||
_user_info(user_info),
|
|
||||||
_time_info(time_info),
|
|
||||||
_parent_exit(parent_exit),
|
|
||||||
_kill_broadcaster(kill_broadcaster),
|
|
||||||
_timer_connection(timer_connection),
|
|
||||||
_parent_execve(parent_execve),
|
|
||||||
_pid_allocator(pid_allocator),
|
|
||||||
_env(env),
|
|
||||||
_root_dir(root_dir),
|
|
||||||
_vfs_io_waiter_registry(vfs_io_waiter_registry),
|
|
||||||
_destruct_queue(destruct_queue),
|
|
||||||
_heap(heap),
|
|
||||||
_ref_pd (ref_pd), _ref_pd_cap (ref_pd_cap),
|
|
||||||
_args(ref_pd, _env.rm(), ARGS_DS_SIZE, args),
|
|
||||||
_sysio_env(_ref_pd, _env.rm(), sysio_env),
|
|
||||||
_parent_services(parent_services),
|
|
||||||
_sysio_ds_info(_ds_registry, _sysio_ds.cap()),
|
|
||||||
_args_ds_info(_ds_registry, _args.cap()),
|
|
||||||
_sysio_env_ds_info(_ds_registry, _sysio_env.cap()),
|
|
||||||
_config_ds_info(_ds_registry, _config.cap()),
|
|
||||||
_child_policy(name, forked,
|
|
||||||
_args.cap(), _sysio_env.cap(), _config.cap(),
|
|
||||||
_ep, _pd_service, _cpu_service,
|
|
||||||
_noux_service, _empty_rom_service,
|
|
||||||
_rom_service, _parent_services,
|
|
||||||
*this, parent_exit, *this, _destruct_handler,
|
|
||||||
ref_pd, ref_pd_cap,
|
|
||||||
_verbose.enabled()),
|
|
||||||
_child(_env.rm(), _ep, _child_policy)
|
|
||||||
{
|
|
||||||
if (_verbose.enabled())
|
|
||||||
_args.dump();
|
|
||||||
|
|
||||||
if (!_child.main_thread_cap().valid()) {
|
|
||||||
_destruct();
|
|
||||||
throw Insufficient_memory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~Child() { _destruct(); }
|
|
||||||
|
|
||||||
void start() { _ep.activate(); }
|
|
||||||
|
|
||||||
void start_forked_main_thread(addr_t ip, addr_t sp, addr_t parent_cap_addr)
|
|
||||||
{
|
|
||||||
/* poke parent_cap_addr into child's address space */
|
|
||||||
Capability<Parent>::Raw const raw = _child.parent_cap().raw();
|
|
||||||
|
|
||||||
_pd.poke(_env.rm(), parent_cap_addr, (char *)&raw, sizeof(raw));
|
|
||||||
|
|
||||||
/* start execution of new main thread at supplied trampoline */
|
|
||||||
_cpu.start_main_thread(ip, sp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void submit_exit_signal()
|
|
||||||
{
|
|
||||||
if (init_process(this)) {
|
|
||||||
log("init process exited");
|
|
||||||
|
|
||||||
/* trigger exit of main event loop */
|
|
||||||
init_process_exited(_child_policy.exit_value());
|
|
||||||
} else {
|
|
||||||
Signal_transmitter(_destruct_handler).submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Pd_session_component &pd() { return _pd; }
|
|
||||||
|
|
||||||
Dataspace_registry &ds_registry() { return _ds_registry; }
|
|
||||||
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
** Noux session interface **
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
Dataspace_capability sysio_dataspace() override
|
|
||||||
{
|
|
||||||
return _sysio_ds.cap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Capability<Region_map> lookup_region_map(addr_t const addr) override
|
|
||||||
{
|
|
||||||
return _pd.lookup_region_map(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool syscall(Syscall sc) override;
|
|
||||||
|
|
||||||
int next_open_fd(int start_fd) override
|
|
||||||
{
|
|
||||||
if (start_fd >= 0)
|
|
||||||
for (int fd = start_fd; fd < MAX_FILE_DESCRIPTORS; fd++)
|
|
||||||
if (fd_in_use(fd))
|
|
||||||
return fd;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
** File_descriptor_registry overrides **
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find out if the IO channel associated with 'fd' has more file
|
|
||||||
* descriptors associated with it
|
|
||||||
*/
|
|
||||||
bool _is_the_only_fd_for_io_channel(int fd,
|
|
||||||
Shared_pointer<Io_channel> io_channel)
|
|
||||||
{
|
|
||||||
for (int f = 0; f < MAX_FILE_DESCRIPTORS; f++) {
|
|
||||||
if ((f != fd) &&
|
|
||||||
fd_in_use(f) &&
|
|
||||||
(io_channel_by_fd(f) == io_channel))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1) override
|
|
||||||
{
|
|
||||||
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_listener *l = new (_heap) Io_channel_listener(this);
|
|
||||||
io_channel->register_interrupt_handler(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_io_channel(int fd) override
|
|
||||||
{
|
|
||||||
Shared_pointer<Io_channel> io_channel = _lookup_channel(fd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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_listener *l = io_channel->lookup_io_channel_listener(this);
|
|
||||||
io_channel->unregister_interrupt_handler(l);
|
|
||||||
Genode::destroy(_heap, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
File_descriptor_registry::remove_io_channel(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() override
|
|
||||||
{
|
|
||||||
for (int fd = 0; fd < MAX_FILE_DESCRIPTORS; fd++)
|
|
||||||
try {
|
|
||||||
remove_io_channel(fd);
|
|
||||||
} catch (Invalid_fd) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
|
||||||
** Family_member interface **
|
|
||||||
*****************************/
|
|
||||||
|
|
||||||
void submit_signal(Noux::Sysio::Signal sig) override
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_pending_signals.add(sig);
|
|
||||||
} catch (Signal_queue::Overflow) {
|
|
||||||
error("signal queue is full - signal dropped");
|
|
||||||
}
|
|
||||||
|
|
||||||
_blocker.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
Family_member *do_execve(const char *filename,
|
|
||||||
Args const &args,
|
|
||||||
Sysio::Env const &env) override
|
|
||||||
{
|
|
||||||
Lock::Guard signal_lock_guard(signal_lock());
|
|
||||||
|
|
||||||
Child *child = new (_heap) Child(filename,
|
|
||||||
_verbose,
|
|
||||||
_user_info,
|
|
||||||
_time_info,
|
|
||||||
_parent_exit,
|
|
||||||
_kill_broadcaster,
|
|
||||||
_timer_connection,
|
|
||||||
_parent_execve,
|
|
||||||
_pid_allocator,
|
|
||||||
pid(),
|
|
||||||
_env,
|
|
||||||
_root_dir,
|
|
||||||
_vfs_io_waiter_registry,
|
|
||||||
args,
|
|
||||||
env,
|
|
||||||
_heap,
|
|
||||||
_ref_pd, _ref_pd_cap,
|
|
||||||
_parent_services,
|
|
||||||
false,
|
|
||||||
_destruct_queue);
|
|
||||||
|
|
||||||
_assign_io_channels_to(child, true);
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
Signal_transmitter(_destruct_handler).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(Sysio::Signal signal) override
|
|
||||||
{
|
|
||||||
submit_signal(signal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__CHILD_H_ */
|
|
|
@ -1,232 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux child environment
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-07-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__CHILD_ENV_H_
|
|
||||||
#define _NOUX__CHILD_ENV_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/string.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
#include <range_checked_index.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
template <size_t> class Child_env;
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \param ARGS_SIZE size of the argument buffer given
|
|
||||||
* to the constructor
|
|
||||||
*/
|
|
||||||
template <Noux::size_t ARGS_SIZE>
|
|
||||||
class Noux::Child_env
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
static unsigned constexpr MAX_LEN_INTERPRETER_LINE = 128;
|
|
||||||
|
|
||||||
char const *_binary_name { nullptr };
|
|
||||||
char _args[ARGS_SIZE + MAX_LEN_INTERPRETER_LINE];
|
|
||||||
Sysio::Env _env;
|
|
||||||
|
|
||||||
void _process_env(Sysio::Env env)
|
|
||||||
{
|
|
||||||
memcpy(_env, env, sizeof(Sysio::Env));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify that the file exists and return its size
|
|
||||||
*/
|
|
||||||
Vfs::file_size _file_size(Vfs::File_system &root_dir,
|
|
||||||
char const *binary_name)
|
|
||||||
{
|
|
||||||
Vfs::Directory_service::Stat stat_out;
|
|
||||||
Vfs::Directory_service::Stat_result stat_result;
|
|
||||||
|
|
||||||
stat_result = root_dir.stat(binary_name, stat_out);
|
|
||||||
|
|
||||||
switch (stat_result) {
|
|
||||||
case Vfs::Directory_service::STAT_OK:
|
|
||||||
break;
|
|
||||||
case Vfs::Directory_service::STAT_ERR_NO_ENTRY:
|
|
||||||
throw Binary_does_not_exist();
|
|
||||||
case Vfs::Directory_service::STAT_ERR_NO_PERM:
|
|
||||||
throw Binary_is_not_accessible();
|
|
||||||
}
|
|
||||||
|
|
||||||
return stat_out.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify that the file is a valid ELF executable
|
|
||||||
*/
|
|
||||||
void _verify_elf(char const *file)
|
|
||||||
{
|
|
||||||
if ((file[0] != 0x7f) ||
|
|
||||||
(file[1] != 'E') ||
|
|
||||||
(file[2] != 'L') ||
|
|
||||||
(file[3] != 'F'))
|
|
||||||
throw Binary_is_not_executable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the case that the given binary needs an interpreter
|
|
||||||
*/
|
|
||||||
void _process_binary_name_and_args(char const *binary_name,
|
|
||||||
char const *args,
|
|
||||||
Vfs::File_system &root_dir,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Ram_allocator &ram,
|
|
||||||
Region_map &rm,
|
|
||||||
Allocator &alloc)
|
|
||||||
{
|
|
||||||
Vfs::file_size binary_size = _file_size(root_dir, binary_name);
|
|
||||||
|
|
||||||
if (binary_size == 0)
|
|
||||||
throw Binary_is_not_executable();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We may have to check the dataspace twice because the binary
|
|
||||||
* could be a script that uses an interpreter which might not
|
|
||||||
* exist.
|
|
||||||
*/
|
|
||||||
Reconstructible<Vfs_dataspace> binary_ds {
|
|
||||||
root_dir, vfs_io_waiter_registry,
|
|
||||||
binary_name, ram, rm, alloc
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!binary_ds->ds.valid())
|
|
||||||
throw Binary_is_not_executable();
|
|
||||||
|
|
||||||
Reconstructible<Attached_dataspace> attached_binary_ds(rm, binary_ds->ds);
|
|
||||||
|
|
||||||
char const *binary_addr = attached_binary_ds->local_addr<char const>();
|
|
||||||
|
|
||||||
/* look for '#!' */
|
|
||||||
if ((binary_addr[0] != '#') || (binary_addr[1] != '!')) {
|
|
||||||
_binary_name = binary_name;
|
|
||||||
Genode::memcpy(_args, args, ARGS_SIZE);
|
|
||||||
_verify_elf(binary_addr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find end of line */
|
|
||||||
Range_checked_index<unsigned int>
|
|
||||||
eol(2, min(binary_size, MAX_LEN_INTERPRETER_LINE));
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (binary_addr[eol] != '\n') eol++;
|
|
||||||
} catch (Index_out_of_range) { }
|
|
||||||
|
|
||||||
/* skip leading spaces */
|
|
||||||
Range_checked_index<unsigned int>
|
|
||||||
interpreter_line_cursor(2, eol);
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (binary_addr[interpreter_line_cursor] == ' ')
|
|
||||||
interpreter_line_cursor++;
|
|
||||||
} catch (Index_out_of_range) { }
|
|
||||||
|
|
||||||
/* no interpreter name found */
|
|
||||||
if (interpreter_line_cursor == eol)
|
|
||||||
throw Binary_does_not_exist();
|
|
||||||
|
|
||||||
int interpreter_name_start = interpreter_line_cursor;
|
|
||||||
|
|
||||||
/* find end of interpreter name */
|
|
||||||
try {
|
|
||||||
while (binary_addr[interpreter_line_cursor] != ' ')
|
|
||||||
interpreter_line_cursor++;
|
|
||||||
} catch (Index_out_of_range) { }
|
|
||||||
|
|
||||||
size_t interpreter_name_len =
|
|
||||||
interpreter_line_cursor - interpreter_name_start;
|
|
||||||
|
|
||||||
/* copy interpreter name into argument buffer */
|
|
||||||
unsigned int args_buf_cursor = 0;
|
|
||||||
Genode::strncpy(&_args[args_buf_cursor],
|
|
||||||
&binary_addr[interpreter_name_start],
|
|
||||||
interpreter_name_len + 1);
|
|
||||||
_binary_name = &_args[args_buf_cursor];
|
|
||||||
args_buf_cursor += interpreter_name_len + 1;
|
|
||||||
|
|
||||||
/* skip more spaces */
|
|
||||||
try {
|
|
||||||
while (binary_addr[interpreter_line_cursor] == ' ')
|
|
||||||
interpreter_line_cursor++;
|
|
||||||
} catch (Index_out_of_range) { }
|
|
||||||
|
|
||||||
/* append interpreter arguments to argument buffer */
|
|
||||||
size_t interpreter_args_len = eol - interpreter_line_cursor;
|
|
||||||
if (interpreter_args_len > 0) {
|
|
||||||
Genode::strncpy(&_args[args_buf_cursor],
|
|
||||||
&binary_addr[interpreter_line_cursor],
|
|
||||||
interpreter_args_len + 1);
|
|
||||||
args_buf_cursor += interpreter_args_len + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* append script arguments to argument buffer */
|
|
||||||
Genode::memcpy(&_args[args_buf_cursor],
|
|
||||||
args, ARGS_SIZE);
|
|
||||||
|
|
||||||
/* check if interpreter exists and is executable */
|
|
||||||
|
|
||||||
binary_size = _file_size(root_dir, _binary_name);
|
|
||||||
|
|
||||||
if (binary_size == 0)
|
|
||||||
throw Binary_is_not_executable();
|
|
||||||
|
|
||||||
binary_ds.construct(root_dir, vfs_io_waiter_registry,
|
|
||||||
_binary_name, ram,
|
|
||||||
rm, alloc);
|
|
||||||
|
|
||||||
if (!binary_ds->ds.valid())
|
|
||||||
throw Binary_is_not_executable();
|
|
||||||
|
|
||||||
attached_binary_ds.construct(rm, binary_ds->ds);
|
|
||||||
|
|
||||||
_verify_elf(attached_binary_ds->local_addr<char const>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct Binary_does_not_exist : Exception { };
|
|
||||||
struct Binary_is_not_accessible : Exception { };
|
|
||||||
struct Binary_is_not_executable : Exception { };
|
|
||||||
|
|
||||||
Child_env(char const *binary_name,
|
|
||||||
char const *args, Sysio::Env env,
|
|
||||||
Vfs::File_system &root_dir,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Ram_allocator &ram,
|
|
||||||
Region_map &rm,
|
|
||||||
Allocator &alloc)
|
|
||||||
{
|
|
||||||
_process_env(env);
|
|
||||||
_process_binary_name_and_args(binary_name, args, root_dir,
|
|
||||||
vfs_io_waiter_registry,
|
|
||||||
ram, rm, alloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *binary_name() const { return _binary_name; }
|
|
||||||
|
|
||||||
Args args() { return Args(_args, sizeof(_args)); }
|
|
||||||
|
|
||||||
Sysio::Env const &env() const { return _env; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__CHILD_ENV_H_ */
|
|
|
@ -1,218 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux child policy
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-02-25
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__CHILD_POLICY_H_
|
|
||||||
#define _NOUX__CHILD_POLICY_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <init/child_policy.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <family_member.h>
|
|
||||||
#include <parent_exit.h>
|
|
||||||
#include <file_descriptor_registry.h>
|
|
||||||
#include <empty_rom_service.h>
|
|
||||||
#include <local_rom_service.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
typedef Registered<Genode::Parent_service> Parent_service;
|
|
||||||
typedef Registry<Parent_service> Parent_services;
|
|
||||||
|
|
||||||
typedef Local_service<Pd_session_component> Pd_service;
|
|
||||||
typedef Local_service<Cpu_session_component> Cpu_service;
|
|
||||||
typedef Local_service<Rpc_object<Session> > Noux_service;
|
|
||||||
|
|
||||||
class Child_policy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Child_policy : public Genode::Child_policy
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Noncopyable
|
|
||||||
*/
|
|
||||||
Child_policy(Child_policy const &);
|
|
||||||
Child_policy &operator = (Child_policy const &);
|
|
||||||
|
|
||||||
Name const _name;
|
|
||||||
bool _forked;
|
|
||||||
Init::Child_policy_provide_rom_file _args_policy;
|
|
||||||
Init::Child_policy_provide_rom_file _env_policy;
|
|
||||||
Init::Child_policy_provide_rom_file _config_policy;
|
|
||||||
Pd_service &_pd_service;
|
|
||||||
Cpu_service &_cpu_service;
|
|
||||||
Noux_service &_noux_service;
|
|
||||||
Empty_rom_service &_empty_rom_service;
|
|
||||||
Local_rom_service &_rom_service;
|
|
||||||
Parent_services &_parent_services;
|
|
||||||
Family_member &_family_member;
|
|
||||||
Parent_exit *_parent_exit;
|
|
||||||
File_descriptor_registry &_file_descriptor_registry;
|
|
||||||
Signal_context_capability _destruct_context_cap;
|
|
||||||
Pd_session &_ref_pd;
|
|
||||||
Pd_session_capability _ref_pd_cap;
|
|
||||||
int _exit_value;
|
|
||||||
bool _verbose;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static Genode::Service *_find_service(Genode::Registry<T> &services,
|
|
||||||
Genode::Service::Name const &name)
|
|
||||||
{
|
|
||||||
Genode::Service *service = nullptr;
|
|
||||||
services.for_each([&] (T &s) {
|
|
||||||
if (!service && (s.name() == name))
|
|
||||||
service = &s; });
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find suitable service for a given session request
|
|
||||||
*
|
|
||||||
* \throw Service_denied
|
|
||||||
*/
|
|
||||||
Service &_matching_service(Service::Name const &service_name,
|
|
||||||
Session_label const &label)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Route initial ROM requests (binary and linker) of a forked child
|
|
||||||
* to the empty ROM service, since the ROMs are already attached in
|
|
||||||
* the replayed region map.
|
|
||||||
*/
|
|
||||||
if (_forked && (service_name == Genode::Rom_session::service_name())) {
|
|
||||||
if (label.last_element() == name()) return _empty_rom_service;
|
|
||||||
if (label.last_element() == linker_name()) return _empty_rom_service;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Service *service = nullptr;
|
|
||||||
|
|
||||||
/* check for local ROM requests */
|
|
||||||
if ((service = _args_policy .resolve_session_request_with_label(service_name, label))
|
|
||||||
|| (service = _env_policy .resolve_session_request_with_label(service_name, label))
|
|
||||||
|| (service = _config_policy.resolve_session_request_with_label(service_name, label)))
|
|
||||||
return *service;
|
|
||||||
|
|
||||||
/* check for local services */
|
|
||||||
if (service_name == Genode::Cpu_session::service_name()) return _cpu_service;
|
|
||||||
if (service_name == Genode::Rom_session::service_name()) return _rom_service;
|
|
||||||
if (service_name == Genode::Pd_session::service_name()) return _pd_service;
|
|
||||||
if (service_name == Noux::Session::service_name()) return _noux_service;
|
|
||||||
|
|
||||||
/* check for parent services */
|
|
||||||
if ((service = _find_service(_parent_services, service_name)))
|
|
||||||
return *service;
|
|
||||||
|
|
||||||
throw Service_denied();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Child_policy(Name const &name,
|
|
||||||
bool forked,
|
|
||||||
Dataspace_capability args_ds,
|
|
||||||
Dataspace_capability env_ds,
|
|
||||||
Dataspace_capability config_ds,
|
|
||||||
Rpc_entrypoint &entrypoint,
|
|
||||||
Pd_service &pd_service,
|
|
||||||
Cpu_service &cpu_service,
|
|
||||||
Noux_service &noux_service,
|
|
||||||
Empty_rom_service &empty_rom_service,
|
|
||||||
Local_rom_service &rom_service,
|
|
||||||
Parent_services &parent_services,
|
|
||||||
Family_member &family_member,
|
|
||||||
Parent_exit *parent_exit,
|
|
||||||
File_descriptor_registry &file_descriptor_registry,
|
|
||||||
Signal_context_capability destruct_context_cap,
|
|
||||||
Pd_session &ref_pd,
|
|
||||||
Pd_session_capability ref_pd_cap,
|
|
||||||
bool verbose)
|
|
||||||
:
|
|
||||||
_name(name), _forked(forked),
|
|
||||||
_args_policy( "args", args_ds, &entrypoint),
|
|
||||||
_env_policy( "env", env_ds, &entrypoint),
|
|
||||||
_config_policy("config", config_ds, &entrypoint),
|
|
||||||
_pd_service(pd_service),
|
|
||||||
_cpu_service(cpu_service),
|
|
||||||
_noux_service(noux_service),
|
|
||||||
_empty_rom_service(empty_rom_service),
|
|
||||||
_rom_service(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_pd (ref_pd), _ref_pd_cap (ref_pd_cap),
|
|
||||||
_exit_value(~0),
|
|
||||||
_verbose(verbose)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
int exit_value() const { return _exit_value; }
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
** Child policy interface **
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
Name name() const override { return _name; }
|
|
||||||
|
|
||||||
Pd_session &ref_pd() override { return _ref_pd; }
|
|
||||||
Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
|
|
||||||
|
|
||||||
void init(Pd_session &session, Pd_session_capability) override
|
|
||||||
{
|
|
||||||
session.ref_account(_ref_pd_cap);
|
|
||||||
}
|
|
||||||
|
|
||||||
Route resolve_session_request(Service::Name const &service_name,
|
|
||||||
Session_label const &label) override
|
|
||||||
{
|
|
||||||
return Route { .service = _matching_service(service_name, label),
|
|
||||||
.label = label,
|
|
||||||
.diag = Session::Diag() };
|
|
||||||
}
|
|
||||||
|
|
||||||
void exit(int exit_value) override
|
|
||||||
{
|
|
||||||
_exit_value = exit_value;
|
|
||||||
|
|
||||||
if (_verbose || (exit_value != 0))
|
|
||||||
log("child ", _name, " exited with exit value ", exit_value);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Close all open file descriptors. This is necessary to unblock
|
|
||||||
* the parent if it is trying to read from a pipe (connected to
|
|
||||||
* the child) before calling 'wait4()'.
|
|
||||||
*/
|
|
||||||
_file_descriptor_registry.flush();
|
|
||||||
|
|
||||||
_family_member.exit(exit_value);
|
|
||||||
|
|
||||||
/* notify the parent */
|
|
||||||
if (_parent_exit)
|
|
||||||
_parent_exit->exit_child();
|
|
||||||
else {
|
|
||||||
/* handle exit of the init process */
|
|
||||||
Signal_transmitter(_destruct_context_cap).submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Region_map *address_space(Pd_session &pd) override
|
|
||||||
{
|
|
||||||
return &static_cast<Pd_session_component &>(pd).address_space_region_map();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool forked() const override { return _forked; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__CHILD_POLICY_H_ */
|
|
|
@ -1,173 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief CPU session provided to Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-02-22
|
|
||||||
*
|
|
||||||
* The custom implementation of the CPU session interface is used to tweak
|
|
||||||
* the startup procedure as performed by the 'Process' class. Normally,
|
|
||||||
* processes start execution immediately at creation time at the ELF entry
|
|
||||||
* point. For implementing fork semantics, however, this default behavior
|
|
||||||
* does not work. Instead, we need to defer the start of the main thread
|
|
||||||
* until we have finished copying the address space of the forking process.
|
|
||||||
* Furthermore, we need to start the main thread at a custom trampoline
|
|
||||||
* function rather than at the ELF entry point. Those customizations are
|
|
||||||
* possible by wrapping core's CPU service.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__CPU_SESSION_COMPONENT_H_
|
|
||||||
#define _NOUX__CPU_SESSION_COMPONENT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/child.h>
|
|
||||||
#include <base/rpc_server.h>
|
|
||||||
#include <cpu_session/connection.h>
|
|
||||||
#include <cpu_thread/client.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <pd_session_component.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Cpu_session_component;
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Cpu_session_component : public Rpc_object<Cpu_session>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
bool const _forked;
|
|
||||||
Cpu_connection _cpu;
|
|
||||||
|
|
||||||
enum { MAX_THREADS = 8, MAIN_THREAD_IDX = 0 };
|
|
||||||
|
|
||||||
Thread_capability _threads[MAX_THREADS];
|
|
||||||
Dataspace_capability _trace_control { };
|
|
||||||
Dataspace_registry &_registry;
|
|
||||||
|
|
||||||
Constructible<Static_dataspace_info> _ds_info { };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param forked false if the CPU session belongs to a child
|
|
||||||
* created via execve or to the init process, or
|
|
||||||
* true if the CPU session belongs to a newly
|
|
||||||
* forked process.
|
|
||||||
*
|
|
||||||
* The 'forked' parameter controls the policy applied to the
|
|
||||||
* startup of the main thread.
|
|
||||||
*/
|
|
||||||
Cpu_session_component(Env &env,
|
|
||||||
Rpc_entrypoint &ep,
|
|
||||||
Child_policy::Name const &label,
|
|
||||||
bool forked,
|
|
||||||
Dataspace_registry ®istry)
|
|
||||||
:
|
|
||||||
_ep(ep), _forked(forked), _cpu(env, label.string()), _registry(registry)
|
|
||||||
{
|
|
||||||
_ep.manage(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Cpu_session_component()
|
|
||||||
{
|
|
||||||
_ep.dissolve(this);
|
|
||||||
|
|
||||||
if (!_trace_control.valid())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Explicitly start main thread, only meaningful when
|
|
||||||
* 'forked' is true
|
|
||||||
*/
|
|
||||||
void start_main_thread(addr_t ip, addr_t sp)
|
|
||||||
{
|
|
||||||
Capability<Cpu_thread> main_thread = _threads[MAIN_THREAD_IDX];
|
|
||||||
Cpu_thread_client(main_thread).start(ip, sp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cpu_session_capability cpu_cap() { return _cpu.cap(); }
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
** Cpu_session interface **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
Thread_capability create_thread(Capability<Pd_session> pd_cap,
|
|
||||||
Name const &name,
|
|
||||||
Affinity::Location affinity,
|
|
||||||
Weight weight,
|
|
||||||
addr_t utcb) override
|
|
||||||
{
|
|
||||||
/* create thread at core, keep local copy (needed on NOVA) */
|
|
||||||
for (unsigned i = 0; i < MAX_THREADS; i++) {
|
|
||||||
if (_threads[i].valid())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto lambda = [&] (Pd_session_component *pd)
|
|
||||||
{
|
|
||||||
if (!pd)
|
|
||||||
throw Thread_creation_failed();
|
|
||||||
|
|
||||||
return _cpu.create_thread(pd->core_pd_cap(), name,
|
|
||||||
affinity, weight, utcb);
|
|
||||||
};
|
|
||||||
|
|
||||||
Thread_capability cap = _ep.apply(pd_cap, lambda);
|
|
||||||
_threads[i] = cap;
|
|
||||||
return cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
error("maximum number of threads per session reached");
|
|
||||||
throw Thread_creation_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void kill_thread(Thread_capability thread) override
|
|
||||||
{
|
|
||||||
/* purge local copy of thread capability */
|
|
||||||
for (unsigned i = 0; i < MAX_THREADS; i++)
|
|
||||||
if (_threads[i].local_name() == thread.local_name())
|
|
||||||
_threads[i] = Thread_capability();
|
|
||||||
|
|
||||||
_cpu.kill_thread(thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
void exception_sigh(Signal_context_capability handler) override {
|
|
||||||
_cpu.exception_sigh(handler); }
|
|
||||||
|
|
||||||
Affinity::Space affinity_space() const override {
|
|
||||||
return _cpu.affinity_space(); }
|
|
||||||
|
|
||||||
Dataspace_capability trace_control() override
|
|
||||||
{
|
|
||||||
if (!_trace_control.valid()) {
|
|
||||||
_trace_control = _cpu.trace_control();
|
|
||||||
_ds_info.construct(_registry, _trace_control);
|
|
||||||
}
|
|
||||||
return _trace_control;
|
|
||||||
}
|
|
||||||
|
|
||||||
Quota quota() override { return _cpu.quota(); }
|
|
||||||
|
|
||||||
int ref_account(Cpu_session_capability c) override {
|
|
||||||
return _cpu.ref_account(c); }
|
|
||||||
|
|
||||||
int transfer_quota(Cpu_session_capability c, size_t q) override {
|
|
||||||
return _cpu.transfer_quota(c, q); }
|
|
||||||
|
|
||||||
Capability<Native_cpu> native_cpu() override {
|
|
||||||
return _cpu.native_cpu(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__CPU_SESSION_COMPONENT_H_ */
|
|
|
@ -1,207 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Registry for dataspaces used by noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-02-22
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__DATASPACE_REGISTRY_H_
|
|
||||||
#define _NOUX__DATASPACE_REGISTRY_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/rpc_server.h>
|
|
||||||
#include <dataspace/client.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Dataspace_user;
|
|
||||||
class Dataspace_info;
|
|
||||||
class Dataspace_registry;
|
|
||||||
|
|
||||||
struct Static_dataspace_info;
|
|
||||||
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Dataspace_user : Interface, private List<Dataspace_user>::Element
|
|
||||||
{
|
|
||||||
friend class Dataspace_info;
|
|
||||||
friend class List<Dataspace_user>;
|
|
||||||
|
|
||||||
virtual void dissolve(Dataspace_info &ds) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Dataspace_info : public Object_pool<Dataspace_info>::Entry
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
size_t _size;
|
|
||||||
Dataspace_capability _ds_cap;
|
|
||||||
Lock _users_lock { };
|
|
||||||
List<Dataspace_user> _users { };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Dataspace_info(Dataspace_capability ds_cap)
|
|
||||||
:
|
|
||||||
Object_pool<Dataspace_info>::Entry(ds_cap),
|
|
||||||
_size(ds_cap.valid() ? Dataspace_client(ds_cap).size() : 0),
|
|
||||||
_ds_cap(ds_cap)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
virtual ~Dataspace_info() { }
|
|
||||||
|
|
||||||
size_t size() const { return _size; }
|
|
||||||
Dataspace_capability ds_cap() const { return _ds_cap; }
|
|
||||||
|
|
||||||
void register_user(Dataspace_user &user)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_users_lock);
|
|
||||||
_users.insert(&user);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unregister_user(Dataspace_user &user)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_users_lock);
|
|
||||||
_users.remove(&user);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dissolve_users()
|
|
||||||
{
|
|
||||||
for (;;) {
|
|
||||||
Dataspace_user *user = 0;
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_users_lock);
|
|
||||||
user = _users.first();
|
|
||||||
if (!user)
|
|
||||||
break;
|
|
||||||
|
|
||||||
_users.remove(user);
|
|
||||||
}
|
|
||||||
user->dissolve(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create shadow copy of dataspace
|
|
||||||
*
|
|
||||||
* \param ram backing store used for copied dataspaces
|
|
||||||
* \param local_rm region map used for temporarily attaching
|
|
||||||
* dataspaces to the local address space
|
|
||||||
* \param alloc allocator used for creatng new 'Dataspace_info'
|
|
||||||
* objects
|
|
||||||
* \param ds_registry registry for keeping track of
|
|
||||||
* the new dataspace
|
|
||||||
* \param ep entrypoint used to serve the RPC
|
|
||||||
* interface of the new dataspace
|
|
||||||
* (used if the dataspace is a sub
|
|
||||||
* RM session)
|
|
||||||
* \return capability for the new dataspace
|
|
||||||
*/
|
|
||||||
virtual Dataspace_capability fork(Ram_allocator &ram,
|
|
||||||
Region_map &local_rm,
|
|
||||||
Allocator &alloc,
|
|
||||||
Dataspace_registry &ds_registry,
|
|
||||||
Rpc_entrypoint &ep) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write raw byte sequence into dataspace
|
|
||||||
*
|
|
||||||
* \param local_rm region map used for temporarily attaching
|
|
||||||
* the targeted dataspace to the local address
|
|
||||||
* space
|
|
||||||
* \param dst_offset destination offset within dataspace
|
|
||||||
* \param src data source buffer
|
|
||||||
* \param len length of source buffer in bytes
|
|
||||||
*/
|
|
||||||
virtual void poke(Region_map &local_rm, addr_t dst_offset,
|
|
||||||
char const *src, size_t len) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return leaf region map that covers a given address
|
|
||||||
*
|
|
||||||
* \param addr address that is covered by the requested region map
|
|
||||||
*/
|
|
||||||
virtual Capability<Region_map> lookup_region_map(addr_t)
|
|
||||||
{
|
|
||||||
/* by default a dataspace is no sub region map, so return invalid */
|
|
||||||
return Capability<Region_map>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Dataspace_registry : public Object_pool<Dataspace_info>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Allocator &_alloc;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Dataspace_registry(Allocator &alloc) : _alloc(alloc) { }
|
|
||||||
|
|
||||||
~Dataspace_registry()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* At the time the destructor is called, most 'Dataspace_info'
|
|
||||||
* objects are expected to be gone already because
|
|
||||||
* 'Child::_resources' and 'Child::_child' are destructed
|
|
||||||
* before the 'Child::_ds_registry'. However, RM dataspaces
|
|
||||||
* created via 'Rm_dataspace_info::fork', are not handled by
|
|
||||||
* those destructors. So we have to clean them up here.
|
|
||||||
*/
|
|
||||||
remove_all([&] (Dataspace_info *info) { destroy(_alloc, info); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Static_dataspace_info : Dataspace_info
|
|
||||||
{
|
|
||||||
Dataspace_registry &_ds_registry;
|
|
||||||
|
|
||||||
Static_dataspace_info(Dataspace_registry &ds_registry,
|
|
||||||
Dataspace_capability ds)
|
|
||||||
: Dataspace_info(ds), _ds_registry(ds_registry)
|
|
||||||
{
|
|
||||||
_ds_registry.insert(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Static_dataspace_info()
|
|
||||||
{
|
|
||||||
auto lambda = [this] (Static_dataspace_info *info) {
|
|
||||||
|
|
||||||
if (!info) {
|
|
||||||
error("lookup of binary ds info failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ds_registry.remove(info);
|
|
||||||
|
|
||||||
info->dissolve_users();
|
|
||||||
};
|
|
||||||
_ds_registry.apply(ds_cap(), lambda);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dataspace_capability fork(Ram_allocator &,
|
|
||||||
Region_map &,
|
|
||||||
Allocator &,
|
|
||||||
Dataspace_registry &,
|
|
||||||
Rpc_entrypoint &) override
|
|
||||||
{
|
|
||||||
return ds_cap();
|
|
||||||
}
|
|
||||||
|
|
||||||
void poke(Region_map &, addr_t, char const *, size_t) override
|
|
||||||
{
|
|
||||||
error("attempt to poke onto a static dataspace");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__DATASPACE_REGISTRY_H_ */
|
|
|
@ -1,99 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Queue for delayed object destruction
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2013-01-03
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__DESTRUCT_QUEUE_H_
|
|
||||||
#define _NOUX__DESTRUCT_QUEUE_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/allocator.h>
|
|
||||||
#include <util/list.h>
|
|
||||||
|
|
||||||
namespace Noux { class Destruct_queue; }
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Destruct_queue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct Element_base : Interface, private Genode::List<Element_base>::Element
|
|
||||||
{
|
|
||||||
friend class List<Noux::Destruct_queue::Element_base>;
|
|
||||||
virtual void destroy() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When a pointer to an object which inherits 'Element' among other
|
|
||||||
* base classes gets static-casted to a pointer to the 'Element'
|
|
||||||
* base object, the resulting address can differ from the start
|
|
||||||
* address of the inherited object. To be able to pass the start
|
|
||||||
* address of the inherited object to the allocator, a static-cast
|
|
||||||
* back to the inherited class needs to be performed. Therefore the
|
|
||||||
* type of the class inheriting from 'Element' needs to be given as
|
|
||||||
* template parameter.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
class Element : public Element_base
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Genode::Allocator &_alloc;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param alloc the allocator which was used to allocate
|
|
||||||
* the element
|
|
||||||
*/
|
|
||||||
Element(Genode::Allocator &alloc) : _alloc(alloc) { }
|
|
||||||
|
|
||||||
virtual ~Element() { };
|
|
||||||
|
|
||||||
void destroy() override
|
|
||||||
{
|
|
||||||
Genode::destroy(_alloc, static_cast<T*>(this));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Genode::List<Element_base> _destruct_list { };
|
|
||||||
Genode::Lock _destruct_list_lock { };
|
|
||||||
Signal_context_capability _sigh;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Destruct_queue(Signal_context_capability sigh) : _sigh(sigh) { }
|
|
||||||
|
|
||||||
void insert(Element_base *element)
|
|
||||||
{
|
|
||||||
Genode::Lock::Guard guard(_destruct_list_lock);
|
|
||||||
_destruct_list.insert(element);
|
|
||||||
|
|
||||||
Signal_transmitter(_sigh).submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
Genode::Lock::Guard guard(_destruct_list_lock);
|
|
||||||
|
|
||||||
Element_base *element;
|
|
||||||
while ((element = _destruct_list.first())) {
|
|
||||||
_destruct_list.remove(element);
|
|
||||||
element->destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__DESTRUCT_QUEUE_H_ */
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief ROM service provided to Noux processes for initial ROMs
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2017-01-31
|
|
||||||
*
|
|
||||||
* The initial ROMs (binary and linker) are already attached in a forked
|
|
||||||
* child and don't need a new ROM dataspace.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2017 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__EMPTY_ROM_SERVICE_H_
|
|
||||||
#define _NOUX__EMPTY_ROM_SERVICE_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/service.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <empty_rom_session_component.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
typedef Local_service<Empty_rom_session_component> Empty_rom_service;
|
|
||||||
class Empty_rom_factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Empty_rom_factory : public Empty_rom_service::Factory
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Allocator &_alloc;
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Empty_rom_factory(Allocator &alloc, Rpc_entrypoint &ep)
|
|
||||||
: _alloc(alloc), _ep(ep) { }
|
|
||||||
|
|
||||||
Empty_rom_session_component &create(Args const &, Affinity) override
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return *new (_alloc) Empty_rom_session_component(_ep); }
|
|
||||||
catch (Rom_connection::Rom_connection_failed) {
|
|
||||||
throw Service_denied(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
void upgrade(Empty_rom_session_component &, Args const &) override { }
|
|
||||||
|
|
||||||
void destroy(Empty_rom_session_component &session) override
|
|
||||||
{
|
|
||||||
Genode::destroy(_alloc, &session);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__EMPTY_ROM_SERVICE_H_ */
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief ROM session implementation used by Noux processes for initial ROMs
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2017-01-31
|
|
||||||
*
|
|
||||||
* The initial ROMs (binary and linker) are already attached in a forked
|
|
||||||
* child and don't need a new ROM dataspace. The invalid dataspace returned
|
|
||||||
* by this component is handled in 'Child::Process'.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2017 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__EMPTY_ROM_SESSION_COMPONENT_H_
|
|
||||||
#define _NOUX__EMPTY_ROM_SESSION_COMPONENT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/rpc_server.h>
|
|
||||||
|
|
||||||
namespace Noux { class Empty_rom_session_component; }
|
|
||||||
|
|
||||||
class Noux::Empty_rom_session_component : public Rpc_object<Rom_session>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Empty_rom_session_component(Rpc_entrypoint &ep)
|
|
||||||
: _ep(ep)
|
|
||||||
{
|
|
||||||
_ep.manage(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Empty_rom_session_component()
|
|
||||||
{
|
|
||||||
_ep.dissolve(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
** ROM session interface **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
Rom_dataspace_capability dataspace() override
|
|
||||||
{
|
|
||||||
return Rom_dataspace_capability();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sigh(Signal_context_capability) override { }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__EMPTY_ROM_SESSION_COMPONENT_H_ */
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Process environment utility
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__ENVIRONMENT_H_
|
|
||||||
#define _NOUX__ENVIRONMENT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/string.h>
|
|
||||||
#include <base/attached_ram_dataspace.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <path.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Environment;
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Environment : Noncopyable
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Attached_ram_dataspace _ds;
|
|
||||||
|
|
||||||
Sysio::Env &_env { *_ds.local_addr<Sysio::Env>() };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param env comma-separated list of environment variables
|
|
||||||
*/
|
|
||||||
Environment(Ram_allocator &ram, Region_map &local_rm, Sysio::Env const &env)
|
|
||||||
: _ds(ram, local_rm, sizeof(Sysio::Env))
|
|
||||||
{
|
|
||||||
memcpy(&_env, &env, sizeof(Sysio::Env));
|
|
||||||
}
|
|
||||||
|
|
||||||
Dataspace_capability cap() { return _ds.cap(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return list of environment variables as zero-separated list
|
|
||||||
*/
|
|
||||||
Sysio::Env const &env() { return _env; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__ENVIRONMENT_H_ */
|
|
|
@ -1,177 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Helper for handling the relationship between Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-02-25
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__FAMILY_MEMBER_H_
|
|
||||||
#define _NOUX__FAMILY_MEMBER_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/list.h>
|
|
||||||
#include <base/lock.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <parent_exit.h>
|
|
||||||
#include <parent_execve.h>
|
|
||||||
|
|
||||||
namespace Noux { class Family_member; }
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Family_member : private List<Family_member>::Element,
|
|
||||||
public Parent_exit,
|
|
||||||
public Parent_execve
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
friend class List<Noux::Family_member>;
|
|
||||||
|
|
||||||
int const _pid;
|
|
||||||
Lock _lock { };
|
|
||||||
List<Family_member> _list { };
|
|
||||||
bool _has_exited { false };
|
|
||||||
int _exit_status { 0 };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock used for implementing blocking syscalls,
|
|
||||||
* i.e., select, wait4, ...
|
|
||||||
*/
|
|
||||||
Lock _blocker { };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Family_member(int pid) : _pid(pid) { }
|
|
||||||
|
|
||||||
virtual ~Family_member() { }
|
|
||||||
|
|
||||||
int pid() const { return _pid; }
|
|
||||||
|
|
||||||
int exit_status() const { return _exit_status; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the parent at creation time of the process
|
|
||||||
*/
|
|
||||||
void insert(Family_member *member)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
_list.insert(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the parent from the return path of the wait4 syscall
|
|
||||||
*/
|
|
||||||
void remove(Family_member *member)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
_list.remove(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void submit_signal(Noux::Sysio::Signal sig) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the parent (originates from Kill_broadcaster)
|
|
||||||
*/
|
|
||||||
bool deliver_kill(int pid, Noux::Sysio::Signal sig)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
|
|
||||||
if (pid == _pid) {
|
|
||||||
submit_signal(sig);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
for (Family_member *child = _list.first(); child; child = child->next())
|
|
||||||
if (child->deliver_kill(pid, sig))
|
|
||||||
result = true;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parent_exit interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Called by the child on the parent (via Parent_exit) */
|
|
||||||
void exit_child() override
|
|
||||||
{
|
|
||||||
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) = 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) override
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
Family_member *new_child = child.do_execve(filename,
|
|
||||||
args,
|
|
||||||
env);
|
|
||||||
_list.insert(new_child);
|
|
||||||
_list.remove(&child);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the parent that we exited
|
|
||||||
*/
|
|
||||||
void exit(int exit_status)
|
|
||||||
{
|
|
||||||
_exit_status = exit_status;
|
|
||||||
_has_exited = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Family_member *poll4()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
|
|
||||||
/* check if any of our children has exited */
|
|
||||||
Family_member *curr = _list.first();
|
|
||||||
for (; curr; curr = curr->next()) {
|
|
||||||
if (curr->_has_exited)
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for the exit of any of our children
|
|
||||||
*/
|
|
||||||
Family_member *wait4()
|
|
||||||
{
|
|
||||||
/* reset the blocker lock to the 'locked' state */
|
|
||||||
_blocker.unlock();
|
|
||||||
_blocker.lock();
|
|
||||||
|
|
||||||
Family_member *result = poll4();
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
_blocker.lock();
|
|
||||||
|
|
||||||
/* either a child exited or a signal occurred */
|
|
||||||
return poll4();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__FAMILY_MEMBER_H_ */
|
|
|
@ -1,144 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Manager for file descriptors of one child
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__FILE_DESCRIPTOR_REGISTRY_H_
|
|
||||||
#define _NOUX__FILE_DESCRIPTOR_REGISTRY_H_
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <io_channel.h>
|
|
||||||
|
|
||||||
namespace Noux { class File_descriptor_registry; }
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::File_descriptor_registry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum { MAX_FILE_DESCRIPTORS = 64 };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool allocated;
|
|
||||||
bool close_on_execve;
|
|
||||||
Shared_pointer<Io_channel> io_channel;
|
|
||||||
} _fds[MAX_FILE_DESCRIPTORS] { };
|
|
||||||
|
|
||||||
bool _valid_fd(int fd) const
|
|
||||||
{
|
|
||||||
return (fd >= 0) && (fd < MAX_FILE_DESCRIPTORS);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _find_available_fd(int *fd) const
|
|
||||||
{
|
|
||||||
/* allocate the first free file descriptor */
|
|
||||||
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)
|
|
||||||
if (_fds[i].allocated == false) {
|
|
||||||
*fd = i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _assign_fd(int fd, Shared_pointer<Io_channel> &io_channel)
|
|
||||||
{
|
|
||||||
_fds[fd].io_channel = io_channel;
|
|
||||||
_fds[fd].allocated = true;
|
|
||||||
_fds[fd].close_on_execve = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _reset_fd(int fd)
|
|
||||||
{
|
|
||||||
_fds[fd].io_channel = Shared_pointer<Io_channel>();
|
|
||||||
_fds[fd].allocated = false;
|
|
||||||
_fds[fd].close_on_execve = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
File_descriptor_registry()
|
|
||||||
{
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~File_descriptor_registry() { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associate I/O channel with file descriptor
|
|
||||||
*
|
|
||||||
* \return noux file descriptor used for the I/O channel
|
|
||||||
*/
|
|
||||||
virtual int add_io_channel(Shared_pointer<Io_channel> io_channel, int fd = -1)
|
|
||||||
{
|
|
||||||
if ((fd == -1) && !_find_available_fd(&fd)) {
|
|
||||||
error("could not allocate file descriptor");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_valid_fd(fd)) {
|
|
||||||
error("file descriptor ", fd, " is out of range");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
_assign_fd(fd, io_channel);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void remove_io_channel(int fd)
|
|
||||||
{
|
|
||||||
if (!_valid_fd(fd))
|
|
||||||
error("file descriptor ", fd, " is out of range");
|
|
||||||
else
|
|
||||||
_reset_fd(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fd_in_use(int fd) const
|
|
||||||
{
|
|
||||||
return (_valid_fd(fd) && _fds[fd].io_channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared_pointer<Io_channel> io_channel_by_fd(int fd) const
|
|
||||||
{
|
|
||||||
if (!fd_in_use(fd))
|
|
||||||
return Shared_pointer<Io_channel>();
|
|
||||||
|
|
||||||
return _fds[fd].io_channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_fd_on_execve(int fd, bool close_on_execve)
|
|
||||||
{
|
|
||||||
if (!_valid_fd(fd))
|
|
||||||
error("file descriptor ", fd, " is out of range");
|
|
||||||
else
|
|
||||||
_fds[fd].close_on_execve = close_on_execve;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool close_fd_on_execve(int fd)
|
|
||||||
{
|
|
||||||
if (!_valid_fd(fd)) {
|
|
||||||
error("file descriptor ", fd, " is out of range");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _fds[fd].close_on_execve;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void flush()
|
|
||||||
{
|
|
||||||
/* close all file descriptors */
|
|
||||||
for (unsigned i = 0; i < MAX_FILE_DESCRIPTORS; i++)
|
|
||||||
_reset_fd(i);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__FILE_DESCRIPTOR_REGISTRY_H_ */
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Interrupt handler interface
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2013-10-08
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__INTERRUPT_HANDLER__H_
|
|
||||||
#define _NOUX__INTERRUPT_HANDLER__H_
|
|
||||||
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
struct Interrupt_handler : Genode::Interface
|
|
||||||
{
|
|
||||||
virtual void handle_interrupt(Sysio::Signal) = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__INTERRUPT_HANDLER__H_ */
|
|
||||||
|
|
|
@ -1,201 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief I/O channel
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*
|
|
||||||
* An 'Io_channel' is the interface for the operations on an open file
|
|
||||||
* descriptor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__IO_CHANNEL_H_
|
|
||||||
#define _NOUX__IO_CHANNEL_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/lock.h>
|
|
||||||
#include <vfs/file_system.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
#include <shared_pointer.h>
|
|
||||||
#include <wake_up_notifier.h>
|
|
||||||
#include <io_channel_listener.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
extern Genode::Lock &signal_lock();
|
|
||||||
|
|
||||||
class Io_channel_backend;
|
|
||||||
class Io_channel;
|
|
||||||
|
|
||||||
class Terminal_io_channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input/output channel backend that is used for calling
|
|
||||||
* different methods, which does not belong to the original
|
|
||||||
* interface, e.g. network methods.
|
|
||||||
*/
|
|
||||||
struct Noux::Io_channel_backend
|
|
||||||
{
|
|
||||||
virtual ~Io_channel_backend() { }
|
|
||||||
|
|
||||||
virtual int type() const { return -1; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input/output channel interface
|
|
||||||
*/
|
|
||||||
class Noux::Io_channel : private Reference_counter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
friend class Shared_pointer<Io_channel>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<Io_channel_listener> _interrupt_handlers { };
|
|
||||||
Lock _interrupt_handlers_lock { };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~Io_channel() { }
|
|
||||||
|
|
||||||
virtual Io_channel_backend *backend() { return nullptr; }
|
|
||||||
|
|
||||||
virtual bool write(Sysio &) { return false; }
|
|
||||||
virtual bool read(Sysio &) { return false; }
|
|
||||||
virtual bool fstat(Sysio &) { return false; }
|
|
||||||
virtual bool ftruncate(Sysio &) { return false; }
|
|
||||||
virtual bool fcntl(Sysio &) { return false; }
|
|
||||||
virtual bool dirent(Sysio &) { return false; }
|
|
||||||
virtual bool ioctl(Sysio &) { return false; }
|
|
||||||
virtual bool lseek(Sysio &) { return false; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if an unblocking condition of the channel is satisfied
|
|
||||||
*
|
|
||||||
* \param rd if true, check for data available for reading
|
|
||||||
* \param wr if true, check for readiness for writing
|
|
||||||
* \param ex if true, check for exceptions
|
|
||||||
*/
|
|
||||||
virtual bool check_unblock(bool /* rd */, bool /* wr */, bool /* ex */) const {
|
|
||||||
return false; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the channel is set to non-blocking mode
|
|
||||||
*/
|
|
||||||
virtual bool nonblocking() { return false; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register blocker for getting waked up on an I/O channel event
|
|
||||||
*
|
|
||||||
* This function is normally called by the to-be-blocked thread
|
|
||||||
* prior blocking itself, e.g., during a 'select' syscall.
|
|
||||||
*/
|
|
||||||
void register_wake_up_notifier(Wake_up_notifier *notifier)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_notifiers_lock);
|
|
||||||
|
|
||||||
_notifiers.insert(notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregister wake-up notifier
|
|
||||||
*
|
|
||||||
* This function is normally called after a blocker has left the
|
|
||||||
* blocking condition, e.g., during the return from the 'select'
|
|
||||||
* syscall'.
|
|
||||||
*/
|
|
||||||
void unregister_wake_up_notifier(Wake_up_notifier *notifier)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_notifiers_lock);
|
|
||||||
|
|
||||||
_notifiers.remove(notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell all registered notifiers about an occurred I/O event
|
|
||||||
*
|
|
||||||
* This function is called by I/O channel implementations that
|
|
||||||
* respond to external signals, e.g., the availability of new
|
|
||||||
* input from a terminal session.
|
|
||||||
*/
|
|
||||||
void invoke_all_notifiers()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_notifiers_lock);
|
|
||||||
|
|
||||||
for (Wake_up_notifier *n = _notifiers.first(); n; n = n->next())
|
|
||||||
n->wake_up();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register interrupt handler
|
|
||||||
*
|
|
||||||
* This function is called by Child objects to get woken up if the
|
|
||||||
* terminal sends, for example, Ctrl-C.
|
|
||||||
*/
|
|
||||||
void register_interrupt_handler(Io_channel_listener *handler)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_interrupt_handlers_lock);
|
|
||||||
|
|
||||||
_interrupt_handlers.insert(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregister interrupt 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(Sysio::Signal signal)
|
|
||||||
{
|
|
||||||
Lock::Guard signal_lock_guard(signal_lock());
|
|
||||||
Lock::Guard guard(_interrupt_handlers_lock);
|
|
||||||
|
|
||||||
for (Io_channel_listener *l = _interrupt_handlers.first();
|
|
||||||
l; l = l->next())
|
|
||||||
l->object()->handle_interrupt(signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the path of the file associated with the I/O channel
|
|
||||||
*
|
|
||||||
* This function is used to simplify the implemenation of SYSCALL_FSTAT
|
|
||||||
* and is only implemented by Vfs_io_channel.
|
|
||||||
*/
|
|
||||||
virtual bool path(char * /* path */, size_t /* len */) { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__IO_CHANNEL_H_ */
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief IO channel listener
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2014-01-23
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2014-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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_ */
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Io receptor registry
|
|
||||||
* \author Josef Soentgen
|
|
||||||
* \date 2012-10-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__IO_RECEPTOR_REGISTRY_H_
|
|
||||||
#define _NOUX__IO_RECEPTOR_REGISTRY_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/lock.h>
|
|
||||||
#include <util/list.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
struct Io_receptor;
|
|
||||||
struct Io_receptor_registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Io_receptor : List<Io_receptor>::Element
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Lock *_lock;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Io_receptor(Lock *lock) : _lock(lock) { }
|
|
||||||
|
|
||||||
void check_and_wakeup()
|
|
||||||
{
|
|
||||||
if (_lock)
|
|
||||||
_lock->unlock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Io_receptor_registry
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
List<Io_receptor> _receptors { };
|
|
||||||
Lock _receptors_lock { };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Io_receptor_registry() { }
|
|
||||||
|
|
||||||
~Io_receptor_registry()
|
|
||||||
{
|
|
||||||
Io_receptor *receptor;
|
|
||||||
while ((receptor = _receptors.first()) != 0)
|
|
||||||
_receptors.remove(receptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_receptor(Io_receptor *receptor)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_receptors_lock);
|
|
||||||
|
|
||||||
_receptors.insert(receptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unregister_receptor(Io_receptor *receptor)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_receptors_lock);
|
|
||||||
|
|
||||||
_receptors.remove(receptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Io_receptor *first() { return _receptors.first(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__IO_RECEPTOR_REGISTRY_H_ */
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Kill_broadcaster interface
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2014-01-13
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2014-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__KILL_BROADCASTER__H_
|
|
||||||
#define _NOUX__KILL_BROADCASTER__H_
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
struct Kill_broadcaster : Genode::Interface
|
|
||||||
{
|
|
||||||
virtual bool kill(int pid, Noux::Sysio::Signal sig) = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__KILL_BROADCASTER__H_ */
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief ROM service provided to Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2013-07-18
|
|
||||||
*
|
|
||||||
* The local ROM service has the sole purpose of tracking ROM dataspaces
|
|
||||||
* so that they are properly detached from RM sessions when the corresponding
|
|
||||||
* ROM sessions are closed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__LOCAL_ROM_SERVICE_H_
|
|
||||||
#define _NOUX__LOCAL_ROM_SERVICE_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/service.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <dataspace_registry.h>
|
|
||||||
#include <rom_session_component.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
typedef Local_service<Rom_session_component> Local_rom_service;
|
|
||||||
class Local_rom_factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Local_rom_factory : public Local_rom_service::Factory
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Allocator &_alloc;
|
|
||||||
Env &_env;
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
Vfs::File_system &_root_dir;
|
|
||||||
Vfs_io_waiter_registry &_vfs_io_waiter_registry;
|
|
||||||
Dataspace_registry &_registry;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Local_rom_factory(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
|
|
||||||
Vfs::File_system &root_dir,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Dataspace_registry ®istry)
|
|
||||||
:
|
|
||||||
_alloc(alloc), _env(env), _ep(ep), _root_dir(root_dir),
|
|
||||||
_vfs_io_waiter_registry(vfs_io_waiter_registry),
|
|
||||||
_registry(registry)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Rom_session_component &create(Args const &args, Affinity) override
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
Rom_session_component::Name const rom_name =
|
|
||||||
label_from_args(args.string()).last_element();
|
|
||||||
|
|
||||||
return *new (_alloc)
|
|
||||||
Rom_session_component(_alloc, _env, _ep, _root_dir,
|
|
||||||
_vfs_io_waiter_registry, _registry,
|
|
||||||
rom_name);
|
|
||||||
}
|
|
||||||
catch (Rom_connection::Rom_connection_failed) {
|
|
||||||
throw Service_denied(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
void upgrade(Rom_session_component &, Args const &) override { }
|
|
||||||
|
|
||||||
void destroy(Rom_session_component &session) override
|
|
||||||
{
|
|
||||||
Genode::destroy(_alloc, &session);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__LOCAL_ROM_SERVICE_H_ */
|
|
|
@ -1,340 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Unix emulation environment for Genode
|
|
||||||
* \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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
#include <base/component.h>
|
|
||||||
#include <libc/component.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <child.h>
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
#include <vfs_io_channel.h>
|
|
||||||
#include <terminal_io_channel.h>
|
|
||||||
#include <user_info.h>
|
|
||||||
#include <io_receptor_registry.h>
|
|
||||||
#include <destruct_queue.h>
|
|
||||||
#include <kill_broadcaster.h>
|
|
||||||
#include <vfs/dir_file_system.h>
|
|
||||||
#include <vfs/simple_env.h>
|
|
||||||
#include <time_info.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
static Noux::Child *init_child;
|
|
||||||
static int exit_value = ~0;
|
|
||||||
|
|
||||||
bool init_process(Child *child) { return child == init_child; }
|
|
||||||
void init_process_exited(int exit) { init_child = 0; exit_value = exit; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Noux::Io_receptor_registry * Noux::io_receptor_registry()
|
|
||||||
{
|
|
||||||
static Noux::Io_receptor_registry _inst;
|
|
||||||
return &_inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return string containing the environment variables of init
|
|
||||||
*
|
|
||||||
* The variable definitions are separated by zeros. The end of the string is
|
|
||||||
* marked with another zero.
|
|
||||||
*/
|
|
||||||
static Noux::Sysio::Env &env_string_of_init_process(Genode::Xml_node config)
|
|
||||||
{
|
|
||||||
static Noux::Sysio::Env env;
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
/* read environment variables for init process from config */
|
|
||||||
Genode::Xml_node start_node = config.sub_node("start");
|
|
||||||
try {
|
|
||||||
Genode::Xml_node node = start_node.sub_node("env");
|
|
||||||
for (; ; node = node.next("env")) {
|
|
||||||
|
|
||||||
typedef Genode::String<256> Var;
|
|
||||||
Var const var(node.attribute_value("name", Var()), "=",
|
|
||||||
node.attribute_value("value", Var()));
|
|
||||||
|
|
||||||
bool const env_exeeded = index + var.length() >= sizeof(env);
|
|
||||||
bool const var_exeeded = (var.length() == var.capacity());
|
|
||||||
|
|
||||||
if (env_exeeded || var_exeeded) {
|
|
||||||
warning("truncated environment variable: ", node);
|
|
||||||
env[index] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::strncpy(&env[index], var.string(), var.length());
|
|
||||||
index += var.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Genode::Xml_node::Nonexistent_sub_node) { }
|
|
||||||
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace Noux { class Stdio_unavailable : Genode::Exception { }; }
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* \throw Stdio_unavailable
|
|
||||||
*/
|
|
||||||
static Noux::Io_channel &
|
|
||||||
connect_stdio(Genode::Env &env,
|
|
||||||
Genode::Constructible<Terminal::Connection> &terminal,
|
|
||||||
Genode::Xml_node config,
|
|
||||||
Vfs::File_system &root,
|
|
||||||
Noux::Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Noux::Terminal_io_channel::Type type,
|
|
||||||
Genode::Allocator &alloc,
|
|
||||||
Noux::Time_info &time_info,
|
|
||||||
Timer::Connection &timer)
|
|
||||||
{
|
|
||||||
using namespace Vfs;
|
|
||||||
using namespace Noux;
|
|
||||||
typedef Terminal_io_channel Tio; /* just a local abbreviation */
|
|
||||||
|
|
||||||
typedef Genode::String<MAX_PATH_LEN> Path;
|
|
||||||
Vfs_handle *vfs_handle = nullptr;
|
|
||||||
char const *stdio_name = "";
|
|
||||||
unsigned mode = 0;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Tio::STDIN:
|
|
||||||
stdio_name = "stdin";
|
|
||||||
mode = Directory_service::OPEN_MODE_RDONLY;
|
|
||||||
break;
|
|
||||||
case Tio::STDOUT:
|
|
||||||
stdio_name = "stdout";
|
|
||||||
mode = Directory_service::OPEN_MODE_WRONLY;
|
|
||||||
break;
|
|
||||||
case Tio::STDERR:
|
|
||||||
stdio_name = "stderr";
|
|
||||||
mode = Directory_service::OPEN_MODE_WRONLY;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!config.has_attribute(stdio_name)) {
|
|
||||||
if (!terminal.constructed())
|
|
||||||
terminal.construct(env);
|
|
||||||
warning(stdio_name, " VFS path not defined, connecting to terminal session");
|
|
||||||
return *new (alloc) Tio(*terminal, type, env.ep());
|
|
||||||
}
|
|
||||||
|
|
||||||
Path const path = config.attribute_value(stdio_name, Path());
|
|
||||||
|
|
||||||
if (root.open(path.string(), mode, &vfs_handle, alloc)
|
|
||||||
!= Directory_service::OPEN_OK)
|
|
||||||
{
|
|
||||||
error("failed to connect ", stdio_name, " to '", path, "'");
|
|
||||||
throw Stdio_unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
return *new (alloc)
|
|
||||||
Vfs_io_channel(path.string(), root.leaf_path(path.string()),
|
|
||||||
vfs_handle, vfs_io_waiter_registry, env.ep(),
|
|
||||||
time_info, timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace Noux { struct Main; }
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Main
|
|
||||||
{
|
|
||||||
Env &_env;
|
|
||||||
|
|
||||||
Heap _heap { _env.ram(), _env.rm() };
|
|
||||||
|
|
||||||
/* whitelist of service requests to be routed to the parent */
|
|
||||||
Noux::Parent_services _parent_services { };
|
|
||||||
|
|
||||||
Noux::Parent_service _log_parent_service { _parent_services, _env, "LOG" };
|
|
||||||
Noux::Parent_service _timer_parent_service { _parent_services, _env, "Timer" };
|
|
||||||
|
|
||||||
Attached_rom_dataspace _config { _env, "config" };
|
|
||||||
|
|
||||||
Verbose _verbose { _config.xml() };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return name of init process as specified in the config
|
|
||||||
*/
|
|
||||||
Child_policy::Name _name_of_init_process() const
|
|
||||||
{
|
|
||||||
return _config.xml().sub_node("start").attribute_value("name", Child_policy::Name());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read command-line arguments of init process from config
|
|
||||||
*/
|
|
||||||
Args const &_args_of_init_process()
|
|
||||||
{
|
|
||||||
static char args_buf[4096];
|
|
||||||
static Args args(args_buf, sizeof(args_buf));
|
|
||||||
|
|
||||||
Xml_node start_node = _config.xml().sub_node("start");
|
|
||||||
try {
|
|
||||||
/* the first argument is the program name */
|
|
||||||
args.append(_name_of_init_process().string());
|
|
||||||
|
|
||||||
start_node.for_each_sub_node("arg", [&] (Xml_node arg_node) {
|
|
||||||
typedef String<512> Value;
|
|
||||||
args.append(arg_node.attribute_value("value", Value()).string());
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (Args::Overrun) { error("argument buffer overrun"); }
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize virtual file system */
|
|
||||||
Vfs::Global_file_system_factory _global_file_system_factory { _heap };
|
|
||||||
|
|
||||||
struct Io_progress_handler : Genode::Entrypoint::Io_progress_handler
|
|
||||||
{
|
|
||||||
Vfs_io_waiter_registry io_waiter_registry { };
|
|
||||||
|
|
||||||
Io_progress_handler(Genode::Entrypoint &ep)
|
|
||||||
{
|
|
||||||
ep.register_io_progress_handler(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_io_progress() override
|
|
||||||
{
|
|
||||||
io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
|
|
||||||
r.wakeup();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} _io_response_handler { _env.ep() };
|
|
||||||
|
|
||||||
Vfs::Simple_env _vfs_env { _env, _heap, _config.xml().sub_node("fstab") };
|
|
||||||
|
|
||||||
Vfs::File_system &_root_dir = _vfs_env.root_dir();
|
|
||||||
|
|
||||||
Pid_allocator _pid_allocator { };
|
|
||||||
|
|
||||||
Timer::Connection _timer_connection { _env };
|
|
||||||
|
|
||||||
User_info _user_info { _config.xml() };
|
|
||||||
|
|
||||||
Time_info _time_info { _env, _config.xml() };
|
|
||||||
|
|
||||||
Signal_handler<Main> _destruct_handler {
|
|
||||||
_env.ep(), *this, &Main::_handle_destruct };
|
|
||||||
|
|
||||||
Destruct_queue _destruct_queue { _destruct_handler };
|
|
||||||
|
|
||||||
void _handle_destruct()
|
|
||||||
{
|
|
||||||
_destruct_queue.flush();
|
|
||||||
|
|
||||||
/* let noux exit if the init process exited */
|
|
||||||
if (!init_child) {
|
|
||||||
_channel_0 = Shared_pointer<Io_channel>();
|
|
||||||
_channel_1 = Shared_pointer<Io_channel>();
|
|
||||||
_channel_2 = Shared_pointer<Io_channel>();
|
|
||||||
_env.parent().exit(exit_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Kill_broadcaster_impl: Kill_broadcaster
|
|
||||||
{
|
|
||||||
Family_member *init_process = nullptr;
|
|
||||||
|
|
||||||
bool kill(int pid, Noux::Sysio::Signal sig) override
|
|
||||||
{
|
|
||||||
return init_process->deliver_kill(pid, sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
} _kill_broadcaster { };
|
|
||||||
|
|
||||||
Noux::Child _init_child { _name_of_init_process(),
|
|
||||||
_verbose,
|
|
||||||
_user_info,
|
|
||||||
_time_info,
|
|
||||||
0,
|
|
||||||
_kill_broadcaster,
|
|
||||||
_timer_connection,
|
|
||||||
_init_child,
|
|
||||||
_pid_allocator,
|
|
||||||
_pid_allocator.alloc(),
|
|
||||||
_env,
|
|
||||||
_root_dir,
|
|
||||||
_io_response_handler.io_waiter_registry,
|
|
||||||
_args_of_init_process(),
|
|
||||||
env_string_of_init_process(_config.xml()),
|
|
||||||
_heap,
|
|
||||||
_env.pd(),
|
|
||||||
_env.pd_session_cap(),
|
|
||||||
_parent_services,
|
|
||||||
false,
|
|
||||||
_destruct_queue };
|
|
||||||
|
|
||||||
Constructible<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 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
|
|
||||||
_io_response_handler.io_waiter_registry,
|
|
||||||
Tio::STDIN, _heap, _time_info, _timer_connection), _heap },
|
|
||||||
_channel_1 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
|
|
||||||
_io_response_handler.io_waiter_registry,
|
|
||||||
Tio::STDOUT, _heap, _time_info, _timer_connection), _heap },
|
|
||||||
_channel_2 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir,
|
|
||||||
_io_response_handler.io_waiter_registry,
|
|
||||||
Tio::STDERR, _heap, _time_info, _timer_connection), _heap };
|
|
||||||
|
|
||||||
Main(Env &env) : _env(env)
|
|
||||||
{
|
|
||||||
log("--- noux started ---");
|
|
||||||
|
|
||||||
_init_child.add_io_channel(_channel_0, 0);
|
|
||||||
_init_child.add_io_channel(_channel_1, 1);
|
|
||||||
_init_child.add_io_channel(_channel_2, 2);
|
|
||||||
|
|
||||||
_kill_broadcaster.init_process = &_init_child;
|
|
||||||
|
|
||||||
init_child = &_init_child;
|
|
||||||
|
|
||||||
_init_child.start();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void Component::construct(Genode::Env &env)
|
|
||||||
{
|
|
||||||
static Noux::Main main(env);
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Parent_execve interface
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2014-01-13
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2014-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Parent_execve : Genode::Interface
|
|
||||||
{
|
|
||||||
virtual void execve_child(Family_member &child,
|
|
||||||
const char *filename,
|
|
||||||
Args const &args,
|
|
||||||
Sysio::Env const &env) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__PARENT_EXECVE__H_ */
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Parent_exit interface
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2014-01-16
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2014-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__PARENT_EXIT__H_
|
|
||||||
#define _NOUX__PARENT_EXIT__H_
|
|
||||||
|
|
||||||
#include <util/interface.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
struct Family_member;
|
|
||||||
|
|
||||||
struct Parent_exit : Genode::Interface
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Handle the exiting of a child
|
|
||||||
*/
|
|
||||||
|
|
||||||
virtual void exit_child() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__PARENT_EXIT__H_ */
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Path handling utility for Noux
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__PATH_H_
|
|
||||||
#define _NOUX__PATH_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <vfs/types.h>
|
|
||||||
|
|
||||||
namespace Noux { using Vfs::Absolute_path; }
|
|
||||||
|
|
||||||
#endif /* _NOUX__PATH_H_ */
|
|
|
@ -1,327 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief PD service used by Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2016-04-20
|
|
||||||
*
|
|
||||||
* The custom implementation of the PD session interface provides a pool of
|
|
||||||
* RAM shared by Noux and all Noux processes. The use of a shared pool
|
|
||||||
* alleviates the need to assign RAM quota to individual Noux processes.
|
|
||||||
*
|
|
||||||
* Furthermore, the custom implementation is needed to get hold of the RAM
|
|
||||||
* dataspaces allocated by each Noux process. When forking a process, the
|
|
||||||
* acquired information (in the form of 'Ram_dataspace_info' objects) is used
|
|
||||||
* to create a shadow copy of the forking address space.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2016-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__PD_SESSION_COMPONENT_H_
|
|
||||||
#define _NOUX__PD_SESSION_COMPONENT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <pd_session/connection.h>
|
|
||||||
#include <base/rpc_server.h>
|
|
||||||
#include <base/env.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <region_map_component.h>
|
|
||||||
#include <dataspace_registry.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
struct Ram_dataspace_info;
|
|
||||||
struct Pd_session_component;
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Ram_dataspace_info : Dataspace_info,
|
|
||||||
private List<Ram_dataspace_info>::Element
|
|
||||||
{
|
|
||||||
friend class List<Ram_dataspace_info>;
|
|
||||||
|
|
||||||
Ram_dataspace_info(Ram_dataspace_capability ds_cap)
|
|
||||||
: Dataspace_info(ds_cap) { }
|
|
||||||
|
|
||||||
Dataspace_capability fork(Ram_allocator &ram,
|
|
||||||
Region_map &local_rm,
|
|
||||||
Allocator &alloc,
|
|
||||||
Dataspace_registry &ds_registry,
|
|
||||||
Rpc_entrypoint &) override
|
|
||||||
{
|
|
||||||
size_t const size = Dataspace_client(ds_cap()).size();
|
|
||||||
Ram_dataspace_capability dst_ds_cap;
|
|
||||||
|
|
||||||
try {
|
|
||||||
dst_ds_cap = ram.alloc(size);
|
|
||||||
|
|
||||||
Attached_dataspace src_ds(local_rm, ds_cap());
|
|
||||||
Attached_dataspace dst_ds(local_rm, dst_ds_cap);
|
|
||||||
memcpy(dst_ds.local_addr<char>(), src_ds.local_addr<char>(), size);
|
|
||||||
|
|
||||||
ds_registry.insert(new (alloc) Ram_dataspace_info(dst_ds_cap));
|
|
||||||
return dst_ds_cap;
|
|
||||||
|
|
||||||
} catch (...) {
|
|
||||||
error("fork of RAM dataspace failed");
|
|
||||||
|
|
||||||
if (dst_ds_cap.valid())
|
|
||||||
ram.free(dst_ds_cap);
|
|
||||||
|
|
||||||
return Dataspace_capability();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void poke(Region_map &rm, addr_t dst_offset, char const *src, size_t len) override
|
|
||||||
{
|
|
||||||
if (!src) return;
|
|
||||||
|
|
||||||
if ((dst_offset >= size()) || (dst_offset + len > size())) {
|
|
||||||
error("illegal attemt to write beyond dataspace boundary");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Attached_dataspace ds(rm, ds_cap());
|
|
||||||
memcpy(ds.local_addr<char>() + dst_offset, src, len);
|
|
||||||
} catch (...) { warning("poke: failed to attach RAM dataspace"); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Pd_session_component : public Rpc_object<Pd_session>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
|
|
||||||
Pd_connection _pd;
|
|
||||||
|
|
||||||
Pd_session &_ref_pd;
|
|
||||||
|
|
||||||
Region_map_component _address_space;
|
|
||||||
Region_map_component _stack_area;
|
|
||||||
Region_map_component _linker_area;
|
|
||||||
|
|
||||||
Allocator &_alloc;
|
|
||||||
|
|
||||||
Ram_allocator &_ram;
|
|
||||||
|
|
||||||
Ram_quota _used_ram_quota { 0 };
|
|
||||||
|
|
||||||
List<Ram_dataspace_info> _ds_list { };
|
|
||||||
|
|
||||||
Dataspace_registry &_ds_registry;
|
|
||||||
|
|
||||||
template <typename FUNC>
|
|
||||||
auto _with_automatic_cap_upgrade(FUNC func) -> decltype(func())
|
|
||||||
{
|
|
||||||
Cap_quota upgrade { 10 };
|
|
||||||
enum { NUM_ATTEMPTS = 3 };
|
|
||||||
return retry<Out_of_caps>(
|
|
||||||
[&] () { return func(); },
|
|
||||||
[&] () { _ref_pd.transfer_quota(_pd.rpc_cap(), upgrade); },
|
|
||||||
NUM_ATTEMPTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
Pd_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
|
|
||||||
Child_policy::Name const &name,
|
|
||||||
Dataspace_registry &ds_registry)
|
|
||||||
:
|
|
||||||
_ep(ep), _pd(env, name.string()), _ref_pd(env.pd()),
|
|
||||||
_address_space(alloc, _ep, ds_registry, _pd, _pd.address_space()),
|
|
||||||
_stack_area (alloc, _ep, ds_registry, _pd, _pd.stack_area()),
|
|
||||||
_linker_area (alloc, _ep, ds_registry, _pd, _pd.linker_area()),
|
|
||||||
_alloc(alloc), _ram(env.ram()), _ds_registry(ds_registry)
|
|
||||||
{
|
|
||||||
_ep.manage(this);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Equip the PD with an initial cap quota that suffices in the
|
|
||||||
* common case. Further capabilities are provisioned on demand
|
|
||||||
* via '_with_automatic_cap_upgrade'.
|
|
||||||
*/
|
|
||||||
_pd.ref_account(env.pd_session_cap());
|
|
||||||
_ref_pd.transfer_quota(_pd.rpc_cap(), Cap_quota{10});
|
|
||||||
}
|
|
||||||
|
|
||||||
~Pd_session_component()
|
|
||||||
{
|
|
||||||
_ep.dissolve(this);
|
|
||||||
|
|
||||||
Ram_dataspace_info *info = 0;
|
|
||||||
while ((info = _ds_list.first()))
|
|
||||||
free(static_cap_cast<Ram_dataspace>(info->ds_cap()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Pd_session_capability core_pd_cap() { return _pd.cap(); }
|
|
||||||
|
|
||||||
void poke(Region_map &rm, addr_t dst_addr, char const *src, size_t len)
|
|
||||||
{
|
|
||||||
_address_space.poke(rm, dst_addr, src, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
Capability<Region_map> lookup_region_map(addr_t const addr)
|
|
||||||
{
|
|
||||||
return _address_space.lookup_region_map(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Region_map &address_space_region_map() { return _address_space; }
|
|
||||||
Region_map &linker_area_region_map() { return _linker_area; }
|
|
||||||
Region_map &stack_area_region_map() { return _stack_area; }
|
|
||||||
|
|
||||||
void replay(Pd_session_component &dst_pd,
|
|
||||||
Region_map &local_rm,
|
|
||||||
Allocator &alloc,
|
|
||||||
Dataspace_registry &ds_registry,
|
|
||||||
Rpc_entrypoint &ep)
|
|
||||||
{
|
|
||||||
/* replay region map into new protection domain */
|
|
||||||
_stack_area .replay(dst_pd, dst_pd.stack_area_region_map(), local_rm, alloc, ds_registry, ep);
|
|
||||||
_linker_area .replay(dst_pd, dst_pd.linker_area_region_map(), local_rm, alloc, ds_registry, ep);
|
|
||||||
_address_space.replay(dst_pd, dst_pd.address_space_region_map(), local_rm, alloc, ds_registry, ep);
|
|
||||||
|
|
||||||
Region_map &dst_address_space = dst_pd.address_space_region_map();
|
|
||||||
Region_map &dst_stack_area = dst_pd.stack_area_region_map();
|
|
||||||
Region_map &dst_linker_area = dst_pd.linker_area_region_map();
|
|
||||||
|
|
||||||
/* attach stack area */
|
|
||||||
dst_address_space.attach(dst_stack_area.dataspace(),
|
|
||||||
Dataspace_client(dst_stack_area.dataspace()).size(),
|
|
||||||
0, true,
|
|
||||||
_address_space.lookup_region_base(_stack_area.dataspace()));
|
|
||||||
|
|
||||||
/* attach linker area */
|
|
||||||
dst_address_space.attach(dst_linker_area.dataspace(),
|
|
||||||
Dataspace_client(dst_linker_area.dataspace()).size(),
|
|
||||||
0, true,
|
|
||||||
_address_space.lookup_region_base(_linker_area.dataspace()), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************
|
|
||||||
** Pd_session interface **
|
|
||||||
**************************/
|
|
||||||
|
|
||||||
void assign_parent(Capability<Parent> parent) override {
|
|
||||||
_pd.assign_parent(parent); }
|
|
||||||
|
|
||||||
bool assign_pci(addr_t addr, uint16_t bdf) override {
|
|
||||||
return _pd.assign_pci(addr, bdf); }
|
|
||||||
|
|
||||||
void map(addr_t virt, addr_t size) override {
|
|
||||||
return _pd.map(virt, size); }
|
|
||||||
|
|
||||||
Signal_source_capability alloc_signal_source() override
|
|
||||||
{
|
|
||||||
return _with_automatic_cap_upgrade([&] () {
|
|
||||||
return _pd.alloc_signal_source(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_signal_source(Signal_source_capability cap) override {
|
|
||||||
_pd.free_signal_source(cap); }
|
|
||||||
|
|
||||||
Capability<Signal_context> alloc_context(Signal_source_capability source,
|
|
||||||
unsigned long imprint) override
|
|
||||||
{
|
|
||||||
return _with_automatic_cap_upgrade([&] () {
|
|
||||||
return _pd.alloc_context(source, imprint); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_context(Capability<Signal_context> cap) override {
|
|
||||||
_pd.free_context(cap); }
|
|
||||||
|
|
||||||
void submit(Capability<Signal_context> context, unsigned cnt) override {
|
|
||||||
_pd.submit(context, cnt); }
|
|
||||||
|
|
||||||
Native_capability alloc_rpc_cap(Native_capability ep) override
|
|
||||||
{
|
|
||||||
return _with_automatic_cap_upgrade([&] () {
|
|
||||||
return _pd.alloc_rpc_cap(ep); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_rpc_cap(Native_capability cap) override {
|
|
||||||
_pd.free_rpc_cap(cap); }
|
|
||||||
|
|
||||||
Capability<Region_map> address_space() override {
|
|
||||||
return _address_space.Rpc_object<Region_map>::cap(); }
|
|
||||||
|
|
||||||
Capability<Region_map> stack_area() override {
|
|
||||||
return _stack_area.Rpc_object<Region_map>::cap(); }
|
|
||||||
|
|
||||||
Capability<Region_map> linker_area() override {
|
|
||||||
return _linker_area.Rpc_object<Region_map>::cap(); }
|
|
||||||
|
|
||||||
void ref_account(Capability<Pd_session>) override { }
|
|
||||||
|
|
||||||
void transfer_quota(Capability<Pd_session>, Cap_quota) override { }
|
|
||||||
|
|
||||||
Cap_quota cap_quota() const override { return _pd.cap_quota(); }
|
|
||||||
Cap_quota used_caps() const override { return _pd.used_caps(); }
|
|
||||||
|
|
||||||
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached) override
|
|
||||||
{
|
|
||||||
Ram_dataspace_capability ds_cap = _ram.alloc(size, cached);
|
|
||||||
|
|
||||||
Ram_dataspace_info *ds_info = new (_alloc) Ram_dataspace_info(ds_cap);
|
|
||||||
|
|
||||||
_ds_registry.insert(ds_info);
|
|
||||||
_ds_list.insert(ds_info);
|
|
||||||
|
|
||||||
_used_ram_quota = Ram_quota { _used_ram_quota.value + size };
|
|
||||||
|
|
||||||
return ds_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(Ram_dataspace_capability ds_cap) override
|
|
||||||
{
|
|
||||||
Ram_dataspace_info *ds_info;
|
|
||||||
|
|
||||||
auto lambda = [&] (Ram_dataspace_info *rdi) {
|
|
||||||
ds_info = rdi;
|
|
||||||
|
|
||||||
if (!ds_info) {
|
|
||||||
error("RAM free: dataspace lookup failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t const ds_size = rdi->size();
|
|
||||||
|
|
||||||
_ds_registry.remove(ds_info);
|
|
||||||
ds_info->dissolve_users();
|
|
||||||
_ds_list.remove(ds_info);
|
|
||||||
_ram.free(ds_cap);
|
|
||||||
|
|
||||||
_used_ram_quota = Ram_quota { _used_ram_quota.value - ds_size };
|
|
||||||
};
|
|
||||||
_ds_registry.apply(ds_cap, lambda);
|
|
||||||
destroy(_alloc, ds_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dataspace_size(Ram_dataspace_capability ds_cap) const override
|
|
||||||
{
|
|
||||||
size_t result = 0;
|
|
||||||
_ds_registry.apply(ds_cap, [&] (Ram_dataspace_info *rdi) {
|
|
||||||
if (rdi)
|
|
||||||
result = rdi->size(); });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void transfer_quota(Pd_session_capability, Ram_quota) override { }
|
|
||||||
Ram_quota ram_quota() const override { return _pd.ram_quota(); }
|
|
||||||
Ram_quota used_ram() const override { return Ram_quota{_used_ram_quota}; }
|
|
||||||
|
|
||||||
Capability<Native_pd> native_pd() override {
|
|
||||||
return _pd.native_pd(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__PD_SESSION_COMPONENT_H_ */
|
|
|
@ -1,353 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief I/O channels for pipe input/output
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-03-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__PIPE_IO_CHANNEL_H_
|
|
||||||
#define _NOUX__PIPE_IO_CHANNEL_H_
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <io_channel.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Pipe;
|
|
||||||
class Pipe_sink_io_channel;
|
|
||||||
class Pipe_source_io_channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Pipe : public Reference_counter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Lock mutable _lock { };
|
|
||||||
|
|
||||||
enum { BUFFER_SIZE = 4096 };
|
|
||||||
char _buffer[BUFFER_SIZE];
|
|
||||||
|
|
||||||
unsigned _read_offset { 0 };
|
|
||||||
unsigned _write_offset { 0 };
|
|
||||||
|
|
||||||
Signal_context_capability _read_ready_sigh { };
|
|
||||||
Signal_context_capability _write_ready_sigh { };
|
|
||||||
|
|
||||||
bool _writer_is_gone { false };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return space available in the buffer for writing, in bytes
|
|
||||||
*/
|
|
||||||
size_t _avail_buffer_space() const
|
|
||||||
{
|
|
||||||
if (_read_offset < _write_offset)
|
|
||||||
return (BUFFER_SIZE - _write_offset) + _read_offset - 1;
|
|
||||||
|
|
||||||
if (_read_offset > _write_offset)
|
|
||||||
return _read_offset - _write_offset - 1;
|
|
||||||
|
|
||||||
/* _read_offset == _write_offset */
|
|
||||||
return BUFFER_SIZE - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _any_space_avail_for_writing() const
|
|
||||||
{
|
|
||||||
return _avail_buffer_space() > 0;;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _wake_up_reader()
|
|
||||||
{
|
|
||||||
if (_read_ready_sigh.valid())
|
|
||||||
Signal_transmitter(_read_ready_sigh).submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _wake_up_writer()
|
|
||||||
{
|
|
||||||
if (_write_ready_sigh.valid())
|
|
||||||
Signal_transmitter(_write_ready_sigh).submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
~Pipe()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writer_close()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
|
|
||||||
_writer_is_gone = true;
|
|
||||||
_write_ready_sigh = Signal_context_capability();
|
|
||||||
_wake_up_reader();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reader_close()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
_read_ready_sigh = Signal_context_capability();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool writer_is_gone() const
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
return _writer_is_gone;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool any_space_avail_for_writing() const
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
return _any_space_avail_for_writing();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool data_avail_for_reading() const
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
|
|
||||||
return _read_offset != _write_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t read(char *dst, size_t dst_len)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
|
|
||||||
if (_read_offset < _write_offset) {
|
|
||||||
|
|
||||||
size_t len = min(dst_len, _write_offset - _read_offset);
|
|
||||||
memcpy(dst, &_buffer[_read_offset], len);
|
|
||||||
|
|
||||||
_read_offset += len;
|
|
||||||
_wake_up_writer();
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_read_offset > _write_offset) {
|
|
||||||
|
|
||||||
size_t const upper_len = min(dst_len, BUFFER_SIZE - _read_offset);
|
|
||||||
memcpy(dst, &_buffer[_read_offset], upper_len);
|
|
||||||
|
|
||||||
size_t const lower_len = min(dst_len - upper_len, _write_offset);
|
|
||||||
if (lower_len) {
|
|
||||||
memcpy(dst + upper_len, &_buffer[0], lower_len);
|
|
||||||
_read_offset = lower_len;
|
|
||||||
} else {
|
|
||||||
_read_offset += upper_len;
|
|
||||||
}
|
|
||||||
_wake_up_writer();
|
|
||||||
|
|
||||||
return upper_len + lower_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* _read_offset == _write_offset */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write to pipe buffer
|
|
||||||
*
|
|
||||||
* \return number of written bytes (may be less than 'len')
|
|
||||||
*/
|
|
||||||
size_t write(char *src, size_t len)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
|
|
||||||
/* trim write request to the available buffer space */
|
|
||||||
size_t const trimmed_len = min(len, _avail_buffer_space());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remember pipe state prior writing to see whether a reader
|
|
||||||
* must be unblocked after writing.
|
|
||||||
*/
|
|
||||||
bool const pipe_was_empty = (_read_offset == _write_offset);
|
|
||||||
|
|
||||||
/* write data up to the upper boundary of the pipe buffer */
|
|
||||||
size_t const upper_len = min(BUFFER_SIZE - _write_offset, trimmed_len);
|
|
||||||
memcpy(&_buffer[_write_offset], src, upper_len);
|
|
||||||
|
|
||||||
_write_offset += upper_len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine number of remaining bytes beyond the buffer boundary.
|
|
||||||
* The buffer wraps. So this data will end up in the lower part
|
|
||||||
* of the pipe buffer.
|
|
||||||
*/
|
|
||||||
size_t const lower_len = trimmed_len - upper_len;
|
|
||||||
|
|
||||||
if (lower_len > 0) {
|
|
||||||
|
|
||||||
/* pipe buffer wrap-around, write remaining data to the lower part */
|
|
||||||
memcpy(&_buffer[0], src + upper_len, lower_len);
|
|
||||||
_write_offset = lower_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wake up reader who may block for incoming data.
|
|
||||||
*/
|
|
||||||
if (pipe_was_empty || !_any_space_avail_for_writing())
|
|
||||||
_wake_up_reader();
|
|
||||||
|
|
||||||
/* return number of written bytes */
|
|
||||||
return trimmed_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_write_ready_sigh(Signal_context_capability sigh)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
_write_ready_sigh = sigh;
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_read_ready_sigh(Signal_context_capability sigh)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
_read_ready_sigh = sigh;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Pipe_sink_io_channel : public Io_channel
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Signal_handler<Pipe_sink_io_channel> _write_ready_handler;
|
|
||||||
|
|
||||||
void _handle_write_ready() { Io_channel::invoke_all_notifiers(); }
|
|
||||||
|
|
||||||
Shared_pointer<Pipe> _pipe;
|
|
||||||
|
|
||||||
bool _nonblocking { false };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Pipe_sink_io_channel(Shared_pointer<Pipe> pipe, Entrypoint &ep)
|
|
||||||
:
|
|
||||||
_write_ready_handler(ep, *this, &Pipe_sink_io_channel::_handle_write_ready),
|
|
||||||
_pipe(pipe)
|
|
||||||
{
|
|
||||||
pipe->register_write_ready_sigh(_write_ready_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Pipe_sink_io_channel() { _pipe->writer_close(); }
|
|
||||||
|
|
||||||
bool check_unblock(bool, bool wr, bool) const override
|
|
||||||
{
|
|
||||||
return wr && _pipe->any_space_avail_for_writing();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nonblocking() override { return _nonblocking; }
|
|
||||||
|
|
||||||
bool write(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
sysio.write_out.count = _pipe->write(sysio.write_in.chunk,
|
|
||||||
sysio.write_in.count);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fcntl(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
switch (sysio.fcntl_in.cmd) {
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
|
||||||
sysio.fcntl_out.result = Sysio::OPEN_MODE_WRONLY;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS:
|
|
||||||
_nonblocking = (sysio.fcntl_in.long_arg &
|
|
||||||
Sysio::FCNTL_FILE_STATUS_FLAG_NONBLOCK);
|
|
||||||
sysio.fcntl_out.result = 0;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fstat(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
sysio.fstat_out.st.type = Vfs::Node_type::CONTINUOUS_FILE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Pipe_source_io_channel : public Io_channel
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Signal_handler<Pipe_source_io_channel> _read_avail_handler;
|
|
||||||
|
|
||||||
void _handle_read_avail() { Io_channel::invoke_all_notifiers(); }
|
|
||||||
|
|
||||||
Shared_pointer<Pipe> _pipe;
|
|
||||||
|
|
||||||
bool _nonblocking { false };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Pipe_source_io_channel(Shared_pointer<Pipe> pipe, Entrypoint &ep)
|
|
||||||
:
|
|
||||||
_read_avail_handler(ep, *this, &Pipe_source_io_channel::_handle_read_avail),
|
|
||||||
_pipe(pipe)
|
|
||||||
{
|
|
||||||
_pipe->register_read_ready_sigh(_read_avail_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Pipe_source_io_channel() { _pipe->reader_close(); }
|
|
||||||
|
|
||||||
bool check_unblock(bool rd, bool, bool) const override
|
|
||||||
{
|
|
||||||
/* unblock if the writer has already closed its pipe end */
|
|
||||||
if (_pipe->writer_is_gone())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return (rd && _pipe->data_avail_for_reading());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nonblocking() override { return _nonblocking; }
|
|
||||||
|
|
||||||
bool read(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
size_t const max_count =
|
|
||||||
min(sysio.read_in.count,
|
|
||||||
sizeof(sysio.read_out.chunk));
|
|
||||||
|
|
||||||
sysio.read_out.count =
|
|
||||||
_pipe->read(sysio.read_out.chunk, max_count);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fcntl(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
switch (sysio.fcntl_in.cmd) {
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
|
||||||
sysio.fcntl_out.result = Sysio::OPEN_MODE_RDONLY;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS:
|
|
||||||
_nonblocking = (sysio.fcntl_in.long_arg &
|
|
||||||
Sysio::FCNTL_FILE_STATUS_FLAG_NONBLOCK);
|
|
||||||
sysio.fcntl_out.result = 0;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fstat(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
sysio.fstat_out.st.type = Vfs::Node_type::CONTINUOUS_FILE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__PIPE_IO_CHANNEL_H_ */
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Utility for checking array bounds
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-18
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__RANGE_CHECKED_INDEX_H_
|
|
||||||
#define _NOUX__RANGE_CHECKED_INDEX_H_
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
class Index_out_of_range { };
|
|
||||||
template <typename> struct Range_checked_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct Noux::Range_checked_index
|
|
||||||
{
|
|
||||||
T value;
|
|
||||||
T const max;
|
|
||||||
|
|
||||||
Range_checked_index(T value, T max) : value(value), max(max) { }
|
|
||||||
|
|
||||||
Range_checked_index<T> operator++ (int)
|
|
||||||
{
|
|
||||||
T old_value = value;
|
|
||||||
|
|
||||||
if (++value >= max)
|
|
||||||
throw Index_out_of_range();
|
|
||||||
|
|
||||||
return Range_checked_index<T>(old_value, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator T () { return value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__RANGE_CHECKED_INDEX_H_ */
|
|
|
@ -1,425 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Region map implementation used by Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \author Martin Stein
|
|
||||||
* \date 2012-02-22
|
|
||||||
*
|
|
||||||
* The custom region-map implementation is used for recording all regions
|
|
||||||
* attached to the region map. Using the recorded information, the address-
|
|
||||||
* space layout can then be replayed onto a new process created via fork.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__REGION_MAP_COMPONENT_H_
|
|
||||||
#define _NOUX__REGION_MAP_COMPONENT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <region_map/client.h>
|
|
||||||
#include <base/rpc_server.h>
|
|
||||||
#include <util/retry.h>
|
|
||||||
#include <pd_session/capability.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <dataspace_registry.h>
|
|
||||||
|
|
||||||
namespace Noux { class Region_map_component; }
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Region_map_component : public Rpc_object<Region_map>,
|
|
||||||
public Dataspace_info
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
static constexpr bool verbose_attach = false;
|
|
||||||
static constexpr bool verbose_replay = false;
|
|
||||||
|
|
||||||
Allocator &_alloc;
|
|
||||||
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Record of an attached dataspace
|
|
||||||
*/
|
|
||||||
struct Region : private List<Region>::Element, private Dataspace_user
|
|
||||||
{
|
|
||||||
friend class Region_map_component; /* list operations */
|
|
||||||
friend class List<Noux::Region_map_component::Region>;
|
|
||||||
|
|
||||||
Region_map_component &rm;
|
|
||||||
Dataspace_capability ds;
|
|
||||||
size_t size;
|
|
||||||
off_t offset;
|
|
||||||
addr_t local_addr;
|
|
||||||
bool executable;
|
|
||||||
|
|
||||||
Region(Region_map_component &rm,
|
|
||||||
Dataspace_capability ds, size_t size,
|
|
||||||
off_t offset, addr_t local_addr, bool exec)
|
|
||||||
:
|
|
||||||
rm(rm), ds(ds), size(size), offset(offset),
|
|
||||||
local_addr(local_addr), executable(exec)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if region contains specified address
|
|
||||||
*/
|
|
||||||
bool contains(addr_t addr) const
|
|
||||||
{
|
|
||||||
return (addr >= local_addr) && (addr < local_addr + size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Region *next_region()
|
|
||||||
{
|
|
||||||
return List<Region>::Element::next();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dissolve(Dataspace_info &ds) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
Lock _region_lock { };
|
|
||||||
List<Region> _regions { };
|
|
||||||
|
|
||||||
Region *_lookup_region_by_addr(addr_t local_addr)
|
|
||||||
{
|
|
||||||
Region *curr = _regions.first();
|
|
||||||
for (; curr; curr = curr->next_region()) {
|
|
||||||
if (curr->contains(local_addr))
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapped region map at core
|
|
||||||
*/
|
|
||||||
Region_map_client _rm;
|
|
||||||
|
|
||||||
Pd_connection &_pd;
|
|
||||||
|
|
||||||
Dataspace_registry &_ds_registry;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param pd protection domain the region map belongs to, used for
|
|
||||||
* quota upgrades
|
|
||||||
* \param rm region map at core
|
|
||||||
*/
|
|
||||||
Region_map_component(Allocator &alloc, Rpc_entrypoint &ep,
|
|
||||||
Dataspace_registry &ds_registry,
|
|
||||||
Pd_connection &pd,
|
|
||||||
Capability<Region_map> rm)
|
|
||||||
:
|
|
||||||
Dataspace_info(Region_map_client(rm).dataspace()),
|
|
||||||
_alloc(alloc), _ep(ep), _rm(rm), _pd(pd), _ds_registry(ds_registry)
|
|
||||||
{
|
|
||||||
_ep.manage(this);
|
|
||||||
_ds_registry.insert(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destructor
|
|
||||||
*/
|
|
||||||
~Region_map_component()
|
|
||||||
{
|
|
||||||
_ds_registry.remove(this);
|
|
||||||
_ep.dissolve(this);
|
|
||||||
|
|
||||||
Region *curr;
|
|
||||||
while ((curr = _regions.first()))
|
|
||||||
detach(curr->local_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return address where the specified dataspace is attached
|
|
||||||
*
|
|
||||||
* This function is used by the 'Pd_session_component' to look up
|
|
||||||
* the base addresses for the stack area and linker area.
|
|
||||||
*/
|
|
||||||
addr_t lookup_region_base(Dataspace_capability ds)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_region_lock);
|
|
||||||
|
|
||||||
Region *curr = _regions.first();
|
|
||||||
for (; curr; curr = curr->next_region()) {
|
|
||||||
if (curr->ds.local_name() == ds.local_name())
|
|
||||||
return curr->local_addr;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replay attachments onto specified region map
|
|
||||||
*
|
|
||||||
* \param dst_ram backing store used for allocating the
|
|
||||||
* the copies of RAM dataspaces
|
|
||||||
* \param ds_registry dataspace registry used for keeping track
|
|
||||||
* of newly created dataspaces
|
|
||||||
* \param ep entrypoint used to serve the RPC interface
|
|
||||||
* of forked managed dataspaces
|
|
||||||
*/
|
|
||||||
void replay(Ram_allocator &dst_ram,
|
|
||||||
Region_map &dst_rm,
|
|
||||||
Region_map &local_rm,
|
|
||||||
Allocator &alloc,
|
|
||||||
Dataspace_registry &ds_registry,
|
|
||||||
Rpc_entrypoint &ep)
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_region_lock);
|
|
||||||
for (Region *curr = _regions.first(); curr; curr = curr->next_region()) {
|
|
||||||
|
|
||||||
auto lambda = [&] (Dataspace_info *info)
|
|
||||||
{
|
|
||||||
Dataspace_capability ds;
|
|
||||||
if (info) {
|
|
||||||
|
|
||||||
ds = info->fork(dst_ram, local_rm, alloc, ds_registry, ep);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX We could detect dataspaces that are attached
|
|
||||||
* more than once. For now, we create a new fork
|
|
||||||
* for each attachment.
|
|
||||||
*/
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
warning("replay: missing ds_info for dataspace at addr ",
|
|
||||||
Hex(curr->local_addr));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the dataspace is not a RAM dataspace, assume that
|
|
||||||
* it's a ROM dataspace.
|
|
||||||
*
|
|
||||||
* XXX Handle ROM dataspaces explicitly. For once, we
|
|
||||||
* need to make sure that they remain available
|
|
||||||
* until the child process exits even if the parent
|
|
||||||
* process exits earlier. Furthermore, we would
|
|
||||||
* like to detect unexpected dataspaces.
|
|
||||||
*/
|
|
||||||
ds = curr->ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The call of 'info->fork' returns a invalid dataspace
|
|
||||||
* capability for the stack area and linker area. Those
|
|
||||||
* region maps are directly replayed and attached in
|
|
||||||
* 'Pd_session_component::replay'. So we can skip them
|
|
||||||
* here.
|
|
||||||
*/
|
|
||||||
if (!ds.valid()) {
|
|
||||||
if (verbose_replay)
|
|
||||||
warning("replay: skip dataspace of region ",
|
|
||||||
Hex(curr->local_addr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum { USE_LOCAL_ADDR = true };
|
|
||||||
dst_rm.attach(ds, curr->size, curr->offset, USE_LOCAL_ADDR,
|
|
||||||
curr->local_addr, curr->executable);
|
|
||||||
};
|
|
||||||
_ds_registry.apply(curr->ds, lambda);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************
|
|
||||||
** Region_map interface **
|
|
||||||
**************************/
|
|
||||||
|
|
||||||
Local_addr attach(Dataspace_capability ds,
|
|
||||||
size_t size, off_t offset,
|
|
||||||
bool use_local_addr,
|
|
||||||
Local_addr local_addr,
|
|
||||||
bool executable,
|
|
||||||
bool writeable) override
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Region map subtracts offset from size if size is 0
|
|
||||||
*/
|
|
||||||
if (size == 0) size = Dataspace_client(ds).size() - offset;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
try {
|
|
||||||
local_addr = _rm.attach(ds, size, offset, use_local_addr,
|
|
||||||
local_addr, executable, writeable);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (Out_of_ram) { _pd.upgrade_ram(8*1024); }
|
|
||||||
catch (Out_of_caps) { _pd.upgrade_caps(2); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Region * region = new (_alloc) Region(*this, ds, size, offset,
|
|
||||||
local_addr, executable);
|
|
||||||
|
|
||||||
/* register region as user of RAM dataspaces */
|
|
||||||
auto lambda = [&] (Dataspace_info *info)
|
|
||||||
{
|
|
||||||
if (info) {
|
|
||||||
info->register_user(*region);
|
|
||||||
} else {
|
|
||||||
if (verbose_attach) {
|
|
||||||
warning("trying to attach unknown dataspace type "
|
|
||||||
"ds=", ds.local_name(), " "
|
|
||||||
"info@", info, " "
|
|
||||||
"local_addr=", Hex(local_addr), " "
|
|
||||||
"size=", Dataspace_client(ds).size(), " "
|
|
||||||
"offset=", Hex(offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
_ds_registry.apply(ds, lambda);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Record attachment for later replay (needed during fork)
|
|
||||||
*/
|
|
||||||
Lock::Guard guard(_region_lock);
|
|
||||||
_regions.insert(region);
|
|
||||||
|
|
||||||
return local_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void detach(Local_addr local_addr) override
|
|
||||||
{
|
|
||||||
Region * region = 0;
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_region_lock);
|
|
||||||
region = _lookup_region_by_addr(local_addr);
|
|
||||||
if (!region) {
|
|
||||||
warning("attempt to detach unknown region at ", (void *)local_addr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_regions.remove(region);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ds_registry.apply(region->ds, [&] (Dataspace_info *info) {
|
|
||||||
if (info) info->unregister_user(*region); });
|
|
||||||
|
|
||||||
destroy(_alloc, region);
|
|
||||||
|
|
||||||
_rm.detach(local_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fault_handler(Signal_context_capability handler) override
|
|
||||||
{
|
|
||||||
return _rm.fault_handler(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
State state() override
|
|
||||||
{
|
|
||||||
return _rm.state();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dataspace_capability dataspace() override
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We cannot call '_rm.dataspace()' here because NOVA would
|
|
||||||
* hand out a capability that is unequal to the one we got
|
|
||||||
* during the construction of the 'Dataspace_info' base class.
|
|
||||||
* To work around this problem, we return the capability
|
|
||||||
* that is kept in the 'Dataspace_info'.
|
|
||||||
*/
|
|
||||||
return ds_cap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
** Dataspace_info interface **
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
Dataspace_capability fork(Ram_allocator &,
|
|
||||||
Region_map &,
|
|
||||||
Allocator &,
|
|
||||||
Dataspace_registry &,
|
|
||||||
Rpc_entrypoint &) override
|
|
||||||
{
|
|
||||||
return Dataspace_capability();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return leaf region map that covers a given address
|
|
||||||
*
|
|
||||||
* \param addr address that is covered by the requested region map
|
|
||||||
*/
|
|
||||||
Capability<Region_map> lookup_region_map(addr_t const addr) override
|
|
||||||
{
|
|
||||||
/* if there's no region that could be a sub RM then we're a leaf */
|
|
||||||
Region * const region = _lookup_region_by_addr(addr);
|
|
||||||
if (!region) { return Rpc_object<Region_map>::cap(); }
|
|
||||||
|
|
||||||
auto lambda = [&] (Dataspace_info *info)
|
|
||||||
{
|
|
||||||
/* if there is no info for the region it can't be a sub RM */
|
|
||||||
if (!info) { return Rpc_object<Region_map>::cap(); }
|
|
||||||
|
|
||||||
/* ask the dataspace info for an appropriate sub RM */
|
|
||||||
addr_t const region_base = region->local_addr;
|
|
||||||
addr_t const region_off = region->offset;
|
|
||||||
addr_t const sub_addr = addr - region_base + region_off;
|
|
||||||
Capability<Region_map> sub_rm = info->lookup_region_map(sub_addr);
|
|
||||||
|
|
||||||
/* if the result is invalid the dataspace is no sub RM */
|
|
||||||
if (!sub_rm.valid()) { return Rpc_object<Region_map>::cap(); }
|
|
||||||
return sub_rm;
|
|
||||||
};
|
|
||||||
return _ds_registry.apply(region->ds, lambda);
|
|
||||||
}
|
|
||||||
|
|
||||||
void poke(Region_map &rm, addr_t dst_addr, char const *src, size_t len) override
|
|
||||||
{
|
|
||||||
Dataspace_capability ds_cap;
|
|
||||||
addr_t local_addr;
|
|
||||||
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_region_lock);
|
|
||||||
|
|
||||||
Region *region = _lookup_region_by_addr(dst_addr);
|
|
||||||
if (!region) {
|
|
||||||
error("poke: no region at ", Hex(dst_addr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test if start and end address occupied by the object
|
|
||||||
* type refers to the same region.
|
|
||||||
*/
|
|
||||||
if (region != _lookup_region_by_addr(dst_addr + len - 1)) {
|
|
||||||
error("attempt to write beyond region boundary");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (region->offset) {
|
|
||||||
error("poke: writing to region with offset is not supported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ds_cap = region->ds;
|
|
||||||
local_addr = region->local_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ds_registry.apply(ds_cap, [&] (Dataspace_info *info) {
|
|
||||||
if (!info) {
|
|
||||||
error("attempt to write to unknown dataspace type");
|
|
||||||
for (;;);
|
|
||||||
}
|
|
||||||
info->poke(rm, dst_addr - local_addr, src, len);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
inline void Noux::Region_map_component::Region::dissolve(Dataspace_info &)
|
|
||||||
{
|
|
||||||
rm.detach(local_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _NOUX__REGION_MAP_COMPONENT_H_ */
|
|
|
@ -1,261 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief ROM session implementation used by Noux processes
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2013-07-18
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__ROM_SESSION_COMPONENT_H_
|
|
||||||
#define _NOUX__ROM_SESSION_COMPONENT_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <rom_session/connection.h>
|
|
||||||
#include <base/rpc_server.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include "vfs_io_channel.h"
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
|
|
||||||
struct Vfs_dataspace;
|
|
||||||
struct Rom_dataspace_info;
|
|
||||||
class Rom_session_component;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dataspace obtained from the VFS
|
|
||||||
*/
|
|
||||||
struct Noux::Vfs_dataspace
|
|
||||||
{
|
|
||||||
typedef Child_policy::Name Name;
|
|
||||||
Name const name;
|
|
||||||
|
|
||||||
Vfs::File_system &root_dir;
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry;
|
|
||||||
Genode::Ram_allocator &ram;
|
|
||||||
Genode::Region_map &rm;
|
|
||||||
Genode::Allocator &alloc;
|
|
||||||
|
|
||||||
Dataspace_capability ds { };
|
|
||||||
bool got_ds_from_vfs { true };
|
|
||||||
|
|
||||||
Vfs_dataspace(Vfs::File_system &root_dir,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Name const &name,
|
|
||||||
Genode::Ram_allocator &ram, Genode::Region_map &rm,
|
|
||||||
Genode::Allocator &alloc)
|
|
||||||
:
|
|
||||||
name(name),
|
|
||||||
root_dir(root_dir), vfs_io_waiter_registry(vfs_io_waiter_registry),
|
|
||||||
ram(ram), rm(rm), alloc(alloc)
|
|
||||||
{
|
|
||||||
ds = root_dir.dataspace(name.string());
|
|
||||||
|
|
||||||
if (!ds.valid()) {
|
|
||||||
|
|
||||||
got_ds_from_vfs = false;
|
|
||||||
|
|
||||||
Vfs::Directory_service::Stat stat_out;
|
|
||||||
|
|
||||||
if (root_dir.stat(name.string(), stat_out) != Vfs::Directory_service::STAT_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (stat_out.size == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vfs::Vfs_handle *file;
|
|
||||||
if (root_dir.open(name.string(),
|
|
||||||
Vfs::Directory_service::OPEN_MODE_RDONLY,
|
|
||||||
&file,
|
|
||||||
alloc) != Vfs::Directory_service::OPEN_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vfs_handle_context read_context;
|
|
||||||
Vfs::Vfs_handle::Guard guard(file);
|
|
||||||
file->handler(&read_context);
|
|
||||||
|
|
||||||
ds = ram.alloc(stat_out.size);
|
|
||||||
|
|
||||||
char *addr = rm.attach(static_cap_cast<Genode::Ram_dataspace>(ds));
|
|
||||||
|
|
||||||
for (Vfs::file_size bytes_read = 0; bytes_read < stat_out.size; ) {
|
|
||||||
|
|
||||||
Registered_no_delete<Vfs_io_waiter>
|
|
||||||
vfs_io_waiter(vfs_io_waiter_registry);
|
|
||||||
|
|
||||||
while (!file->fs().queue_read(file, stat_out.size - bytes_read))
|
|
||||||
vfs_io_waiter.wait_for_io();
|
|
||||||
|
|
||||||
Vfs::File_io_service::Read_result read_result;
|
|
||||||
|
|
||||||
Vfs::file_size out_count;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
read_result = file->fs().complete_read(file, addr + bytes_read,
|
|
||||||
stat_out.size,
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (read_result != Vfs::File_io_service::READ_OK) {
|
|
||||||
Genode::error("Error reading dataspace from VFS");
|
|
||||||
rm.detach(addr);
|
|
||||||
ram.free(static_cap_cast<Genode::Ram_dataspace>(ds));
|
|
||||||
root_dir.close(file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_read += out_count;
|
|
||||||
file->advance_seek(out_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
rm.detach(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~Vfs_dataspace()
|
|
||||||
{
|
|
||||||
if (got_ds_from_vfs)
|
|
||||||
root_dir.release(name.string(), ds);
|
|
||||||
else
|
|
||||||
ram.free(static_cap_cast<Genode::Ram_dataspace>(ds));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Rom_dataspace_info : Dataspace_info
|
|
||||||
{
|
|
||||||
Rom_dataspace_info(Dataspace_capability ds) : Dataspace_info(ds) { }
|
|
||||||
|
|
||||||
~Rom_dataspace_info() { }
|
|
||||||
|
|
||||||
Dataspace_capability fork(Ram_allocator &,
|
|
||||||
Region_map &,
|
|
||||||
Allocator &alloc,
|
|
||||||
Dataspace_registry &ds_registry,
|
|
||||||
Rpc_entrypoint &) override
|
|
||||||
{
|
|
||||||
ds_registry.insert(new (alloc) Rom_dataspace_info(ds_cap()));
|
|
||||||
return ds_cap();
|
|
||||||
}
|
|
||||||
|
|
||||||
void poke(Region_map &, addr_t, char const *, size_t) override
|
|
||||||
{
|
|
||||||
error("attempt to poke onto a ROM dataspace");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local ROM service
|
|
||||||
*
|
|
||||||
* Depending on the ROM name, the data is provided by the VFS (if the name
|
|
||||||
* starts with a '/' or the parent's ROM service. If the name empty, an
|
|
||||||
* invalid dataspace capability is returned (this is used for the binary
|
|
||||||
* ROM session of a forked process).
|
|
||||||
*/
|
|
||||||
class Noux::Rom_session_component : public Rpc_object<Rom_session>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef Child_policy::Name Name;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Allocator &_alloc;
|
|
||||||
Rpc_entrypoint &_ep;
|
|
||||||
Vfs::File_system &_root_dir;
|
|
||||||
Vfs_io_waiter_registry &_vfs_io_waiter_registry;
|
|
||||||
Dataspace_registry &_ds_registry;
|
|
||||||
|
|
||||||
Constructible<Vfs_dataspace> _rom_from_vfs { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapped ROM session at core
|
|
||||||
*/
|
|
||||||
Constructible<Rom_connection> _rom_from_parent { };
|
|
||||||
|
|
||||||
Dataspace_capability _init_ds_cap(Env &env, Name const &name)
|
|
||||||
{
|
|
||||||
if (name.string()[0] == '/') {
|
|
||||||
_rom_from_vfs.construct(_root_dir, _vfs_io_waiter_registry,
|
|
||||||
name, env.ram(), env.rm(), _alloc);
|
|
||||||
return _rom_from_vfs->ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
_rom_from_parent.construct(env, name.string());
|
|
||||||
Dataspace_capability ds = _rom_from_parent->dataspace();
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dataspace_capability const _ds_cap;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Rom_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
|
|
||||||
Vfs::File_system &root_dir,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Dataspace_registry &ds_registry, Name const &name)
|
|
||||||
:
|
|
||||||
_alloc(alloc), _ep(ep), _root_dir(root_dir),
|
|
||||||
_vfs_io_waiter_registry(vfs_io_waiter_registry),
|
|
||||||
_ds_registry(ds_registry), _ds_cap(_init_ds_cap(env, name))
|
|
||||||
{
|
|
||||||
_ep.manage(this);
|
|
||||||
_ds_registry.insert(new (alloc) Rom_dataspace_info(_ds_cap));
|
|
||||||
}
|
|
||||||
|
|
||||||
~Rom_session_component()
|
|
||||||
{
|
|
||||||
Rom_dataspace_info *ds_info = nullptr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Lookup and lock ds info instead of directly accessing
|
|
||||||
* the '_ds_info' member.
|
|
||||||
*/
|
|
||||||
_ds_registry.apply(_ds_cap, [&] (Rom_dataspace_info *info) {
|
|
||||||
|
|
||||||
if (!info) {
|
|
||||||
error("~Rom_session_component: unexpected !info");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ds_registry.remove(info);
|
|
||||||
|
|
||||||
info->dissolve_users();
|
|
||||||
|
|
||||||
ds_info = info;
|
|
||||||
});
|
|
||||||
destroy(_alloc, ds_info);
|
|
||||||
_ep.dissolve(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
** ROM session interface **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
Rom_dataspace_capability dataspace() override
|
|
||||||
{
|
|
||||||
return static_cap_cast<Rom_dataspace>(_ds_cap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sigh(Signal_context_capability) override { }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__ROM_SESSION_COMPONENT_H_ */
|
|
|
@ -1,159 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Reference-counting smart pointer
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__SHARED_POINTER_PTR_H_
|
|
||||||
#define _NOUX__SHARED_POINTER_PTR_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/lock.h>
|
|
||||||
#include <base/allocator.h>
|
|
||||||
#include <base/log.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Reference_counter;
|
|
||||||
class Shared_pointer_base;
|
|
||||||
template <typename T> class Shared_pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Reference_counter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Lock _lock { };
|
|
||||||
long _value;
|
|
||||||
|
|
||||||
friend class Shared_pointer_base;
|
|
||||||
|
|
||||||
void _inc_ref_count()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
_value++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return reference counter after decrement
|
|
||||||
*/
|
|
||||||
long _dec_ref_count()
|
|
||||||
{
|
|
||||||
Lock::Guard guard(_lock);
|
|
||||||
return --_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Reference_counter() : _value(0) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Shared_pointer_base
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Reference_counter *_ref_counter;
|
|
||||||
|
|
||||||
Shared_pointer_base(Reference_counter *ref_counter)
|
|
||||||
: _ref_counter(ref_counter) { }
|
|
||||||
|
|
||||||
void _inc_ref_count() {
|
|
||||||
if (_ref_counter) _ref_counter->_inc_ref_count(); }
|
|
||||||
|
|
||||||
bool _dec_ref_count() {
|
|
||||||
return _ref_counter && (_ref_counter->_dec_ref_count() == 0); }
|
|
||||||
|
|
||||||
long count() const { return _ref_counter ? _ref_counter->_value : -99; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Noux::Shared_pointer : public Shared_pointer_base
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
T *_ptr;
|
|
||||||
Allocator *_alloc;
|
|
||||||
|
|
||||||
void _dec_ref_count()
|
|
||||||
{
|
|
||||||
if (Shared_pointer_base::_dec_ref_count()) {
|
|
||||||
|
|
||||||
destroy(_alloc, _ptr);
|
|
||||||
_ptr = 0;
|
|
||||||
_alloc = 0;
|
|
||||||
_ref_counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Shared_pointer() : Shared_pointer_base(0), _ptr(0), _alloc(0) { }
|
|
||||||
|
|
||||||
Shared_pointer(T *ptr, Allocator &alloc)
|
|
||||||
: Shared_pointer_base(ptr), _ptr(ptr), _alloc(&alloc)
|
|
||||||
{
|
|
||||||
_inc_ref_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared_pointer(Shared_pointer const & from)
|
|
||||||
:
|
|
||||||
Shared_pointer_base(from._ref_counter),
|
|
||||||
_ptr(from._ptr), _alloc(from._alloc)
|
|
||||||
{
|
|
||||||
_inc_ref_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared_pointer & operator=(const Shared_pointer& from)
|
|
||||||
{
|
|
||||||
/* check for self assignment */
|
|
||||||
if (_ptr == from._ptr)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
/* forget about original pointed-to object */
|
|
||||||
_dec_ref_count();
|
|
||||||
|
|
||||||
_ref_counter = from._ref_counter;
|
|
||||||
_ptr = from._ptr;
|
|
||||||
_alloc = from._alloc;
|
|
||||||
|
|
||||||
/* account for newly assigned pointed-to object */
|
|
||||||
_inc_ref_count();
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Shared_pointer()
|
|
||||||
{
|
|
||||||
_dec_ref_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
T * operator -> () { return _ptr; }
|
|
||||||
T const* operator -> () const { return _ptr; }
|
|
||||||
|
|
||||||
operator bool () const { return _ptr != 0; }
|
|
||||||
|
|
||||||
bool operator== (const Shared_pointer &other)
|
|
||||||
{
|
|
||||||
return (_ptr == other._ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename To>
|
|
||||||
Shared_pointer<To> dynamic_pointer_cast()
|
|
||||||
{
|
|
||||||
return Shared_pointer<To>(dynamic_cast<To *>(_ptr), _alloc);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__SHARED_POINTER_H_ */
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
||||||
TARGET = noux
|
|
||||||
LIBS = base vfs
|
|
||||||
SRC_CC = main.cc syscall.cc
|
|
||||||
INC_DIR += $(PRG_DIR)
|
|
||||||
|
|
||||||
vpath %.cc $(PRG_DIR)
|
|
|
@ -1,232 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief I/O channel targeting Genode's terminal interface
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-10-21
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__TERMINAL_IO_CHANNEL_H_
|
|
||||||
#define _NOUX__TERMINAL_IO_CHANNEL_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/string.h>
|
|
||||||
#include <base/log.h>
|
|
||||||
#include <os/ring_buffer.h>
|
|
||||||
#include <terminal_session/connection.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <io_channel.h>
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
|
|
||||||
namespace Noux { struct Terminal_io_channel; }
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Terminal_io_channel : Io_channel
|
|
||||||
{
|
|
||||||
Terminal::Session &_terminal;
|
|
||||||
|
|
||||||
Signal_handler<Terminal_io_channel> _read_avail_handler;
|
|
||||||
|
|
||||||
Signal_handler<Terminal_io_channel> _resize_handler;
|
|
||||||
|
|
||||||
enum { EOF = 4 };
|
|
||||||
|
|
||||||
bool eof = false;
|
|
||||||
|
|
||||||
enum Type { STDIN, STDOUT, STDERR } type;
|
|
||||||
|
|
||||||
Ring_buffer<char, Sysio::CHUNK_SIZE + 1> read_buffer { };
|
|
||||||
|
|
||||||
Terminal_io_channel(Terminal::Session &terminal, Type type,
|
|
||||||
Entrypoint &ep)
|
|
||||||
:
|
|
||||||
_terminal(terminal),
|
|
||||||
_read_avail_handler(ep, *this, &Terminal_io_channel::_handle_read_avail),
|
|
||||||
_resize_handler (ep, *this, &Terminal_io_channel::_handle_resize),
|
|
||||||
type(type)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Enable wake up STDIN channel on the presence of new input
|
|
||||||
*
|
|
||||||
* By registering our I/O channel as signal handler, the Noux
|
|
||||||
* main loop will be unblocked on the arrival of new input.
|
|
||||||
* It will check if the received signal belongs to an I/O channel
|
|
||||||
* and invokes the 'handle_signal' function of the I/O channel.
|
|
||||||
*
|
|
||||||
* This gives us the opportunity to handle the unblocking of
|
|
||||||
* blocking system calls such as 'select'.
|
|
||||||
*/
|
|
||||||
if (type == STDIN) {
|
|
||||||
terminal.read_avail_sigh(_read_avail_handler);
|
|
||||||
terminal.size_changed_sigh(_resize_handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
size_t const count = min(sysio.write_in.count,
|
|
||||||
sizeof(sysio.write_in.chunk));
|
|
||||||
|
|
||||||
_terminal.write(sysio.write_in.chunk, count);
|
|
||||||
|
|
||||||
sysio.write_out.count = count;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool read(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
if (type != STDIN) {
|
|
||||||
error("attempt to read from terminal output channel");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* deliver EOF observed by the previous 'read' call */
|
|
||||||
if (eof) {
|
|
||||||
sysio.read_out.count = 0;
|
|
||||||
eof = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t const max_count =
|
|
||||||
min(sysio.read_in.count,
|
|
||||||
sizeof(sysio.read_out.chunk));
|
|
||||||
|
|
||||||
for (sysio.read_out.count = 0;
|
|
||||||
(sysio.read_out.count < max_count) && !read_buffer.empty();
|
|
||||||
sysio.read_out.count++) {
|
|
||||||
|
|
||||||
char c = read_buffer.get();
|
|
||||||
|
|
||||||
if (c == EOF) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If EOF was the only character of the batch, the count
|
|
||||||
* has reached zero. In this case the read result indicates
|
|
||||||
* the EOF condition as is. However, if count is greater
|
|
||||||
* than zero, we deliver the previous characters of the
|
|
||||||
* batch and return the zero result from the subsequent
|
|
||||||
* 'read' call. This condition is tracked by the 'eof'
|
|
||||||
* variable.
|
|
||||||
*/
|
|
||||||
if (sysio.read_out.count > 0)
|
|
||||||
eof = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
sysio.read_out.chunk[sysio.read_out.count] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fcntl(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Actually it is "inappropiate" to use fcntl() directly on terminals
|
|
||||||
* (atleast according to the Open Group Specification). We do it anyway
|
|
||||||
* since in our case stdout/in/err is directly connected to the terminal.
|
|
||||||
*
|
|
||||||
* Some GNU programms check if stdout is open by calling fcntl(stdout, F_GETFL, ...).
|
|
||||||
*/
|
|
||||||
switch (sysio.fcntl_in.cmd) {
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
|
||||||
sysio.fcntl_out.result = 0;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fstat(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
sysio.fstat_out.st.type = Vfs::Node_type::CONTINUOUS_FILE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_unblock(bool rd, bool wr, bool) const override
|
|
||||||
{
|
|
||||||
/* never block for writing */
|
|
||||||
if (wr) return true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unblock I/O channel if the terminal has new user input. Channels
|
|
||||||
* otther than STDIN will never unblock.
|
|
||||||
*/
|
|
||||||
return (rd && (type == STDIN) && !read_buffer.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ioctl(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
switch (sysio.ioctl_in.request) {
|
|
||||||
|
|
||||||
case Vfs::File_io_service::IOCTL_OP_TIOCGWINSZ:
|
|
||||||
{
|
|
||||||
Terminal::Session::Size size = _terminal.size();
|
|
||||||
sysio.ioctl_out.tiocgwinsz.rows = size.lines();
|
|
||||||
sysio.ioctl_out.tiocgwinsz.columns = size.columns();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAF:
|
|
||||||
{
|
|
||||||
warning(__func__, ": OP_TIOCSETAF not implemented");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAW:
|
|
||||||
{
|
|
||||||
warning(__func__, ": OP_TIOCSETAW not implemented");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
warning("invalid ioctl request ", (int)sysio.ioctl_in.request);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handle_read_avail()
|
|
||||||
{
|
|
||||||
while ((read_buffer.avail_capacity() > 0) &&
|
|
||||||
_terminal.avail()) {
|
|
||||||
|
|
||||||
char c;
|
|
||||||
_terminal.read(&c, 1);
|
|
||||||
|
|
||||||
enum { INTERRUPT = 3 };
|
|
||||||
|
|
||||||
if (c == INTERRUPT) {
|
|
||||||
Io_channel::invoke_all_interrupt_handlers(Sysio::SIG_INT);
|
|
||||||
} else {
|
|
||||||
read_buffer.add(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Io_channel::invoke_all_notifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handle_resize()
|
|
||||||
{
|
|
||||||
/* respond to terminal-close event */
|
|
||||||
Terminal::Session::Size const size = _terminal.size();
|
|
||||||
if (size.columns()*size.lines() == 0)
|
|
||||||
read_buffer.add(EOF);
|
|
||||||
|
|
||||||
Io_channel::invoke_all_interrupt_handlers(Sysio::SIG_WINCH);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__TERMINAL_IO_CHANNEL_H_ */
|
|
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Time information
|
|
||||||
* \author Josef Soentgen
|
|
||||||
* \date 2019-04-09
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2019 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__TIME_INFO_H_
|
|
||||||
#define _NOUX__TIME_INFO_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/string.h>
|
|
||||||
#include <util/xml_node.h>
|
|
||||||
#include <rtc_session/connection.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Time_info;
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Time_info : Noncopyable
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Constructible<Rtc::Connection> _rtc { };
|
|
||||||
|
|
||||||
Genode::int64_t _initial_time { 0 };
|
|
||||||
|
|
||||||
static bool _leap(unsigned year)
|
|
||||||
{
|
|
||||||
return ((year % 4) == 0
|
|
||||||
&& ((year % 100) != 0 || (year % 400) == 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert RTC timestamp to UNIX epoch (UTC)
|
|
||||||
*/
|
|
||||||
static Genode::int64_t _convert(Rtc::Timestamp const &ts)
|
|
||||||
{
|
|
||||||
if (ts.year < 1970) { return 0; }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Seconds per year lookup table
|
|
||||||
*/
|
|
||||||
static constexpr unsigned _secs_per_year[2] = {
|
|
||||||
365 * 86400, 366 * 86400,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Seconds per month lookup table
|
|
||||||
*/
|
|
||||||
static constexpr unsigned _sec_per_month[13] = {
|
|
||||||
0 * 86400,
|
|
||||||
31 * 86400, 28 * 86400, 31 * 86400, 30 * 86400,
|
|
||||||
31 * 86400, 30 * 86400, 31 * 86400, 31 * 86400,
|
|
||||||
30 * 86400, 31 * 86400, 30 * 86400, 31 * 86400
|
|
||||||
};
|
|
||||||
|
|
||||||
Genode::int64_t time = 0;
|
|
||||||
|
|
||||||
for (unsigned i = 1970; i < ts.year; i++) {
|
|
||||||
/* abuse bool conversion for seconds look up */
|
|
||||||
time += _secs_per_year[(int)_leap(i)];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 1; i < ts.month; i++) {
|
|
||||||
time += _sec_per_month[i];
|
|
||||||
}
|
|
||||||
time += _leap(ts.year) * 86400LL;
|
|
||||||
|
|
||||||
time += 86400LL * (ts.day-1);
|
|
||||||
time += 3600LL * ts.hour;
|
|
||||||
time += 60LL * ts.minute;
|
|
||||||
time += ts.second;
|
|
||||||
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Time_info(Env &env, Xml_node config)
|
|
||||||
{
|
|
||||||
/* only try to establish the connection on demand */
|
|
||||||
bool const rtc = config.attribute_value("rtc", false);
|
|
||||||
if (!rtc) { return; }
|
|
||||||
|
|
||||||
_rtc.construct(env);
|
|
||||||
_initial_time = _convert(_rtc->current_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::int64_t initial_time() const { return _initial_time; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__TIME_INFO_H_ */
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief User information
|
|
||||||
* \author Josef Soentgen
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-07-23
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__USER_INFO_H_
|
|
||||||
#define _NOUX__USER_INFO_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/string.h>
|
|
||||||
#include <util/xml_node.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <noux_session/sysio.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class User_info;
|
|
||||||
using namespace Genode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::User_info : Noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef String<Sysio::MAX_USERNAME_LEN> Name;
|
|
||||||
typedef String<Sysio::MAX_SHELL_LEN> Shell;
|
|
||||||
typedef String<Sysio::MAX_HOME_LEN> Home;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
unsigned const _uid;
|
|
||||||
unsigned const _gid;
|
|
||||||
|
|
||||||
Name const _name;
|
|
||||||
Shell const _shell;
|
|
||||||
Home const _home;
|
|
||||||
|
|
||||||
template <typename S>
|
|
||||||
S _sub_node_name(Xml_node node, char const *sub_node, S const &default_name)
|
|
||||||
{
|
|
||||||
if (!node.has_sub_node(sub_node))
|
|
||||||
return default_name;
|
|
||||||
|
|
||||||
return node.sub_node(sub_node).attribute_value("name", default_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
User_info(Xml_node node)
|
|
||||||
:
|
|
||||||
_uid (node.attribute_value("uid", 0UL)),
|
|
||||||
_gid (node.attribute_value("gid", 0UL)),
|
|
||||||
_name(node.attribute_value("name", Name("root"))),
|
|
||||||
_shell(_sub_node_name(node, "shell", Shell("/bin/bash"))),
|
|
||||||
_home (_sub_node_name(node, "home", Home("name")))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
unsigned uid() const { return _uid; }
|
|
||||||
unsigned gid() const { return _gid; }
|
|
||||||
|
|
||||||
Name name() const { return _name; }
|
|
||||||
Shell shell() const { return _shell; }
|
|
||||||
Home home() const { return _home; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__USER_INFO_H_ */
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux verbosity
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2017-01-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__VERBOSE_H_
|
|
||||||
#define _NOUX__VERBOSE_H_
|
|
||||||
|
|
||||||
#include <util/noncopyable.h>
|
|
||||||
#include <util/xml_node.h>
|
|
||||||
|
|
||||||
namespace Noux { struct Verbose; }
|
|
||||||
|
|
||||||
|
|
||||||
class Noux::Verbose : Genode::Noncopyable
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool const _enabled;
|
|
||||||
bool const _ld;
|
|
||||||
bool const _syscalls;
|
|
||||||
bool const _quota;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Verbose(Genode::Xml_node config)
|
|
||||||
:
|
|
||||||
_enabled (config.attribute_value("verbose", false)),
|
|
||||||
_ld (config.attribute_value("ld_verbose", false)),
|
|
||||||
_syscalls(config.attribute_value("verbose_syscalls", false)),
|
|
||||||
_quota (config.attribute_value("verbose_quota", false))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
bool enabled() const { return _enabled; }
|
|
||||||
bool ld() const { return _ld; }
|
|
||||||
bool syscalls() const { return _syscalls; }
|
|
||||||
bool quota() const { return _quota; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__VERBOSE_H_ */
|
|
|
@ -1,385 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief I/O channel for files opened via virtual directory service
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__VFS_IO_CHANNEL_H_
|
|
||||||
#define _NOUX__VFS_IO_CHANNEL_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
|
|
||||||
/* Noux includes */
|
|
||||||
#include <io_channel.h>
|
|
||||||
#include <vfs/dir_file_system.h>
|
|
||||||
#include <time_info.h>
|
|
||||||
|
|
||||||
namespace Noux {
|
|
||||||
class Vfs_io_waiter;
|
|
||||||
struct Vfs_handle_context;
|
|
||||||
struct Vfs_io_channel;
|
|
||||||
|
|
||||||
typedef Registry<Registered_no_delete<Vfs_io_waiter>>
|
|
||||||
Vfs_io_waiter_registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Noux::Vfs_io_waiter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Genode::Semaphore _sem { };
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void wait_for_io() { _sem.down(); }
|
|
||||||
|
|
||||||
void wakeup() { _sem.up(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Noux::Vfs_handle_context : Vfs::Io_response_handler
|
|
||||||
{
|
|
||||||
Vfs_io_waiter vfs_io_waiter { };
|
|
||||||
|
|
||||||
void read_ready_response() override {
|
|
||||||
vfs_io_waiter.wakeup(); }
|
|
||||||
|
|
||||||
void io_progress_response() override {
|
|
||||||
vfs_io_waiter.wakeup(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Noux::Vfs_io_channel : Io_channel
|
|
||||||
{
|
|
||||||
Signal_handler<Vfs_io_channel> _read_avail_handler;
|
|
||||||
|
|
||||||
void _handle_read_avail()
|
|
||||||
{
|
|
||||||
Io_channel::invoke_all_notifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vfs::Vfs_handle &_fh;
|
|
||||||
|
|
||||||
Vfs_io_waiter_registry &_vfs_io_waiter_registry;
|
|
||||||
|
|
||||||
Absolute_path _path;
|
|
||||||
Absolute_path _leaf_path;
|
|
||||||
|
|
||||||
bool const _dir = _fh.ds().directory(_leaf_path.base());
|
|
||||||
|
|
||||||
Time_info const &_time_info;
|
|
||||||
Timer::Connection &_timer;
|
|
||||||
|
|
||||||
void _sync()
|
|
||||||
{
|
|
||||||
Milliseconds const ms =
|
|
||||||
_timer.curr_time().trunc_to_plain_ms();
|
|
||||||
uint64_t sec = _time_info.initial_time();
|
|
||||||
sec += (ms.value / 1000);
|
|
||||||
|
|
||||||
Vfs::Timestamp ts { .value = (long long)sec };
|
|
||||||
|
|
||||||
Registered_no_delete<Vfs_io_waiter>
|
|
||||||
vfs_io_waiter(_vfs_io_waiter_registry);
|
|
||||||
|
|
||||||
if ((_fh.status_flags() & Sysio::OPEN_MODE_ACCMODE) != Sysio::OPEN_MODE_RDONLY) {
|
|
||||||
for (;;) {
|
|
||||||
if (_fh.fs().update_modification_timestamp(&_fh, ts)) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
Genode::error("_sync: update_modification_timestamp failed");
|
|
||||||
vfs_io_waiter.wait_for_io();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!_fh.fs().queue_sync(&_fh))
|
|
||||||
vfs_io_waiter.wait_for_io();
|
|
||||||
|
|
||||||
while (_fh.fs().complete_sync(&_fh) == Vfs::File_io_service::SYNC_QUEUED)
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Vfs_io_channel(char const *path, char const *leaf_path,
|
|
||||||
Vfs::Vfs_handle *vfs_handle,
|
|
||||||
Vfs_io_waiter_registry &vfs_io_waiter_registry,
|
|
||||||
Entrypoint &ep,
|
|
||||||
Time_info const &time_info,
|
|
||||||
Timer::Connection &timer)
|
|
||||||
:
|
|
||||||
_read_avail_handler(ep, *this, &Vfs_io_channel::_handle_read_avail),
|
|
||||||
_fh(*vfs_handle), _vfs_io_waiter_registry(vfs_io_waiter_registry),
|
|
||||||
_path(path), _leaf_path(leaf_path), _time_info(time_info), _timer(timer)
|
|
||||||
{
|
|
||||||
_fh.fs().register_read_ready_sigh(&_fh, _read_avail_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Vfs_io_channel()
|
|
||||||
{
|
|
||||||
_sync();
|
|
||||||
|
|
||||||
_fh.ds().close(&_fh);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
if (_dir) {
|
|
||||||
sysio.error.write = Vfs::File_io_service::WRITE_ERR_INVALID;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vfs::file_size count = sysio.write_in.count;
|
|
||||||
Vfs::file_size out_count = 0;
|
|
||||||
|
|
||||||
Registered_no_delete<Vfs_io_waiter>
|
|
||||||
vfs_io_waiter(_vfs_io_waiter_registry);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
try {
|
|
||||||
sysio.error.write = _fh.fs().write(&_fh, sysio.write_in.chunk,
|
|
||||||
count, 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 (sysio.error.write != Vfs::File_io_service::WRITE_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_fh.advance_seek(out_count);
|
|
||||||
|
|
||||||
sysio.write_out.count = out_count;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool read(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
if (_dir) {
|
|
||||||
sysio.error.read = Vfs::File_io_service::READ_ERR_INVALID;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t count = min(sysio.read_in.count, sizeof(sysio.read_out.chunk));
|
|
||||||
|
|
||||||
Vfs::file_size out_count = 0;
|
|
||||||
|
|
||||||
Registered_no_delete<Vfs_io_waiter>
|
|
||||||
vfs_io_waiter(_vfs_io_waiter_registry);
|
|
||||||
|
|
||||||
while (!_fh.fs().queue_read(&_fh, count))
|
|
||||||
vfs_io_waiter.wait_for_io();
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
|
|
||||||
sysio.error.read = _fh.fs().complete_read(&_fh, sysio.read_out.chunk, count, out_count);
|
|
||||||
|
|
||||||
if (sysio.error.read != Vfs::File_io_service::READ_QUEUED)
|
|
||||||
break;
|
|
||||||
|
|
||||||
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 (sysio.error.read != Vfs::File_io_service::READ_OK)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
sysio.read_out.count = out_count;
|
|
||||||
|
|
||||||
_fh.advance_seek(out_count);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fstat(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
_sync();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 'sysio.stat_in' is not used in '_fh.ds().stat()',
|
|
||||||
* so no 'sysio' member translation is needed here
|
|
||||||
*/
|
|
||||||
|
|
||||||
Vfs::Directory_service::Stat stat;
|
|
||||||
sysio.error.stat = _fh.ds().stat(_leaf_path.base(), stat);
|
|
||||||
sysio.fstat_out.st = stat;
|
|
||||||
|
|
||||||
return (sysio.error.stat == Vfs::Directory_service::STAT_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ftruncate(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
_sync();
|
|
||||||
|
|
||||||
sysio.error.ftruncate = _fh.fs().ftruncate(&_fh, sysio.ftruncate_in.length);
|
|
||||||
|
|
||||||
return (sysio.error.ftruncate == Vfs::File_io_service::FTRUNCATE_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fcntl(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
switch (sysio.fcntl_in.cmd) {
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
|
|
||||||
|
|
||||||
sysio.fcntl_out.result = _fh.status_flags();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS:
|
|
||||||
_fh.status_flags(sysio.fcntl_in.long_arg);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
warning("invalid fcntl command ", (int)sysio.fcntl_in.cmd);
|
|
||||||
sysio.error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The 'dirent' function for the root directory only (the
|
|
||||||
* 'Dir_file_system::open()' function handles all requests referring
|
|
||||||
* to directories). Hence, '_path' is the absolute path of the
|
|
||||||
* directory to inspect.
|
|
||||||
*/
|
|
||||||
bool dirent(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Return artificial dir entries for "." and ".."
|
|
||||||
*/
|
|
||||||
unsigned const index = _fh.seek() / sizeof(Sysio::Dirent);
|
|
||||||
if (index < 2) {
|
|
||||||
sysio.dirent_out.entry.type = Vfs::Directory_service::Dirent_type::DIRECTORY;
|
|
||||||
strncpy(sysio.dirent_out.entry.name,
|
|
||||||
index ? ".." : ".",
|
|
||||||
sizeof(sysio.dirent_out.entry.name));
|
|
||||||
|
|
||||||
sysio.dirent_out.entry.fileno = 1;
|
|
||||||
_fh.advance_seek(sizeof(Sysio::Dirent));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Delegate remaining dir-entry request to the actual file system.
|
|
||||||
* Align index range to zero when calling the directory service.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Vfs::file_size noux_dirent_seek = _fh.seek();
|
|
||||||
_fh.seek((index - 2) * sizeof(Vfs::Directory_service::Dirent));
|
|
||||||
|
|
||||||
Vfs::file_size const count = sizeof(Vfs::Directory_service::Dirent);
|
|
||||||
|
|
||||||
Registered_no_delete<Vfs_io_waiter>
|
|
||||||
vfs_io_waiter(_vfs_io_waiter_registry);
|
|
||||||
|
|
||||||
while (!_fh.fs().queue_read(&_fh, count))
|
|
||||||
vfs_io_waiter.wait_for_io();
|
|
||||||
|
|
||||||
Vfs::File_io_service::Read_result read_result;
|
|
||||||
Vfs::file_size out_count = 0;
|
|
||||||
Vfs::Directory_service::Dirent dirent { };
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
read_result = _fh.fs().complete_read(&_fh, (char*)&dirent,
|
|
||||||
count, out_count);
|
|
||||||
|
|
||||||
if (read_result != Vfs::File_io_service::READ_QUEUED)
|
|
||||||
break;
|
|
||||||
|
|
||||||
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 ((read_result != Vfs::File_io_service::READ_OK) ||
|
|
||||||
(out_count != sizeof(dirent))) {
|
|
||||||
dirent = { };
|
|
||||||
}
|
|
||||||
|
|
||||||
_fh.seek(noux_dirent_seek);
|
|
||||||
|
|
||||||
sysio.dirent_out.entry = dirent;
|
|
||||||
|
|
||||||
_fh.advance_seek(sizeof(Sysio::Dirent));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return size of file that the I/O channel refers to
|
|
||||||
*
|
|
||||||
* Note that this function overwrites the 'sysio' argument. Do not
|
|
||||||
* call it prior saving all input arguments from the original sysio
|
|
||||||
* structure.
|
|
||||||
*/
|
|
||||||
size_t size(Sysio &sysio)
|
|
||||||
{
|
|
||||||
if (fstat(sysio))
|
|
||||||
return sysio.fstat_out.st.size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ioctl(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
Vfs::File_system::Ioctl_arg arg = (Vfs::File_system::Ioctl_arg)sysio.ioctl_in.argp;
|
|
||||||
|
|
||||||
sysio.error.ioctl = _fh.fs().ioctl(&_fh, sysio.ioctl_in.request, arg, sysio.ioctl_out);
|
|
||||||
|
|
||||||
return (sysio.error.ioctl == Vfs::File_io_service::IOCTL_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lseek(Sysio &sysio) override
|
|
||||||
{
|
|
||||||
switch (sysio.lseek_in.whence) {
|
|
||||||
case Sysio::LSEEK_SET: _fh.seek(sysio.lseek_in.offset); break;
|
|
||||||
case Sysio::LSEEK_CUR: _fh.advance_seek(sysio.lseek_in.offset); break;
|
|
||||||
case Sysio::LSEEK_END:
|
|
||||||
off_t offset = sysio.lseek_in.offset;
|
|
||||||
sysio.fstat_in.fd = sysio.lseek_in.fd;
|
|
||||||
_fh.seek(size(sysio) + offset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sysio.lseek_out.offset = _fh.seek();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
|
||||||
{
|
|
||||||
return _fh.fs().check_unblock(&_fh, rd, wr, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool path(char *path, size_t len) override
|
|
||||||
{
|
|
||||||
strncpy(path, _path.base(), len);
|
|
||||||
path[len - 1] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__VFS_IO_CHANNEL_H_ */
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Utility for implementing blocking syscalls
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2011-11-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NOUX__WAKE_UP_NOTIFIER__H_
|
|
||||||
#define _NOUX__WAKE_UP_NOTIFIER__H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/list.h>
|
|
||||||
#include <base/lock.h>
|
|
||||||
|
|
||||||
namespace Noux { struct Wake_up_notifier; }
|
|
||||||
|
|
||||||
|
|
||||||
struct Noux::Wake_up_notifier : List<Wake_up_notifier>::Element
|
|
||||||
{
|
|
||||||
Lock *lock;
|
|
||||||
|
|
||||||
Wake_up_notifier(Lock *lock = nullptr) : lock(lock) { }
|
|
||||||
|
|
||||||
void wake_up() { if (lock) lock->unlock(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _NOUX__WAKE_UP_NOTIFIER__H_ */
|
|
|
@ -1,8 +0,0 @@
|
||||||
TARGET = test-libc_noux
|
|
||||||
LIBS = libc
|
|
||||||
SRC_CC = main.cc
|
|
||||||
|
|
||||||
# we re-use the libc_vfs test
|
|
||||||
vpath main.cc $(call select_from_repositories,src/test/libc_vfs)
|
|
||||||
|
|
||||||
CC_CXX_WARN_STRICT =
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Noux SIGINT handler test
|
|
||||||
* \author Christian Prochaska
|
|
||||||
* \date 2013-10-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013-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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
void signal_handler(int signal)
|
|
||||||
{
|
|
||||||
printf("%d: signal handler for signal %d called\n",
|
|
||||||
getpid(), signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
|
|
||||||
struct sigaction sa;
|
|
||||||
|
|
||||||
memset (&sa, '\0', sizeof(sa));
|
|
||||||
|
|
||||||
sa.sa_handler = signal_handler;
|
|
||||||
|
|
||||||
sigaction(SIGINT, &sa, 0);
|
|
||||||
|
|
||||||
int pid = fork();
|
|
||||||
|
|
||||||
if (pid == 0)
|
|
||||||
printf("test ready\n");
|
|
||||||
|
|
||||||
if ((read(0, &c, 1) == -1) && (errno = EINTR))
|
|
||||||
printf("%d: 'read()' returned with error EINTR\n", getpid());
|
|
||||||
else
|
|
||||||
printf("%d: 'read()' returned character 0x = %x\n", getpid(), c);
|
|
||||||
|
|
||||||
if (pid > 0) {
|
|
||||||
waitpid(pid, 0, 0);
|
|
||||||
printf("test finished\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
TARGET = test-noux_signals
|
|
||||||
SRC_CC = main.cc
|
|
||||||
LIBS = posix
|
|
||||||
|
|
||||||
CC_CXX_WARN_STRICT =
|
|
Loading…
Reference in New Issue
Block a user