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