Remove noux runtime

The feature set of noux is fully covered by the regular C runtime now.

Fixes #3696
This commit is contained in:
Norman Feske 2020-03-26 16:37:36 +01:00 committed by Christian Helmuth
parent d6bdeed38f
commit 4dd110ce5e
56 changed files with 1 additions and 9956 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
MIRRORED_FROM_REP_DIR := include/noux_session
include $(GENODE_DIR)/repos/os/recipes/api/session.inc

View File

@ -1 +0,0 @@
2020-03-25 5b839717ad8acc5742ac2e7f78f2c50d3d77b1ca

View File

@ -1,5 +1,4 @@
_/raw/system_shell
_/src/noux
_/src/bash-minimal
_/src/vim-minimal
_/src/coreutils-minimal

View File

@ -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 $@

View File

@ -1 +0,0 @@
2020-03-25 d13b8d86250ac0f22c9ea71fc4cfce2759aeba18

View File

@ -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

View File

@ -1,4 +0,0 @@
TARGET = libc_noux
LIBS = posix libc_noux
CC_CXX_WARN_STRICT =

View File

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

View File

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

View File

@ -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(&notifier);
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(&notifier);
}
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_ */

View File

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

View File

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

View File

@ -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 &registry)
:
_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_ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &registry)
:
_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_ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -1,6 +0,0 @@
TARGET = noux
LIBS = base vfs
SRC_CC = main.cc syscall.cc
INC_DIR += $(PRG_DIR)
vpath %.cc $(PRG_DIR)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 =

View File

@ -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;
}

View File

@ -1,5 +0,0 @@
TARGET = test-noux_signals
SRC_CC = main.cc
LIBS = posix
CC_CXX_WARN_STRICT =