parent
76db3b9c06
commit
9a3185f8ed
|
@ -73,8 +73,6 @@ namespace Genode {
|
||||||
|
|
||||||
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
|
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
|
||||||
typedef Fiasco::l4_threadid_t Native_connection_state;
|
typedef Fiasco::l4_threadid_t Native_connection_state;
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
||||||
|
|
|
@ -193,8 +193,6 @@ namespace Genode {
|
||||||
|
|
||||||
|
|
||||||
typedef int Native_connection_state;
|
typedef int Native_connection_state;
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
||||||
|
|
|
@ -40,8 +40,6 @@ namespace Genode
|
||||||
*/
|
*/
|
||||||
struct Native_region;
|
struct Native_region;
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the the minimal supported page-size log 2
|
* Get the the minimal supported page-size log 2
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct Genode::Pd_connection : Connection<Pd_session>, Pd_session_client
|
||||||
*
|
*
|
||||||
* \param label session label
|
* \param label session label
|
||||||
*/
|
*/
|
||||||
Pd_connection(char const *label = "", Native_pd_args const *pd_args = 0)
|
Pd_connection(char const *label = "")
|
||||||
: Connection<Pd_session>(session("ram_quota=%u, label=\"%s\"",
|
: Connection<Pd_session>(session("ram_quota=%u, label=\"%s\"",
|
||||||
RAM_QUOTA, label)),
|
RAM_QUOTA, label)),
|
||||||
Pd_session_client(cap()) { }
|
Pd_session_client(cap()) { }
|
||||||
|
|
|
@ -114,35 +114,6 @@ namespace Genode {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { PARENT_SOCKET_HANDLE = 100 };
|
enum { PARENT_SOCKET_HANDLE = 100 };
|
||||||
|
|
||||||
class Native_pd_args
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum { ROOT_PATH_MAX_LEN = 256 };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
char _root[ROOT_PATH_MAX_LEN];
|
|
||||||
|
|
||||||
unsigned _uid;
|
|
||||||
unsigned _gid;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Native_pd_args() : _uid(0), _gid(0) { _root[0] = 0; }
|
|
||||||
|
|
||||||
Native_pd_args(char const *root, unsigned uid, unsigned gid)
|
|
||||||
:
|
|
||||||
_uid(uid), _gid(gid)
|
|
||||||
{
|
|
||||||
Genode::strncpy(_root, root, sizeof(_root));
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *root() const { return _root; }
|
|
||||||
unsigned uid() const { return _uid; }
|
|
||||||
unsigned gid() const { return _gid; }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Connection to PD service
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-11-21
|
|
||||||
*
|
|
||||||
* In contrast to the generic version of 'pd_session/connection.h', the
|
|
||||||
* Linux-specific version supplies additional argument to core's PD service:
|
|
||||||
*
|
|
||||||
* :'root': is the path of a chroot environment of the process
|
|
||||||
* :'uid': is the user ID of the process
|
|
||||||
* :'gid': is the designated group ID of the process
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-2013 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 _INCLUDE__PD_SESSION__CONNECTION_H_
|
|
||||||
#define _INCLUDE__PD_SESSION__CONNECTION_H_
|
|
||||||
|
|
||||||
#include <pd_session/client.h>
|
|
||||||
#include <base/connection.h>
|
|
||||||
|
|
||||||
namespace Genode {
|
|
||||||
|
|
||||||
class Pd_connection : public Connection<Pd_session>, public Pd_session_client
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
template <Genode::size_t STRING_MAX_LEN>
|
|
||||||
struct Arg
|
|
||||||
{
|
|
||||||
char string[STRING_MAX_LEN];
|
|
||||||
|
|
||||||
Arg() { string[0] = 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert root path argument to session-construction parameter
|
|
||||||
*/
|
|
||||||
struct Root_arg : Arg<Native_pd_args::ROOT_PATH_MAX_LEN>
|
|
||||||
{
|
|
||||||
Root_arg(Native_pd_args const *args)
|
|
||||||
{
|
|
||||||
if (args && args->root() && args->root()[0])
|
|
||||||
Genode::snprintf(string, sizeof(string), ", root=\"%s\"",
|
|
||||||
args->root());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert UID argument to session-construction parameter
|
|
||||||
*/
|
|
||||||
struct Uid_arg : Arg<64>
|
|
||||||
{
|
|
||||||
Uid_arg(Native_pd_args const *args)
|
|
||||||
{
|
|
||||||
if (args && args->uid())
|
|
||||||
Genode::snprintf(string, sizeof(string), ", uid=%u",
|
|
||||||
args->uid());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert GID argument to session-construction parameter
|
|
||||||
*/
|
|
||||||
struct Gid_arg : Arg<64>
|
|
||||||
{
|
|
||||||
Gid_arg(Native_pd_args const *args)
|
|
||||||
{
|
|
||||||
if (args && args->gid())
|
|
||||||
Genode::snprintf(string, sizeof(string), ", gid=%u",
|
|
||||||
args->gid());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum { RAM_QUOTA = 4*1024 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param label session label
|
|
||||||
* \param pd_args Linux-specific PD-session arguments
|
|
||||||
*/
|
|
||||||
Pd_connection(char const *label = "", Native_pd_args const *pd_args = 0)
|
|
||||||
:
|
|
||||||
Connection<Pd_session>(
|
|
||||||
session("ram_quota=36K, label=\"%s\"%s%s%s", label,
|
|
||||||
Root_arg(pd_args).string,
|
|
||||||
Uid_arg(pd_args).string,
|
|
||||||
Gid_arg(pd_args).string)),
|
|
||||||
Pd_session_client(cap())
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__PD_SESSION__CONNECTION_H_ */
|
|
|
@ -1,59 +0,0 @@
|
||||||
#
|
|
||||||
# \brief Test for supplying Linux-specific PD-session arguments to core
|
|
||||||
# \author Norman Feske
|
|
||||||
# \date 2012-11-21
|
|
||||||
#
|
|
||||||
|
|
||||||
build "core init test/printf"
|
|
||||||
|
|
||||||
assert_spec linux
|
|
||||||
|
|
||||||
create_boot_directory
|
|
||||||
|
|
||||||
install_config {
|
|
||||||
<config>
|
|
||||||
<parent-provides>
|
|
||||||
<service name="LOG"/>
|
|
||||||
<service name="RAM"/>
|
|
||||||
<service name="CAP"/>
|
|
||||||
<service name="PD"/>
|
|
||||||
<service name="RM"/>
|
|
||||||
<service name="CPU"/>
|
|
||||||
<service name="ROM"/>
|
|
||||||
</parent-provides>
|
|
||||||
<default-route>
|
|
||||||
<any-service> <parent/> </any-service>
|
|
||||||
</default-route>
|
|
||||||
<start name="init" uid="11">
|
|
||||||
<resource name="RAM" quantum="10M"/>
|
|
||||||
<config>
|
|
||||||
<parent-provides>
|
|
||||||
<service name="LOG"/>
|
|
||||||
</parent-provides>
|
|
||||||
<start name="test-printf" root="/tmp/somewhere" uid="33" gid="44">
|
|
||||||
<resource name="RAM" quantum="10M"/>
|
|
||||||
</start>
|
|
||||||
</config>
|
|
||||||
</start>
|
|
||||||
</config>
|
|
||||||
}
|
|
||||||
|
|
||||||
build_boot_image "core init test-printf"
|
|
||||||
|
|
||||||
#
|
|
||||||
# The chroot to '/tmp/somewhere' is expected to fail. We just want to validate
|
|
||||||
# that the PD-session arguments are properly propagated into core. Hence, we
|
|
||||||
# can take the error message as exit condition of the test run.
|
|
||||||
#
|
|
||||||
run_genode_until {chroot path does not point to valid directory} 10
|
|
||||||
|
|
||||||
# keep only the lines containing the relevant core output
|
|
||||||
grep_output {^ .*:}
|
|
||||||
|
|
||||||
compare_output_to {
|
|
||||||
uid: 11
|
|
||||||
root: /tmp/somewhere
|
|
||||||
uid: 11
|
|
||||||
gid: 44
|
|
||||||
}
|
|
||||||
|
|
|
@ -123,41 +123,6 @@ inline int lx_pollpid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
** Chroot handling **
|
|
||||||
*********************/
|
|
||||||
|
|
||||||
inline int lx_chroot(char const *path)
|
|
||||||
{
|
|
||||||
return lx_syscall(SYS_chroot, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline int lx_chdir(char const *path)
|
|
||||||
{
|
|
||||||
return lx_syscall(SYS_chdir, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline int lx_getcwd(char *dst, size_t dst_len)
|
|
||||||
{
|
|
||||||
return lx_syscall(SYS_getcwd, dst, dst_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline int lx_bindmount(char const *source, char const *target)
|
|
||||||
{
|
|
||||||
enum { MS_BIND = 4096 };
|
|
||||||
return lx_syscall(SYS_mount, source, target, 0, MS_BIND, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline int lx_umount(char const *target)
|
|
||||||
{
|
|
||||||
return lx_syscall(SYS_umount2, target, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/********************************************
|
/********************************************
|
||||||
** Communication over Unix-domain sockets **
|
** Communication over Unix-domain sockets **
|
||||||
********************************************/
|
********************************************/
|
||||||
|
|
|
@ -26,166 +26,6 @@
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
/***********************************
|
|
||||||
** Utilities for chroot handling **
|
|
||||||
***********************************/
|
|
||||||
|
|
||||||
enum { MAX_PATH_LEN = 256 };
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if specified path is an existing directory
|
|
||||||
*/
|
|
||||||
static bool is_directory(char const *path)
|
|
||||||
{
|
|
||||||
struct stat64 s;
|
|
||||||
if (lx_stat(path, &s) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!(s.st_mode & S_IFDIR))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool is_path_delimiter(char c) { return c == '/'; }
|
|
||||||
|
|
||||||
|
|
||||||
static bool has_trailing_path_delimiter(char const *path)
|
|
||||||
{
|
|
||||||
char last_char = 0;
|
|
||||||
for (; *path; path++)
|
|
||||||
last_char = *path;
|
|
||||||
|
|
||||||
return is_path_delimiter(last_char);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return number of path elements of given path
|
|
||||||
*/
|
|
||||||
static Genode::size_t num_path_elements(char const *path)
|
|
||||||
{
|
|
||||||
Genode::size_t count = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If path starts with non-slash, the first characters belongs to a path
|
|
||||||
* element.
|
|
||||||
*/
|
|
||||||
if (*path && !is_path_delimiter(*path))
|
|
||||||
count = 1;
|
|
||||||
|
|
||||||
/* count slashes */
|
|
||||||
for (; *path; path++)
|
|
||||||
if (is_path_delimiter(*path))
|
|
||||||
count++;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool leading_path_elements(char const *path, unsigned num,
|
|
||||||
char *dst, Genode::size_t dst_len)
|
|
||||||
{
|
|
||||||
/* counter of path delimiters */
|
|
||||||
unsigned count = 0;
|
|
||||||
unsigned i = 0;
|
|
||||||
|
|
||||||
if (is_path_delimiter(path[0]))
|
|
||||||
num++;
|
|
||||||
|
|
||||||
for (; path[i] && (count < num) && (i < dst_len); i++)
|
|
||||||
{
|
|
||||||
if (is_path_delimiter(path[i]))
|
|
||||||
count++;
|
|
||||||
|
|
||||||
if (count == num)
|
|
||||||
break;
|
|
||||||
|
|
||||||
dst[i] = path[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i + 1 < dst_len) {
|
|
||||||
dst[i] = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* string is cut, append null termination anyway */
|
|
||||||
dst[dst_len - 1] = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void mirror_path_to_chroot(char const *chroot_path, char const *path)
|
|
||||||
{
|
|
||||||
char target_path[MAX_PATH_LEN];
|
|
||||||
Genode::snprintf(target_path, sizeof(target_path), "%s%s",
|
|
||||||
chroot_path, path);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create directory hierarchy pointing to the target path except for the
|
|
||||||
* last element. The last element will be bind-mounted to refer to the
|
|
||||||
* original 'path'.
|
|
||||||
*/
|
|
||||||
for (unsigned i = 1; i <= num_path_elements(target_path); i++)
|
|
||||||
{
|
|
||||||
char buf[MAX_PATH_LEN];
|
|
||||||
leading_path_elements(target_path, i, buf, sizeof(buf));
|
|
||||||
|
|
||||||
/* skip existing directories */
|
|
||||||
if (is_directory(buf))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* create new directory */
|
|
||||||
lx_mkdir(buf, 0777);
|
|
||||||
}
|
|
||||||
|
|
||||||
lx_umount(target_path);
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
if ((ret = lx_bindmount(path, target_path)))
|
|
||||||
PERR("bind mount failed (errno=%d)", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup content of chroot environment as prerequisite to 'execve' new
|
|
||||||
* processes within the environment. I.e., the current working directory
|
|
||||||
* containing the ROM modules must be mounted at the same location within the
|
|
||||||
* chroot environment.
|
|
||||||
*/
|
|
||||||
static bool setup_chroot_environment(char const *chroot_path)
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
static char cwd_path[MAX_PATH_LEN];
|
|
||||||
|
|
||||||
lx_getcwd(cwd_path, sizeof(cwd_path));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Validate chroot path
|
|
||||||
*/
|
|
||||||
if (!is_directory(chroot_path)) {
|
|
||||||
PERR("chroot path does not point to valid directory");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_trailing_path_delimiter(chroot_path)) {
|
|
||||||
PERR("chroot path has trailing slash");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hardlink directories needed for running Genode within the chroot
|
|
||||||
* environment.
|
|
||||||
*/
|
|
||||||
mirror_path_to_chroot(chroot_path, cwd_path);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************
|
/***************
|
||||||
** Utilities **
|
** Utilities **
|
||||||
***************/
|
***************/
|
||||||
|
@ -196,23 +36,16 @@ static bool setup_chroot_environment(char const *chroot_path)
|
||||||
struct Execve_args
|
struct Execve_args
|
||||||
{
|
{
|
||||||
char const *filename;
|
char const *filename;
|
||||||
char const *root;
|
|
||||||
char * const *argv;
|
char * const *argv;
|
||||||
char * const *envp;
|
char * const *envp;
|
||||||
unsigned int const uid;
|
|
||||||
unsigned int const gid;
|
|
||||||
int const parent_sd;
|
int const parent_sd;
|
||||||
|
|
||||||
Execve_args(char const *filename,
|
Execve_args(char const *filename,
|
||||||
char const *root,
|
|
||||||
char * const *argv,
|
char * const *argv,
|
||||||
char * const *envp,
|
char * const *envp,
|
||||||
unsigned int uid,
|
|
||||||
unsigned int gid,
|
|
||||||
int parent_sd)
|
int parent_sd)
|
||||||
:
|
:
|
||||||
filename(filename), root(root), argv(argv), envp(envp),
|
filename(filename), argv(argv), envp(envp), parent_sd(parent_sd)
|
||||||
uid(uid), gid(gid), parent_sd(parent_sd)
|
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -224,57 +57,6 @@ static int _exec_child(Execve_args *arg)
|
||||||
{
|
{
|
||||||
lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE);
|
lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE);
|
||||||
|
|
||||||
/* change to chroot environment */
|
|
||||||
if (arg->root && arg->root[0]) {
|
|
||||||
char cwd[1024];
|
|
||||||
|
|
||||||
PDBG("arg->root='%s'", arg->root);
|
|
||||||
|
|
||||||
if (setup_chroot_environment(arg->root) == false) {
|
|
||||||
PERR("Could not setup chroot environment");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lx_getcwd(cwd, sizeof(cwd))) {
|
|
||||||
PERR("Failed to getcwd");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PLOG("changing root of %s (PID %d) to %s",
|
|
||||||
arg->filename, lx_getpid(), arg->root);
|
|
||||||
|
|
||||||
int ret = lx_chroot(arg->root);
|
|
||||||
if (ret < 0) {
|
|
||||||
PERR("Syscall chroot failed (errno %d)", ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = lx_chdir(cwd);
|
|
||||||
if (ret < 0) {
|
|
||||||
PERR("chdir to new chroot failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set UID and GID
|
|
||||||
*
|
|
||||||
* We must set the GID prior setting the UID because setting the GID won't
|
|
||||||
* be possible anymore once we set the UID to non-root.
|
|
||||||
*/
|
|
||||||
if (arg->gid) {
|
|
||||||
int const ret = lx_setgid(arg->gid);
|
|
||||||
if (ret)
|
|
||||||
PWRN("Could not set PID %d (%s) to GID %u (error %d)",
|
|
||||||
lx_getpid(), arg->filename, arg->gid, ret);
|
|
||||||
}
|
|
||||||
if (arg->uid) {
|
|
||||||
int const ret = lx_setuid(arg->uid);
|
|
||||||
if (ret)
|
|
||||||
PWRN("Could not set PID %d (%s) to UID %u (error %d)",
|
|
||||||
lx_getpid(), arg->filename, arg->uid, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lx_execve(arg->filename, arg->argv, arg->envp);
|
return lx_execve(arg->filename, arg->argv, arg->envp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +163,7 @@ void Native_pd_component::_start(Dataspace_component &ds)
|
||||||
* Argument frame as passed to 'clone'. Because, we can only pass a single
|
* Argument frame as passed to 'clone'. Because, we can only pass a single
|
||||||
* pointer, all arguments are embedded within the 'execve_args' struct.
|
* pointer, all arguments are embedded within the 'execve_args' struct.
|
||||||
*/
|
*/
|
||||||
Execve_args arg(filename, _root, argv_buf, env, _uid, _gid,
|
Execve_args arg(filename, argv_buf, env,
|
||||||
_pd_session._parent.dst().socket);
|
_pd_session._parent.dst().socket);
|
||||||
|
|
||||||
_pid = lx_create_process((int (*)(void *))_exec_child,
|
_pid = lx_create_process((int (*)(void *))_exec_child,
|
||||||
|
@ -398,37 +180,6 @@ Native_pd_component::Native_pd_component(Pd_session_component &pd_session,
|
||||||
_pd_session(pd_session)
|
_pd_session(pd_session)
|
||||||
{
|
{
|
||||||
_pd_session._thread_ep.manage(this);
|
_pd_session._thread_ep.manage(this);
|
||||||
|
|
||||||
/*
|
|
||||||
* Read Linux-specific session arguments
|
|
||||||
*/
|
|
||||||
Arg_string::find_arg(args, "root").string(_root, sizeof(_root), "");
|
|
||||||
|
|
||||||
_uid = Arg_string::find_arg(args, "uid").ulong_value(0);
|
|
||||||
_gid = Arg_string::find_arg(args, "gid").ulong_value(0);
|
|
||||||
|
|
||||||
bool const is_chroot = (Genode::strcmp(_root, "") != 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a UID is specified but no GID, we use the UID as GID. This way, a
|
|
||||||
* configuration error where the UID is defined but the GID is left
|
|
||||||
* undefined won't result in the execution of the new process with the
|
|
||||||
* root user's GID.
|
|
||||||
*/
|
|
||||||
if (_gid == 0)
|
|
||||||
_gid = _uid;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Print Linux-specific session arguments if specified
|
|
||||||
*
|
|
||||||
* This output used for the automated 'lx_pd_args' test.
|
|
||||||
*/
|
|
||||||
if (is_chroot || _uid || _gid)
|
|
||||||
printf("PD session for '%s'\n", _pd_session._label.string);
|
|
||||||
|
|
||||||
if (is_chroot) printf(" root: %s\n", _root);
|
|
||||||
if (_uid) printf(" uid: %u\n", _uid);
|
|
||||||
if (_gid) printf(" gid: %u\n", _gid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -237,8 +237,6 @@ namespace Genode {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int Native_connection_state;
|
typedef int Native_connection_state;
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
||||||
|
|
|
@ -89,8 +89,6 @@ namespace Genode {
|
||||||
|
|
||||||
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
|
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
|
||||||
typedef Okl4::L4_ThreadId_t Native_connection_state;
|
typedef Okl4::L4_ThreadId_t Native_connection_state;
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
||||||
|
|
|
@ -73,8 +73,6 @@ namespace Genode {
|
||||||
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
|
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
|
||||||
|
|
||||||
typedef Pistachio::L4_ThreadId_t Native_connection_state;
|
typedef Pistachio::L4_ThreadId_t Native_connection_state;
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
|
||||||
|
|
|
@ -126,8 +126,6 @@ namespace Genode {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Native_pd_args { };
|
|
||||||
|
|
||||||
typedef int Native_connection_state;
|
typedef int Native_connection_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct Genode::Pd_connection : Connection<Pd_session>, Pd_session_client
|
||||||
*
|
*
|
||||||
* \param label session label
|
* \param label session label
|
||||||
*/
|
*/
|
||||||
Pd_connection(char const *label = "", Native_pd_args const *pd_args = 0)
|
Pd_connection(char const *label = "")
|
||||||
: Connection<Pd_session>(session("ram_quota=%u, label=\"%s\"",
|
: Connection<Pd_session>(session("ram_quota=%u, label=\"%s\"",
|
||||||
RAM_QUOTA, label)),
|
RAM_QUOTA, label)),
|
||||||
Pd_session_client(cap())
|
Pd_session_client(cap())
|
||||||
|
|
|
@ -111,14 +111,6 @@ struct Genode::Child_policy
|
||||||
virtual Ram_session *ref_ram_session() { return env()->ram_session(); }
|
virtual Ram_session *ref_ram_session() { return env()->ram_session(); }
|
||||||
virtual Ram_session_capability ref_ram_cap() const { return env()->ram_session_cap(); }
|
virtual Ram_session_capability ref_ram_cap() const { return env()->ram_session_cap(); }
|
||||||
|
|
||||||
/**
|
|
||||||
* Return platform-specific PD-session arguments
|
|
||||||
*
|
|
||||||
* This method is used on Linux to supply additional PD-session
|
|
||||||
* argument to core, i.e., the chroot path, the UID, and the GID.
|
|
||||||
*/
|
|
||||||
virtual Native_pd_args const *pd_args() const { return 0; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Respond to the release of resources by the child
|
* Respond to the release of resources by the child
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct Genode::Pd_connection : Connection<Pd_session>, Pd_session_client
|
||||||
*
|
*
|
||||||
* \param label session label
|
* \param label session label
|
||||||
*/
|
*/
|
||||||
Pd_connection(char const *label = "", Native_pd_args const *pd_args = 0)
|
Pd_connection(char const *label = "")
|
||||||
: Connection<Pd_session>(session("ram_quota=%u, label=\"%s\"",
|
: Connection<Pd_session>(session("ram_quota=%u, label=\"%s\"",
|
||||||
RAM_QUOTA, label)),
|
RAM_QUOTA, label)),
|
||||||
Pd_session_client(cap())
|
Pd_session_client(cap())
|
||||||
|
|
|
@ -320,29 +320,6 @@ optional sub node '<exit>' with the attribute 'propagate' set to "yes".
|
||||||
The exit value specified by the exiting child is forwarded to init's parent.
|
The exit value specified by the exiting child is forwarded to init's parent.
|
||||||
|
|
||||||
|
|
||||||
Executing children in chroot environments on Linux
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
On the Linux base platform, each process started by init can be assigned to
|
|
||||||
a chroot environment by specifying the new root location as 'root' attribute
|
|
||||||
to the corresponding '<start>' node. Root environments can be nested. The
|
|
||||||
root path of a nested init instance will be appended to the root path of
|
|
||||||
the outer instance.
|
|
||||||
|
|
||||||
When using the chroot mechanism, core will mirror the current working
|
|
||||||
directory within the chroot environment via the a bind mount operation. This
|
|
||||||
step is needed to enable execve to obtain the ELF binary of the new process.
|
|
||||||
|
|
||||||
In order to use the chroot mechanism when starting Genode's core as a non-root
|
|
||||||
user process, the core executable must be equipped with the 'CAP_SYS_ADMIN' and
|
|
||||||
'CAP_SYS_CHROOT' capabilities. 'CAP_SYS_ADMIN' is needed for bind mounting.
|
|
||||||
'CAP_SYS_CHROOT' is needed to perform the 'chroot' syscall:
|
|
||||||
|
|
||||||
! sudo setcap cap_sys_admin,cap_sys_chroot=ep core
|
|
||||||
|
|
||||||
For an example of using chroot, please refer to the 'os/run/chroot.run' script.
|
|
||||||
|
|
||||||
|
|
||||||
Using the configuration concept
|
Using the configuration concept
|
||||||
###############################
|
###############################
|
||||||
|
|
||||||
|
|
|
@ -414,14 +414,6 @@ class Init::Child : Genode::Child_policy
|
||||||
}
|
}
|
||||||
} _name;
|
} _name;
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform-specific PD-session arguments
|
|
||||||
*/
|
|
||||||
struct Pd_args : Genode::Native_pd_args
|
|
||||||
{
|
|
||||||
Pd_args(Genode::Xml_node start_node);
|
|
||||||
} _pd_args;
|
|
||||||
|
|
||||||
struct Read_quota
|
struct Read_quota
|
||||||
{
|
{
|
||||||
Read_quota(Genode::Xml_node start_node,
|
Read_quota(Genode::Xml_node start_node,
|
||||||
|
@ -482,15 +474,14 @@ class Init::Child : Genode::Child_policy
|
||||||
|
|
||||||
Resources(Genode::Xml_node start_node, const char *label,
|
Resources(Genode::Xml_node start_node, const char *label,
|
||||||
long prio_levels,
|
long prio_levels,
|
||||||
Genode::Affinity::Space const &affinity_space,
|
Genode::Affinity::Space const &affinity_space)
|
||||||
Genode::Native_pd_args const * pd_args)
|
|
||||||
:
|
:
|
||||||
Read_quota(start_node, ram_quota, cpu_quota_pc, constrain_phys),
|
Read_quota(start_node, ram_quota, cpu_quota_pc, constrain_phys),
|
||||||
prio_levels_log2(Genode::log2(prio_levels)),
|
prio_levels_log2(Genode::log2(prio_levels)),
|
||||||
priority(read_priority(start_node, prio_levels)),
|
priority(read_priority(start_node, prio_levels)),
|
||||||
affinity(affinity_space,
|
affinity(affinity_space,
|
||||||
read_affinity_location(affinity_space, start_node)),
|
read_affinity_location(affinity_space, start_node)),
|
||||||
pd(label, pd_args),
|
pd(label),
|
||||||
ram(label),
|
ram(label),
|
||||||
cpu(label,
|
cpu(label,
|
||||||
priority*(Genode::Cpu_session::PRIORITY_LIMIT >> prio_levels_log2),
|
priority*(Genode::Cpu_session::PRIORITY_LIMIT >> prio_levels_log2),
|
||||||
|
@ -549,7 +540,6 @@ class Init::Child : Genode::Child_policy
|
||||||
Init::Child_policy_provide_rom_file _config_policy;
|
Init::Child_policy_provide_rom_file _config_policy;
|
||||||
Init::Child_policy_provide_rom_file _binary_policy;
|
Init::Child_policy_provide_rom_file _binary_policy;
|
||||||
Init::Child_policy_redirect_rom_file _configfile_policy;
|
Init::Child_policy_redirect_rom_file _configfile_policy;
|
||||||
Init::Child_policy_pd_args _pd_args_policy;
|
|
||||||
Init::Child_policy_ram_phys _ram_session_policy;
|
Init::Child_policy_ram_phys _ram_session_policy;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -568,9 +558,8 @@ class Init::Child : Genode::Child_policy
|
||||||
_default_route_node(default_route_node),
|
_default_route_node(default_route_node),
|
||||||
_name_registry(name_registry),
|
_name_registry(name_registry),
|
||||||
_name(start_node, name_registry),
|
_name(start_node, name_registry),
|
||||||
_pd_args(start_node),
|
|
||||||
_resources(start_node, _name.unique, prio_levels,
|
_resources(start_node, _name.unique, prio_levels,
|
||||||
affinity_space, &_pd_args),
|
affinity_space),
|
||||||
_entrypoint(cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false, _resources.affinity.location()),
|
_entrypoint(cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false, _resources.affinity.location()),
|
||||||
_binary_rom(_name.file, _name.file),
|
_binary_rom(_name.file, _name.file),
|
||||||
_binary_rom_ds(_binary_rom.dataspace()),
|
_binary_rom_ds(_binary_rom.dataspace()),
|
||||||
|
@ -585,7 +574,6 @@ class Init::Child : Genode::Child_policy
|
||||||
_config_policy("config", _config.dataspace(), &_entrypoint),
|
_config_policy("config", _config.dataspace(), &_entrypoint),
|
||||||
_binary_policy("binary", _binary_rom_ds, &_entrypoint),
|
_binary_policy("binary", _binary_rom_ds, &_entrypoint),
|
||||||
_configfile_policy("config", _config.filename()),
|
_configfile_policy("config", _config.filename()),
|
||||||
_pd_args_policy(&_pd_args),
|
|
||||||
_ram_session_policy(_resources.constrain_phys)
|
_ram_session_policy(_resources.constrain_phys)
|
||||||
{
|
{
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
@ -743,7 +731,6 @@ class Init::Child : Genode::Child_policy
|
||||||
_labeling_policy. filter_session_args(service, args, args_len);
|
_labeling_policy. filter_session_args(service, args, args_len);
|
||||||
_priority_policy. filter_session_args(service, args, args_len);
|
_priority_policy. filter_session_args(service, args, args_len);
|
||||||
_configfile_policy.filter_session_args(service, args, args_len);
|
_configfile_policy.filter_session_args(service, args, args_len);
|
||||||
_pd_args_policy. filter_session_args(service, args, args_len);
|
|
||||||
_ram_session_policy.filter_session_args(service, args, args_len);
|
_ram_session_policy.filter_session_args(service, args, args_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,8 +825,6 @@ class Init::Child : Genode::Child_policy
|
||||||
*/
|
*/
|
||||||
Child_policy::exit(exit_value);
|
Child_policy::exit(exit_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Genode::Native_pd_args const *pd_args() const { return &_pd_args; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ namespace Init {
|
||||||
|
|
||||||
class Child_policy_ram_phys;
|
class Child_policy_ram_phys;
|
||||||
class Child_policy_enforce_labeling;
|
class Child_policy_enforce_labeling;
|
||||||
class Child_policy_pd_args;
|
|
||||||
class Child_policy_handle_cpu_priorities;
|
class Child_policy_handle_cpu_priorities;
|
||||||
class Child_policy_provide_rom_file;
|
class Child_policy_provide_rom_file;
|
||||||
class Child_policy_redirect_rom_file;
|
class Child_policy_redirect_rom_file;
|
||||||
|
@ -109,29 +108,6 @@ class Init::Child_policy_enforce_labeling
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Policy for handling platform-specific PD-session arguments
|
|
||||||
*
|
|
||||||
* This policy is used onthe Linux base platform for prepending the chroot
|
|
||||||
* path of the child. By applying this policy, the chroot path of the child
|
|
||||||
* gets supplied to PD session requests.
|
|
||||||
*/
|
|
||||||
class Init::Child_policy_pd_args
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Genode::Native_pd_args const *_pd_args;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Child_policy_pd_args(Genode::Native_pd_args const *pd_args)
|
|
||||||
: _pd_args(pd_args) { }
|
|
||||||
|
|
||||||
void filter_session_args(const char *session, char *args,
|
|
||||||
Genode::size_t args_len);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Init::Child_policy_handle_cpu_priorities
|
class Init::Child_policy_handle_cpu_priorities
|
||||||
{
|
{
|
||||||
/* priority parameters */
|
/* priority parameters */
|
||||||
|
|
|
@ -49,9 +49,8 @@ struct Loader::Session_client : Genode::Rpc_client<Session>
|
||||||
void fault_sigh(Signal_context_capability sigh) override {
|
void fault_sigh(Signal_context_capability sigh) override {
|
||||||
call<Rpc_fault_sigh>(sigh); }
|
call<Rpc_fault_sigh>(sigh); }
|
||||||
|
|
||||||
void start(Name const &binary, Name const &label = "",
|
void start(Name const &binary, Name const &label = "") override {
|
||||||
Native_pd_args const &pd_args = Native_pd_args()) override {
|
call<Rpc_start>(binary, label); }
|
||||||
call<Rpc_start>(binary, label, pd_args); }
|
|
||||||
|
|
||||||
void view_geometry(Rect rect, Point offset) override {
|
void view_geometry(Rect rect, Point offset) override {
|
||||||
call<Rpc_view_geometry>(rect, offset); }
|
call<Rpc_view_geometry>(rect, offset); }
|
||||||
|
|
|
@ -31,7 +31,6 @@ namespace Loader {
|
||||||
|
|
||||||
using Genode::Dataspace_capability;
|
using Genode::Dataspace_capability;
|
||||||
using Genode::Signal_context_capability;
|
using Genode::Signal_context_capability;
|
||||||
using Genode::Native_pd_args;
|
|
||||||
using Genode::Meta::Type_tuple;
|
using Genode::Meta::Type_tuple;
|
||||||
|
|
||||||
struct Session;
|
struct Session;
|
||||||
|
@ -144,8 +143,7 @@ struct Loader::Session : Genode::Session
|
||||||
* \throw Rom_module_does_not_exist if the specified binary could
|
* \throw Rom_module_does_not_exist if the specified binary could
|
||||||
* not obtained as ROM module
|
* not obtained as ROM module
|
||||||
*/
|
*/
|
||||||
virtual void start(Name const &binary, Name const &label = "",
|
virtual void start(Name const &binary, Name const &label = "") = 0;
|
||||||
Native_pd_args const &pd_args = Native_pd_args()) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set view geometry and buffer offset
|
* Set view geometry and buffer offset
|
||||||
|
@ -174,7 +172,7 @@ struct Loader::Session : Genode::Session
|
||||||
GENODE_RPC(Rpc_fault_sigh, void, fault_sigh, Signal_context_capability);
|
GENODE_RPC(Rpc_fault_sigh, void, fault_sigh, Signal_context_capability);
|
||||||
GENODE_RPC_THROW(Rpc_start, void, start,
|
GENODE_RPC_THROW(Rpc_start, void, start,
|
||||||
GENODE_TYPE_LIST(Rom_module_does_not_exist),
|
GENODE_TYPE_LIST(Rom_module_does_not_exist),
|
||||||
Name const &, Name const &, Native_pd_args const &);
|
Name const &, Name const &);
|
||||||
GENODE_RPC_THROW(Rpc_view_geometry, void, view_geometry,
|
GENODE_RPC_THROW(Rpc_view_geometry, void, view_geometry,
|
||||||
GENODE_TYPE_LIST(View_does_not_exist),
|
GENODE_TYPE_LIST(View_does_not_exist),
|
||||||
Rect, Point);
|
Rect, Point);
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
SRC_CC = pd_args.cc
|
|
||||||
|
|
||||||
vpath pd_args.cc $(REP_DIR)/src/init
|
|
|
@ -1,3 +0,0 @@
|
||||||
SRC_CC = pd_args.cc
|
|
||||||
|
|
||||||
vpath pd_args.cc $(REP_DIR)/src/init/spec/linux
|
|
|
@ -1,111 +0,0 @@
|
||||||
#
|
|
||||||
# \brief Test for using chroot on Linux
|
|
||||||
# \author Norman Feske
|
|
||||||
# \date 2012-04-18
|
|
||||||
#
|
|
||||||
#
|
|
||||||
if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 }
|
|
||||||
|
|
||||||
#
|
|
||||||
# Build
|
|
||||||
#
|
|
||||||
|
|
||||||
build { core init drivers/timer test/timer }
|
|
||||||
|
|
||||||
if {[catch { exec which setcap }]} {
|
|
||||||
puts stderr "Error: setcap not available, please install the libcap2-bin package"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Clean up remains of a potentially failed previous run
|
|
||||||
#
|
|
||||||
|
|
||||||
proc chroot_path { } { return "/tmp/chroot-test" }
|
|
||||||
proc chroot_cwd_path { } { return "[chroot_path][pwd]/[run_dir]" }
|
|
||||||
proc chroot_genode_tmp_path { } { return "[chroot_path]/tmp/genode-[exec id -u]" }
|
|
||||||
|
|
||||||
proc cleanup_chroot { } {
|
|
||||||
umount_and_rmdir [chroot_cwd_path]
|
|
||||||
umount_and_rmdir [chroot_path]/lib
|
|
||||||
umount_and_rmdir [chroot_path]/lib64
|
|
||||||
}
|
|
||||||
|
|
||||||
create_boot_directory
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generate config
|
|
||||||
#
|
|
||||||
|
|
||||||
set config {
|
|
||||||
<config>
|
|
||||||
<parent-provides>
|
|
||||||
<service name="ROM"/>
|
|
||||||
<service name="LOG"/>
|
|
||||||
<service name="CAP"/>
|
|
||||||
<service name="RAM"/>
|
|
||||||
<service name="CPU"/>
|
|
||||||
<service name="RM"/>
|
|
||||||
<service name="PD"/>
|
|
||||||
<service name="SIGNAL"/>
|
|
||||||
</parent-provides>
|
|
||||||
<default-route>
|
|
||||||
<any-service> <parent/> <any-child/> </any-service>
|
|
||||||
</default-route>
|
|
||||||
<start name="timer">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
<provides><service name="Timer"/></provides>
|
|
||||||
</start>
|
|
||||||
<start name="test-timer" root="chroot_path">
|
|
||||||
<resource name="RAM" quantum="1G"/>
|
|
||||||
</start>
|
|
||||||
</config>
|
|
||||||
}
|
|
||||||
|
|
||||||
# replace 'chroot_path' marker in config with actual path
|
|
||||||
regsub "chroot_path" $config [chroot_path] config
|
|
||||||
|
|
||||||
install_config $config
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copy boot modules into run directory
|
|
||||||
#
|
|
||||||
# We cannot use the predefined 'build_boot_image' function here because
|
|
||||||
# this would create mere symlinks. However, we want to hardlink the
|
|
||||||
# run directory into the chroot environment. If the directory entries
|
|
||||||
# were symlinks, those would point to nowhere within the chroot.
|
|
||||||
#
|
|
||||||
foreach binary { core init timer test-timer } {
|
|
||||||
exec cp -H bin/$binary [run_dir] }
|
|
||||||
|
|
||||||
#
|
|
||||||
# Grant chroot permission to core
|
|
||||||
#
|
|
||||||
# CAP_SYS_ADMIN is needed for bind mounting genode runtime directories
|
|
||||||
# CAP_SYS_CHROOT is needed to perform the chroot syscall
|
|
||||||
#
|
|
||||||
exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/core
|
|
||||||
|
|
||||||
#
|
|
||||||
# Setup chroot environment
|
|
||||||
#
|
|
||||||
|
|
||||||
# start with fresh directory
|
|
||||||
cleanup_chroot
|
|
||||||
exec mkdir -p [chroot_path]
|
|
||||||
exec mkdir -p [chroot_path]/lib
|
|
||||||
|
|
||||||
# bind mount '/lib' as we need ldso and libc within the chroot environment
|
|
||||||
exec sudo mount --bind /lib [chroot_path]/lib
|
|
||||||
|
|
||||||
#
|
|
||||||
# Execute test case
|
|
||||||
#
|
|
||||||
run_genode_until {.*--- timer test finished ---.*} 60
|
|
||||||
|
|
||||||
#
|
|
||||||
# Remove artifacts created while running the test
|
|
||||||
#
|
|
||||||
cleanup_chroot
|
|
||||||
|
|
||||||
# vi: set ft=tcl :
|
|
|
@ -1,145 +0,0 @@
|
||||||
#
|
|
||||||
# \brief Test for using chroot on Linux
|
|
||||||
# \author Norman Feske
|
|
||||||
# \date 2012-06-06
|
|
||||||
#
|
|
||||||
#
|
|
||||||
if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 }
|
|
||||||
|
|
||||||
#
|
|
||||||
# Build
|
|
||||||
#
|
|
||||||
|
|
||||||
build { core init drivers/timer test/timer
|
|
||||||
server/loader test/chroot_loader }
|
|
||||||
|
|
||||||
if {[catch { exec which setcap }]} {
|
|
||||||
puts stderr "Error: setcap not available, please install the libcap2-bin package"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Clean up remains of a potentially failed previous run
|
|
||||||
#
|
|
||||||
|
|
||||||
proc chroot_path { id } { return "/tmp/chroot-test-$id" }
|
|
||||||
proc chroot_cwd_path { id } { return "[chroot_path $id][pwd]/[run_dir]" }
|
|
||||||
|
|
||||||
proc cleanup_chroot { } {
|
|
||||||
|
|
||||||
foreach id { 1 2 } {
|
|
||||||
umount_and_rmdir [chroot_cwd_path $id]
|
|
||||||
umount_and_rmdir [chroot_path $id]/lib
|
|
||||||
umount_and_rmdir [chroot_path $id]/lib64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup_chroot
|
|
||||||
|
|
||||||
create_boot_directory
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generate config
|
|
||||||
#
|
|
||||||
|
|
||||||
set config {
|
|
||||||
<config>
|
|
||||||
<parent-provides>
|
|
||||||
<service name="ROM"/>
|
|
||||||
<service name="LOG"/>
|
|
||||||
<service name="CAP"/>
|
|
||||||
<service name="RAM"/>
|
|
||||||
<service name="CPU"/>
|
|
||||||
<service name="RM"/>
|
|
||||||
<service name="PD"/>
|
|
||||||
<service name="SIGNAL"/>
|
|
||||||
</parent-provides>
|
|
||||||
<default-route>
|
|
||||||
<any-service> <parent/> <any-child/> </any-service>
|
|
||||||
</default-route>
|
|
||||||
<start name="timer">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
<provides><service name="Timer"/></provides>
|
|
||||||
</start>
|
|
||||||
<start name="loader">
|
|
||||||
<resource name="RAM" quantum="1M"/>
|
|
||||||
<provides><service name="Loader"/></provides>
|
|
||||||
</start>
|
|
||||||
<start name="test-chroot_loader">
|
|
||||||
<resource name="RAM" quantum="32M"/>
|
|
||||||
<config>
|
|
||||||
<static_test chroot_path="chroot_path_1" />
|
|
||||||
<dynamic_test chroot_path="chroot_path_2" />
|
|
||||||
</config>
|
|
||||||
</start>
|
|
||||||
</config>
|
|
||||||
}
|
|
||||||
|
|
||||||
# replace 'chroot_path' markers in config with actual paths
|
|
||||||
foreach id { 1 2 } {
|
|
||||||
regsub "chroot_path_$id" $config [chroot_path $id] config }
|
|
||||||
|
|
||||||
install_config $config
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copy boot modules into run directory
|
|
||||||
#
|
|
||||||
# We cannot use the predefined 'build_boot_image' function here because
|
|
||||||
# this would create mere symlinks. However, we want to hardlink the
|
|
||||||
# run directory into the chroot environment. If the directory entries
|
|
||||||
# were symlinks, those would point to nowhere within the chroot.
|
|
||||||
#
|
|
||||||
foreach binary { core init timer loader test-chroot_loader test-timer} {
|
|
||||||
exec cp -H bin/$binary [run_dir] }
|
|
||||||
|
|
||||||
#
|
|
||||||
# Grant chroot permission to 'core'
|
|
||||||
#
|
|
||||||
# CAP_SYS_ADMIN is needed for bind mounting genode runtime directories
|
|
||||||
# CAP_SYS_CHROOT is needed to perform the chroot syscall
|
|
||||||
#
|
|
||||||
exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/core
|
|
||||||
|
|
||||||
#
|
|
||||||
# Setup chroot environment
|
|
||||||
#
|
|
||||||
|
|
||||||
# start with fresh directory
|
|
||||||
foreach id { 1 2 } {
|
|
||||||
exec mkdir -p [chroot_path $id]
|
|
||||||
exec mkdir -p [chroot_path $id]/lib
|
|
||||||
exec mkdir -p [chroot_path $id]/lib64
|
|
||||||
|
|
||||||
# bind mount '/lib' as need libc within the chroot environment
|
|
||||||
exec sudo mount --bind /lib [chroot_path $id]/lib
|
|
||||||
catch { exec sudo mount --bind /lib64 [chroot_path $id]/lib64 }
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Execute test case
|
|
||||||
#
|
|
||||||
run_genode_until {.*--- chroot-loader test finished ---\s*\n} 60
|
|
||||||
|
|
||||||
#
|
|
||||||
# Validate log output
|
|
||||||
#
|
|
||||||
|
|
||||||
if {[regexp -all -- {--- timer test ---} $output] != 6} {
|
|
||||||
puts stderr "Number of spawned subsystems differs from 6"
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
if {![regexp -- {init-1 -> test-timer] Done 500 ms period 4 times} $output]} {
|
|
||||||
puts stderr "Long-running timer test has made too little progress"
|
|
||||||
exit 3
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Remove artifacts created while running the test
|
|
||||||
#
|
|
||||||
cleanup_chroot
|
|
||||||
|
|
||||||
puts "Test succeeded"
|
|
||||||
|
|
||||||
# vi: set ft=tcl :
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Extract 'Native_pd_args' from '<start>' node of the init config
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-11.21
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* init includes */
|
|
||||||
#include <init/child.h>
|
|
||||||
|
|
||||||
|
|
||||||
Init::Child::Pd_args::Pd_args(Genode::Xml_node start_node) { }
|
|
||||||
|
|
||||||
|
|
||||||
void Init::Child_policy_pd_args::filter_session_args(char const *,
|
|
||||||
char *, Genode::size_t)
|
|
||||||
{ }
|
|
|
@ -1,99 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Extract 'Native_pd_args' from '<start>' node of the init config
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-11.21
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* init includes */
|
|
||||||
#include <init/child.h>
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read chroot path from XML node
|
|
||||||
*/
|
|
||||||
struct Root
|
|
||||||
{
|
|
||||||
char path[Genode::Native_pd_args::ROOT_PATH_MAX_LEN];
|
|
||||||
|
|
||||||
Root(Genode::Xml_node node)
|
|
||||||
{
|
|
||||||
path[0] = 0;
|
|
||||||
try { node.attribute("root").value(path, sizeof(path)); }
|
|
||||||
catch (Genode::Xml_node::Nonexistent_attribute) { }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read unsigned ID from XML node
|
|
||||||
*/
|
|
||||||
static unsigned id_value(char const *attr, Genode::Xml_node node)
|
|
||||||
{
|
|
||||||
unsigned value = 0;
|
|
||||||
try { node.attribute(attr).value(&value); }
|
|
||||||
catch (Genode::Xml_node::Nonexistent_attribute) { }
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Init::Child::Pd_args::Pd_args(Genode::Xml_node start_node)
|
|
||||||
:
|
|
||||||
Genode::Native_pd_args(Root(start_node).path,
|
|
||||||
id_value("uid", start_node),
|
|
||||||
id_value("gid", start_node))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
|
|
||||||
void Init::Child_policy_pd_args::filter_session_args(char const *session,
|
|
||||||
char *args,
|
|
||||||
Genode::size_t args_len)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Specify 'Genode' namespace to remove possible ambiguity of
|
|
||||||
* 'strcmp' when including the header along with libc headers.
|
|
||||||
*/
|
|
||||||
if (Genode::strcmp(session, "PD") != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Apply platform-specific PD-session arguments only if specified.
|
|
||||||
*/
|
|
||||||
if (!_pd_args)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prepend the '_root' to the 'root' session argument of PD sessions
|
|
||||||
* initiated through the child (not the child's PD session).
|
|
||||||
*/
|
|
||||||
if (_pd_args->root() && _pd_args->root()[0]) {
|
|
||||||
|
|
||||||
char path[Parent::Session_args::MAX_SIZE];
|
|
||||||
Arg_string::find_arg(args, "root").string(path, sizeof(path), "");
|
|
||||||
|
|
||||||
char value[Parent::Session_args::MAX_SIZE];
|
|
||||||
Genode::snprintf(value, sizeof(value),
|
|
||||||
"\"%s%s\"",
|
|
||||||
_pd_args->root(), path);
|
|
||||||
|
|
||||||
Arg_string::set_arg(args, args_len, "root", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add user ID and group ID to session arguments
|
|
||||||
*/
|
|
||||||
if (_pd_args->uid())
|
|
||||||
Arg_string::set_arg(args, args_len, "uid", _pd_args->uid());
|
|
||||||
|
|
||||||
if (_pd_args->gid())
|
|
||||||
Arg_string::set_arg(args, args_len, "gid", _pd_args->gid());
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
TARGET = init
|
TARGET = init
|
||||||
SRC_CC = main.cc
|
SRC_CC = main.cc
|
||||||
LIBS = base init_pd_args config
|
LIBS = base config
|
||||||
|
|
|
@ -38,8 +38,6 @@ namespace Loader {
|
||||||
Label(char const *l) { strncpy(string, l, sizeof(string)); }
|
Label(char const *l) { strncpy(string, l, sizeof(string)); }
|
||||||
} _label;
|
} _label;
|
||||||
|
|
||||||
Native_pd_args _pd_args;
|
|
||||||
|
|
||||||
Rpc_entrypoint &_ep;
|
Rpc_entrypoint &_ep;
|
||||||
|
|
||||||
struct Resources
|
struct Resources
|
||||||
|
@ -87,7 +85,6 @@ namespace Loader {
|
||||||
|
|
||||||
Init::Child_policy_provide_rom_file _binary_policy;
|
Init::Child_policy_provide_rom_file _binary_policy;
|
||||||
Init::Child_policy_enforce_labeling _labeling_policy;
|
Init::Child_policy_enforce_labeling _labeling_policy;
|
||||||
Init::Child_policy_pd_args _pd_args_policy;
|
|
||||||
|
|
||||||
Genode::Child _child;
|
Genode::Child _child;
|
||||||
|
|
||||||
|
@ -107,7 +104,6 @@ namespace Loader {
|
||||||
|
|
||||||
Child(char const *binary_name,
|
Child(char const *binary_name,
|
||||||
char const *label,
|
char const *label,
|
||||||
Native_pd_args const &pd_args,
|
|
||||||
Rpc_entrypoint &ep,
|
Rpc_entrypoint &ep,
|
||||||
Ram_session_client &ram_session_client,
|
Ram_session_client &ram_session_client,
|
||||||
size_t ram_quota,
|
size_t ram_quota,
|
||||||
|
@ -119,7 +115,6 @@ namespace Loader {
|
||||||
Signal_context_capability fault_sigh)
|
Signal_context_capability fault_sigh)
|
||||||
:
|
:
|
||||||
_label(label),
|
_label(label),
|
||||||
_pd_args(pd_args),
|
|
||||||
_ep(ep),
|
_ep(ep),
|
||||||
_resources(_label.string, ram_session_client, ram_quota, fault_sigh),
|
_resources(_label.string, ram_session_client, ram_quota, fault_sigh),
|
||||||
_parent_services(parent_services),
|
_parent_services(parent_services),
|
||||||
|
@ -130,7 +125,6 @@ namespace Loader {
|
||||||
_binary_rom_session(_rom_session(binary_name)),
|
_binary_rom_session(_rom_session(binary_name)),
|
||||||
_binary_policy("binary", _binary_rom_session.dataspace(), &_ep),
|
_binary_policy("binary", _binary_rom_session.dataspace(), &_ep),
|
||||||
_labeling_policy(_label.string),
|
_labeling_policy(_label.string),
|
||||||
_pd_args_policy(&_pd_args),
|
|
||||||
_child(_binary_rom_session.dataspace(), _resources.pd.cap(),
|
_child(_binary_rom_session.dataspace(), _resources.pd.cap(),
|
||||||
_resources.ram.cap(), _resources.cpu.cap(),
|
_resources.ram.cap(), _resources.cpu.cap(),
|
||||||
_resources.rm.cap(), &_ep, this)
|
_resources.rm.cap(), &_ep, this)
|
||||||
|
@ -146,13 +140,11 @@ namespace Loader {
|
||||||
** Child-policy interface **
|
** Child-policy interface **
|
||||||
****************************/
|
****************************/
|
||||||
|
|
||||||
char const *name() const { return _label.string; }
|
char const *name() const { return _label.string; }
|
||||||
Native_pd_args const *pd_args() const { return &_pd_args; }
|
|
||||||
|
|
||||||
void filter_session_args(char const *service, char *args, size_t args_len)
|
void filter_session_args(char const *service, char *args, size_t args_len)
|
||||||
{
|
{
|
||||||
_labeling_policy.filter_session_args(service, args, args_len);
|
_labeling_policy.filter_session_args(service, args, args_len);
|
||||||
_pd_args_policy. filter_session_args(service, args, args_len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Service *resolve_session_request(const char *name,
|
Service *resolve_session_request(const char *name,
|
||||||
|
|
|
@ -363,8 +363,7 @@ class Loader::Session_component : public Rpc_object<Session>
|
||||||
_fault_sigh = sigh;
|
_fault_sigh = sigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start(Name const &binary_name, Name const &label,
|
void start(Name const &binary_name, Name const &label) override
|
||||||
Genode::Native_pd_args const &pd_args) override
|
|
||||||
{
|
{
|
||||||
if (_child) {
|
if (_child) {
|
||||||
PWRN("cannot start subsystem twice");
|
PWRN("cannot start subsystem twice");
|
||||||
|
@ -378,7 +377,7 @@ class Loader::Session_component : public Rpc_object<Session>
|
||||||
try {
|
try {
|
||||||
_child = new (&_md_alloc)
|
_child = new (&_md_alloc)
|
||||||
Child(binary_name.string(), label.string(),
|
Child(binary_name.string(), label.string(),
|
||||||
pd_args, _ep, _ram_session_client,
|
_ep, _ram_session_client,
|
||||||
ram_quota, _parent_services, _rom_service,
|
ram_quota, _parent_services, _rom_service,
|
||||||
_cpu_service, _rm_service, _nitpicker_service,
|
_cpu_service, _rm_service, _nitpicker_service,
|
||||||
_fault_sigh);
|
_fault_sigh);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
TARGET = loader
|
TARGET = loader
|
||||||
LIBS = base init_pd_args
|
LIBS = base
|
||||||
SRC_CC = main.cc
|
SRC_CC = main.cc
|
||||||
INC_DIR += $(PRG_DIR)
|
INC_DIR += $(PRG_DIR)
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
/*
|
|
||||||
* \brief Test for dynamically starting chrooted subsystems via the loader
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-06-06
|
|
||||||
*
|
|
||||||
* This test creates two subsystems, each residing in a dedicated chroot
|
|
||||||
* environment, by combining the loader service with the chroot mechanism.
|
|
||||||
* One subsystem runs infinitely. The other subsystem will be repeatedly
|
|
||||||
* started and killed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/snprintf.h>
|
|
||||||
#include <loader_session/connection.h>
|
|
||||||
#include <os/config.h>
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************
|
|
||||||
** Helpers for obtaining test parameters from config **
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
static char const *chroot_path_from_config(char const *node_name,
|
|
||||||
char *dst, Genode::size_t dst_len)
|
|
||||||
{
|
|
||||||
Genode::config()->xml_node().sub_node(node_name)
|
|
||||||
.attribute("chroot_path").value(dst, dst_len);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static char const *chroot_path_of_static_test()
|
|
||||||
{
|
|
||||||
static char buf[1024];
|
|
||||||
return chroot_path_from_config("static_test", buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static char const *chroot_path_of_dynamic_test()
|
|
||||||
{
|
|
||||||
static char buf[1024];
|
|
||||||
return chroot_path_from_config("dynamic_test", buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********
|
|
||||||
** Test **
|
|
||||||
**********/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return subsystem configuration.
|
|
||||||
*/
|
|
||||||
static char const *subsystem_config()
|
|
||||||
{
|
|
||||||
return "<config verbose=\"yes\">\n"
|
|
||||||
" <parent-provides>\n"
|
|
||||||
" <service name=\"ROM\"/>\n"
|
|
||||||
" <service name=\"LOG\"/>\n"
|
|
||||||
" <service name=\"CAP\"/>\n"
|
|
||||||
" <service name=\"RAM\"/>\n"
|
|
||||||
" <service name=\"CPU\"/>\n"
|
|
||||||
" <service name=\"RM\"/>\n"
|
|
||||||
" <service name=\"PD\"/>\n"
|
|
||||||
" <service name=\"SIGNAL\"/>\n"
|
|
||||||
" <service name=\"Timer\"/>\n"
|
|
||||||
" </parent-provides>\n"
|
|
||||||
" <default-route>\n"
|
|
||||||
" <any-service> <parent/> </any-service>\n"
|
|
||||||
" </default-route>\n"
|
|
||||||
" <start name=\"test-timer\">\n"
|
|
||||||
" <resource name=\"RAM\" quantum=\"1G\"/>\n"
|
|
||||||
" </start>\n"
|
|
||||||
"</config>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chroot subsystem corresponding to a loader session
|
|
||||||
*/
|
|
||||||
class Chroot_subsystem
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Loader::Connection _loader;
|
|
||||||
|
|
||||||
char _label[32];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import data as ROM module into the subsystem-specific ROM service
|
|
||||||
*/
|
|
||||||
void _import_rom_module(char const *name, void const *ptr, Genode::size_t size)
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
Dataspace_capability ds = _loader.alloc_rom_module(name, size);
|
|
||||||
|
|
||||||
/* fill ROM module with data */
|
|
||||||
char *local_addr = env()->rm_session()->attach(ds);
|
|
||||||
memcpy(local_addr, ptr, size);
|
|
||||||
env()->rm_session()->detach(local_addr);
|
|
||||||
|
|
||||||
_loader.commit_rom_module(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Chroot_subsystem(char const *chroot_path, Genode::size_t ram_quota)
|
|
||||||
:
|
|
||||||
_loader(ram_quota)
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Import subsystem's configuration into the subsystem's loader
|
|
||||||
* session as a ROM module named "config".
|
|
||||||
*/
|
|
||||||
_import_rom_module("config", subsystem_config(),
|
|
||||||
strlen(subsystem_config()) + 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Name of the Genode binary is start as the root of the new
|
|
||||||
* subsystem.
|
|
||||||
*/
|
|
||||||
char const *binary_name = "init";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generate unique label name using a counter
|
|
||||||
*
|
|
||||||
* The label appears in the LOG output of the loaded subsystem.
|
|
||||||
* Technically, it does need to be unique. It is solely used
|
|
||||||
* for validating the test in the run script.
|
|
||||||
*/
|
|
||||||
static int cnt = 0;
|
|
||||||
snprintf(_label, sizeof(_label), "%s-%d", binary_name, ++cnt);
|
|
||||||
|
|
||||||
/* start execution of new subsystem */
|
|
||||||
_loader.start(binary_name,
|
|
||||||
Loader::Session::Name(_label),
|
|
||||||
Native_pd_args(chroot_path, 0, 0));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main(int, char **)
|
|
||||||
{
|
|
||||||
Genode::printf("--- chroot-loader test started ---\n");
|
|
||||||
|
|
||||||
static Chroot_subsystem static_subsystem(chroot_path_of_static_test(),
|
|
||||||
2*1024*1024);
|
|
||||||
|
|
||||||
static Timer::Connection timer;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < 5; i++) {
|
|
||||||
|
|
||||||
PLOG("dynamic test iteration %d", i);
|
|
||||||
|
|
||||||
Chroot_subsystem subsystem(chroot_path_of_dynamic_test(),
|
|
||||||
2*1024*1024);
|
|
||||||
|
|
||||||
/* grant the subsystem one second of life */
|
|
||||||
timer.msleep(1000);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The local 'dynamic_subsystem' instance will be destructed at the of
|
|
||||||
* the loop body.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::printf("--- chroot-loader test finished ---\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
TARGET = test-chroot_loader
|
|
||||||
REQUIRES += linux
|
|
||||||
SRC_CC = main.cc
|
|
||||||
LIBS += base config
|
|
Loading…
Reference in New Issue