98a75b1a78
Issue #1987
233 lines
6.3 KiB
C++
233 lines
6.3 KiB
C++
/*
|
|
* \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_ */
|