cli_monitor: read subsystem configs from VFS

This patch changes the way how CLI monitor obtains its subsystem
configurations. Originally, this information was provided via the
Genode::config mechanism. But for managing complex scenarios, the config
node becomes very complex. Hence, it is preferrable to have a distinct
file for each subsystem configuration.

The CLI monitor scans the directory '/subsystems' for files ending with
".subsystem". Each file has the same syntax as the formerly used
subsystem nodes.
This commit is contained in:
Norman Feske 2015-01-27 17:05:45 +01:00 committed by Christian Helmuth
parent f917728ecb
commit b4ebefd616
11 changed files with 973 additions and 671 deletions

View File

@ -63,6 +63,18 @@ class Child_registry : public List<Child>
snprintf(suffix, sizeof(suffix), ".%d", cnt + 1);
}
}
/**
* Call functor 'fn' for each child
*
* The functor receives the child name as 'char const *'.
*/
template <typename FN>
void for_each_child_name(FN const &fn) const
{
for (Child const *child = first() ; child; child = child->next())
fn(child->name());
}
};
#endif /* _CHILD_REGISTRY_H_ */

View File

@ -29,7 +29,7 @@
#include <child_registry.h>
#include <gdb_prefix.h>
#include <format_util.h>
#include <process_arg_registry.h>
#include <subsystem_config_registry.h>
class Gdb_command_child : public Child
{
@ -123,426 +123,424 @@ class Gdb_command_child : public Child
};
struct Gdb_command : Command
class Gdb_command : public Command
{
typedef Genode::Xml_node Xml_node;
typedef Genode::Signal_context_capability Signal_context_capability;
private:
struct Child_configuration_failed {}; /* exception */
typedef Genode::Xml_node Xml_node;
typedef Genode::Signal_context_capability Signal_context_capability;
Ram &_ram;
Child_registry &_children;
Genode::Cap_session &_cap;
Xml_node _config;
Process_arg_registry &_process_args;
List<Argument> _arguments;
Signal_context_capability _yield_response_sigh_cap;
Signal_context_capability _kill_gdb_sig_cap;
struct Child_configuration_failed {}; /* exception */
Gdb_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
Xml_node config, Process_arg_registry &process_args,
Signal_context_capability yield_response_sigh_cap,
Signal_context_capability kill_gdb_sig_cap)
:
Command("gdb", "create new subsystem with GDB"),
_ram(ram), _children(children), _cap(cap), _config(config),
_process_args(process_args),
_yield_response_sigh_cap(yield_response_sigh_cap),
_kill_gdb_sig_cap(kill_gdb_sig_cap)
{
/* scan config for possible subsystem arguments */
try {
Xml_node node = _config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
Ram &_ram;
Child_registry &_children;
Genode::Cap_session &_cap;
Subsystem_config_registry &_subsystem_configs;
Signal_context_capability _yield_response_sigh_cap;
Signal_context_capability _kill_gdb_sig_cap;
char name[Parameter::NAME_MAX_LEN];
/**
* Generate the config node for the GDB subsystem
*/
Xml_node _gdb_config_node(const char *binary_name,
const char *target_config_addr,
const size_t target_config_size,
Genode::Number_of_bytes gdb_ram_preserve,
Terminal::Session &terminal)
{
/* local exception types */
struct Config_buffer_overflow { };
struct Binary_elf_check_failed { };
try {
Genode::Attached_rom_dataspace gdb_command_config_ds("gdb_command_config");
enum { CONFIG_BUF_SIZE = 4*1024 };
static char config_buf[CONFIG_BUF_SIZE];
int config_bytes_written = 0;
/*
* Copy the first part of the config file template
*/
Xml_node init_config_node(gdb_command_config_ds.local_addr<const char>(),
gdb_command_config_ds.size());
Xml_node noux_node = init_config_node.sub_node("start");
for (;; noux_node = noux_node.next("start"))
if (noux_node.attribute("name").has_value("noux"))
break;
Xml_node noux_config_node = noux_node.sub_node("config");
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
size_t bytes_to_copy = (Genode::addr_t)noux_config_node.content_addr() -
(Genode::addr_t)init_config_node.addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
init_config_node.addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
/*
* Create the GDB arguments for breaking in 'main()'
*/
enum { GDB_MAIN_BREAKPOINT_ARGS_BUF_SIZE = 768 };
static char gdb_main_breakpoint_args_buf[GDB_MAIN_BREAKPOINT_ARGS_BUF_SIZE];
try {
Genode::Attached_rom_dataspace binary_rom_ds(binary_name);
Genode::Elf_binary elf_binary((Genode::addr_t)binary_rom_ds.local_addr<void>());
if (elf_binary.is_dynamically_linked()) {
snprintf(gdb_main_breakpoint_args_buf,
sizeof(gdb_main_breakpoint_args_buf),
"<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/ld.lib.so\" />\n \
<arg value=\"-ex\" /><arg value=\"b call_program_main\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 1\" />\n \
<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/%s\" />\n \
<arg value=\"-ex\" /><arg value=\"b main()\" />\n \
<arg value=\"-ex\" /><arg value=\"set solib-search-path /gdb\" />\n \
<arg value=\"-ex\" /><arg value=\"sharedlibrary\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 2\" />\n",
binary_name);
} else {
snprintf(gdb_main_breakpoint_args_buf,
sizeof(gdb_main_breakpoint_args_buf),
"<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/%s\" />\n \
<arg value=\"-ex\" /><arg value=\"b main\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 1\" />\n",
binary_name);
}
} catch (...) {
throw Binary_elf_check_failed();
}
/*
* Insert the '<start>' node for 'noux'
*/
config_bytes_written += snprintf(&config_buf[config_bytes_written],
sizeof(config_buf) - config_bytes_written, "\n \
<start name=\"/bin/%sgdb\">\n \
<arg value=\"/gdb/%s\"/>\n \
<arg value=\"-ex\" /><arg value=\"set interactive-mode off\" />\n \
<arg value=\"-ex\" /><arg value=\"directory /gdb/src\" />\n \
<arg value=\"-ex\" /><arg value=\"target remote /dev/gdb\" />\n \
%s \
<arg value=\"-ex\" /><arg value=\"set interactive-mode auto\" />\n \
</start>",
gdb_prefix,
binary_name,
gdb_main_breakpoint_args_buf);
/*
* Copy the second part of the config file template
*/
Xml_node gdb_monitor_node = noux_node.next("start");
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
bytes_to_copy = (Genode::addr_t)gdb_monitor_node.content_addr() -
(Genode::addr_t)noux_config_node.content_addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
noux_config_node.content_addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
/*
* Create a zero-terminated string for the GDB target config node
*/
char target_config[target_config_size + 1];
if (target_config_addr)
Genode::strncpy(target_config, target_config_addr, sizeof(target_config));
else
target_config[0] = '\0';
/*
* Insert the '<config>' node for 'gdb_monitor'
*/
config_bytes_written += Genode::snprintf(&config_buf[config_bytes_written],
(sizeof(config_buf) - config_bytes_written), "\n \
<config>\n \
<target name=\"%s\">\n \
%s \
</target>\n \
<preserve name=\"RAM\" quantum=\"%zu\"/>\n \
</config> \
",
binary_name,
target_config,
(size_t)gdb_ram_preserve);
/*
* Copy the third (and final) part of the config file template
*/
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
bytes_to_copy = ((Genode::addr_t)init_config_node.addr() +
init_config_node.size()) -
(Genode::addr_t)gdb_monitor_node.content_addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
gdb_monitor_node.content_addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
return Xml_node(config_buf, config_bytes_written);
} catch (Config_buffer_overflow) {
tprintf(terminal, "Error: the buffer for the generated GDB "
"subsystem configuration is too small.\n");
throw Child_configuration_failed();
} catch (Binary_elf_check_failed) {
tprintf(terminal, "Error: could not determine link type of the "
"GDB target binary.\n");
throw Child_configuration_failed();
}
}
void _execute_subsystem(char const *name, Command_line &cmd,
Terminal::Session &terminal,
Xml_node subsystem_node)
{
Genode::Number_of_bytes ram = 0;
Genode::Number_of_bytes ram_limit = 0;
Genode::Number_of_bytes gdb_ram_preserve = 10*1024*1024;
/* read default RAM quota from config */
try {
Xml_node rsc = subsystem_node.sub_node("resource");
for (;; rsc = rsc.next("resource")) {
if (rsc.attribute("name").has_value("RAM")) {
rsc.attribute("quantum").value(&ram);
if (rsc.has_attribute("limit"))
rsc.attribute("limit").value(&ram_limit);
break;
}
}
} catch (...) { }
cmd.parameter("--ram", ram);
cmd.parameter("--ram-limit", ram_limit);
cmd.parameter("--gdb-ram-preserve", gdb_ram_preserve);
/* account for cli_monitor local metadata */
size_t preserve_ram = 100*1024;
if ((ram + preserve_ram) > Genode::env()->ram_session()->avail()) {
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
return;
}
bool const verbose = cmd.parameter_exists("--verbose");
/*
* Determine binary name
*
* Use subsystem name by default, override with '<binary>' declaration.
*/
char binary_name[128];
strncpy(binary_name, name, sizeof(binary_name));
try {
Xml_node bin = subsystem_node.sub_node("binary");
bin.attribute("name").value(binary_name, sizeof(binary_name));
} catch (...) { }
/* generate unique child name */
char label[Child_registry::CHILD_NAME_MAX_LEN];
_children.unique_child_name(name, label, sizeof(label));
tprintf(terminal, "starting new subsystem '%s'\n", label);
if (verbose) {
tprintf(terminal, " RAM quota: ");
tprint_bytes(terminal, ram);
tprintf(terminal,"\n");
if (ram_limit) {
tprintf(terminal, " RAM limit: ");
tprint_bytes(terminal, ram_limit);
tprintf(terminal,"\n");
}
tprintf(terminal, " binary: %s\n", binary_name);
}
Child *child = 0;
try {
/* create child configuration */
const char *target_config_addr = 0;
size_t target_config_size = 0;
try {
Xml_node target_config_node = subsystem_node.sub_node("config");
target_config_addr = target_config_node.addr();
target_config_size = target_config_node.size();
} catch (...) { }
Xml_node config_node = _gdb_config_node(binary_name,
target_config_addr,
target_config_size,
gdb_ram_preserve,
terminal);
/* create child */
child = new (Genode::env()->heap())
Gdb_command_child(_ram, label, "init", _cap, ram, ram_limit,
_yield_response_sigh_cap, _kill_gdb_sig_cap,
terminal);
/* configure child */
try {
child->configure(config_node.addr(), config_node.size());
if (verbose)
tprintf(terminal, " config: inline\n");
} catch (...) {
if (verbose)
tprintf(terminal, " config: none\n");
}
}
catch (Child_configuration_failed) {
return;
}
catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
binary_name);
return;
}
catch (Child::Quota_exceeded) {
tprintf(terminal, "Error: insufficient memory, need ");
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
tprintf(terminal, ", have ");
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
tprintf(terminal, "\n");
return;
}
catch (Genode::Allocator::Out_of_memory) {
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
return;
}
_children.insert(child);
child->start();
}
public:
Gdb_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
Subsystem_config_registry &subsustem_configs,
Signal_context_capability yield_response_sigh_cap,
Signal_context_capability kill_gdb_sig_cap)
:
Command("gdb", "create new subsystem with GDB"),
_ram(ram), _children(children), _cap(cap),
_subsystem_configs(subsustem_configs),
_yield_response_sigh_cap(yield_response_sigh_cap),
_kill_gdb_sig_cap(kill_gdb_sig_cap)
{
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
add_parameter(new Parameter("--gdb-ram-preserve", Parameter::NUMBER,
"RAM quota which GDB monitor should preserve for itself (default: 5M)"));
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
}
void _for_each_argument(Argument_fn const &fn) const override
{
/* functor for processing a subsystem configuration */
auto process_subsystem_config_fn = [&] (Genode::Xml_node node) {
char name[Parameter::Name::size()];
try { node.attribute("name").value(name, sizeof(name)); }
catch (Xml_node::Nonexistent_attribute) {
PWRN("Missing name in '<subsystem>' configuration");
continue;
return;
}
char const *prefix = "config: ";
size_t const prefix_len = strlen(prefix);
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
char help[Parameter::Short_help::size() + prefix_len];
strncpy(help, prefix, ~0);
try { node.attribute("help").value(help + prefix_len,
sizeof(help) - prefix_len); }
catch (Xml_node::Nonexistent_attribute) {
PWRN("Missing help in '<subsystem>' configuration");
continue;
return;
}
_arguments.insert(new Argument(name, help));
}
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
Argument arg(name, help);
fn(arg);
};
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
add_parameter(new Parameter("--gdb-ram-preserve", Parameter::NUMBER,
"RAM quota which GDB monitor should preserve for itself (default: 5M)"));
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
}
/**
* Lookup subsystem in config
*/
Xml_node _subsystem_node(char const *name)
{
Xml_node node = _config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
if (node.attribute("name").has_value(name))
return node;
/* scan subsystem config registry for possible subsystem arguments */
_subsystem_configs.for_each_config(process_subsystem_config_fn);
}
}
/**
* Generate the config node for the GDB subsystem
*/
Xml_node _gdb_config_node(const char *binary_name,
const char *target_config_addr,
const size_t target_config_size,
Genode::Number_of_bytes gdb_ram_preserve,
Terminal::Session &terminal)
{
/* local exception types */
struct Config_buffer_overflow { };
struct Binary_elf_check_failed { };
void execute(Command_line &cmd, Terminal::Session &terminal)
{
/* check if the GDB-related ROM modules are available */
try {
Genode::Rom_connection gdb_command_config("gdb_command_config");
Genode::Rom_connection terminal_crosslink("terminal_crosslink");
Genode::Rom_connection noux("noux");
Genode::Rom_connection gdb_monitor("gdb_monitor");
} catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: The 'gdb' command needs the following ROM "
"modules (of which some are currently missing): "
"gdb_command_config, terminal_crosslink, noux, ",
"gdb_monitor\n");
return;
}
try {
Genode::Attached_rom_dataspace gdb_command_config_ds("gdb_command_config");
char name[128];
name[0] = 0;
if (cmd.argument(0, name, sizeof(name)) == false) {
tprintf(terminal, "Error: no configuration name specified\n");
return;
}
enum { CONFIG_BUF_SIZE = 4*1024 };
static char config_buf[CONFIG_BUF_SIZE];
int config_bytes_written = 0;
/*
* Copy the first part of the config file template
*/
Xml_node init_config_node(gdb_command_config_ds.local_addr<const char>(),
gdb_command_config_ds.size());
Xml_node noux_node = init_config_node.sub_node("start");
for (;; noux_node = noux_node.next("start"))
if (noux_node.attribute("name").has_value("noux"))
break;
Xml_node noux_config_node = noux_node.sub_node("config");
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
size_t bytes_to_copy = (Genode::addr_t)noux_config_node.content_addr() -
(Genode::addr_t)init_config_node.addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
init_config_node.addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
/*
* Create the GDB arguments for breaking in 'main()'
*/
enum { GDB_MAIN_BREAKPOINT_ARGS_BUF_SIZE = 768 };
static char gdb_main_breakpoint_args_buf[GDB_MAIN_BREAKPOINT_ARGS_BUF_SIZE];
char buf[128];
if (cmd.argument(1, buf, sizeof(buf))) {
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
return;
}
try {
Genode::Attached_rom_dataspace binary_rom_ds(binary_name);
Genode::Elf_binary elf_binary((Genode::addr_t)binary_rom_ds.local_addr<void>());
_subsystem_configs.for_config(name, [&] (Genode::Xml_node node)
{
_execute_subsystem(name, cmd, terminal, node);
});
if (elf_binary.is_dynamically_linked()) {
snprintf(gdb_main_breakpoint_args_buf,
sizeof(gdb_main_breakpoint_args_buf),
"<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/ld.lib.so\" />\n \
<arg value=\"-ex\" /><arg value=\"b call_program_main\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 1\" />\n \
<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/%s\" />\n \
<arg value=\"-ex\" /><arg value=\"b main()\" />\n \
<arg value=\"-ex\" /><arg value=\"set solib-search-path /gdb\" />\n \
<arg value=\"-ex\" /><arg value=\"sharedlibrary\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 2\" />\n",
binary_name);
} else {
snprintf(gdb_main_breakpoint_args_buf,
sizeof(gdb_main_breakpoint_args_buf),
"<arg value=\"-ex\" /><arg value=\"symbol-file /gdb/%s\" />\n \
<arg value=\"-ex\" /><arg value=\"b main\" />\n \
<arg value=\"-ex\" /><arg value=\"c\" />\n \
<arg value=\"-ex\" /><arg value=\"delete 1\" />\n",
binary_name);
}
} catch (...) {
throw Binary_elf_check_failed();
} catch (Subsystem_config_registry::Nonexistent_subsystem_config) {
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
}
/*
* Insert the '<start>' node for 'noux'
*/
config_bytes_written += snprintf(&config_buf[config_bytes_written],
sizeof(config_buf) - config_bytes_written, "\n \
<start name=\"/bin/%sgdb\">\n \
<arg value=\"/gdb/%s\"/>\n \
<arg value=\"-ex\" /><arg value=\"set interactive-mode off\" />\n \
<arg value=\"-ex\" /><arg value=\"directory /gdb/src\" />\n \
<arg value=\"-ex\" /><arg value=\"target remote /dev/gdb\" />\n \
%s \
<arg value=\"-ex\" /><arg value=\"set interactive-mode auto\" />\n \
</start>",
gdb_prefix,
binary_name,
gdb_main_breakpoint_args_buf);
/*
* Copy the second part of the config file template
*/
Xml_node gdb_monitor_node = noux_node.next("start");
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
bytes_to_copy = (Genode::addr_t)gdb_monitor_node.content_addr() -
(Genode::addr_t)noux_config_node.content_addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
noux_config_node.content_addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
/*
* Create a zero-terminated string for the GDB target config node
*/
char target_config[target_config_size + 1];
if (target_config_addr)
Genode::strncpy(target_config, target_config_addr, sizeof(target_config));
else
target_config[0] = '\0';
/*
* Insert the '<config>' node for 'gdb_monitor'
*/
config_bytes_written += Genode::snprintf(&config_buf[config_bytes_written],
(sizeof(config_buf) - config_bytes_written), "\n \
<config>\n \
<target name=\"%s\">\n \
%s \
</target>\n \
<preserve name=\"RAM\" quantum=\"%zu\"/>\n \
</config> \
",
binary_name,
target_config,
(size_t)gdb_ram_preserve);
/*
* Copy the third (and final) part of the config file template
*/
/* Genode::strncpy() makes the last character '\0', so we need to add 1 byte */
bytes_to_copy = ((Genode::addr_t)init_config_node.addr() +
init_config_node.size()) -
(Genode::addr_t)gdb_monitor_node.content_addr() + 1;
if ((sizeof(config_buf) - config_bytes_written) < bytes_to_copy)
throw Config_buffer_overflow();
strncpy(&config_buf[config_bytes_written],
gdb_monitor_node.content_addr(),
bytes_to_copy);
/* subtract the byte for '\0' again */
config_bytes_written += bytes_to_copy - 1;
return Xml_node(config_buf, config_bytes_written);
} catch (Config_buffer_overflow) {
tprintf(terminal, "Error: the buffer for the generated GDB "
"subsystem configuration is too small.\n");
throw Child_configuration_failed();
} catch (Binary_elf_check_failed) {
tprintf(terminal, "Error: could not determine link type of the "
"GDB target binary.\n");
throw Child_configuration_failed();
}
}
void execute(Command_line &cmd, Terminal::Session &terminal)
{
/* check if the GDB-related ROM modules are available */
try {
Genode::Rom_connection gdb_command_config("gdb_command_config");
Genode::Rom_connection terminal_crosslink("terminal_crosslink");
Genode::Rom_connection noux("noux");
Genode::Rom_connection gdb_monitor("gdb_monitor");
} catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: The 'gdb' command needs the following ROM "
"modules (of which some are currently missing): "
"gdb_command_config, terminal_crosslink, noux, ",
"gdb_monitor\n");
return;
}
Genode::Number_of_bytes ram = 0;
Genode::Number_of_bytes ram_limit = 0;
Genode::Number_of_bytes gdb_ram_preserve = 10*1024*1024;
char name[128];
name[0] = 0;
if (cmd.argument(0, name, sizeof(name)) == false) {
tprintf(terminal, "Error: no configuration name specified\n");
return;
}
char buf[128];
if (cmd.argument(1, buf, sizeof(buf))) {
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
return;
}
/* check if a configuration for the subsystem exists */
try { _subsystem_node(name); }
catch (Xml_node::Nonexistent_sub_node) {
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
return;
}
/* read default RAM quota from config */
try {
Xml_node rsc = _subsystem_node(name).sub_node("resource");
for (;; rsc = rsc.next("resource")) {
if (rsc.attribute("name").has_value("RAM")) {
rsc.attribute("quantum").value(&ram);
if (rsc.has_attribute("limit"))
rsc.attribute("limit").value(&ram_limit);
break;
}
}
} catch (...) { }
cmd.parameter("--ram", ram);
cmd.parameter("--ram-limit", ram_limit);
cmd.parameter("--gdb-ram-preserve", gdb_ram_preserve);
/* account for cli_monitor local metadata */
size_t preserve_ram = 100*1024;
if ((ram + preserve_ram) > Genode::env()->ram_session()->avail()) {
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
return;
}
bool const verbose = cmd.parameter_exists("--verbose");
/*
* Determine binary name
*
* Use subsystem name by default, override with '<binary>' declaration.
*/
char binary_name[128];
strncpy(binary_name, name, sizeof(binary_name));
try {
Xml_node bin = _subsystem_node(name).sub_node("binary");
bin.attribute("name").value(binary_name, sizeof(binary_name));
} catch (...) { }
/* generate unique child name */
char label[Child_registry::CHILD_NAME_MAX_LEN];
_children.unique_child_name(name, label, sizeof(label));
tprintf(terminal, "starting new subsystem '%s'\n", label);
if (verbose) {
tprintf(terminal, " RAM quota: ");
tprint_bytes(terminal, ram);
tprintf(terminal,"\n");
if (ram_limit) {
tprintf(terminal, " RAM limit: ");
tprint_bytes(terminal, ram_limit);
tprintf(terminal,"\n");
}
tprintf(terminal, " binary: %s\n", binary_name);
}
Child *child = 0;
try {
/* create child configuration */
const char *target_config_addr = 0;
size_t target_config_size = 0;
try {
Xml_node target_config_node = _subsystem_node(name).sub_node("config");
target_config_addr = target_config_node.addr();
target_config_size = target_config_node.size();
} catch (...) { }
Xml_node config_node = _gdb_config_node(binary_name,
target_config_addr,
target_config_size,
gdb_ram_preserve,
terminal);
/* create child */
child = new (Genode::env()->heap())
Gdb_command_child(_ram, label, "init", _cap, ram, ram_limit,
_yield_response_sigh_cap, _kill_gdb_sig_cap,
terminal);
/* configure child */
try {
child->configure(config_node.addr(), config_node.size());
if (verbose)
tprintf(terminal, " config: inline\n");
} catch (...) {
if (verbose)
tprintf(terminal, " config: none\n");
}
}
catch (Child_configuration_failed) {
return;
}
catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
binary_name);
return;
}
catch (Child::Quota_exceeded) {
tprintf(terminal, "Error: insufficient memory, need ");
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
tprintf(terminal, ", have ");
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
tprintf(terminal, "\n");
return;
}
catch (Genode::Allocator::Out_of_memory) {
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
return;
}
_process_args.list.insert(&child->argument);
_children.insert(child);
child->start();
}
List<Argument> &arguments() { return _arguments; }
};
#endif /* _GDB_COMMAND_H_ */

View File

@ -16,31 +16,36 @@
/* local includes */
#include <child_registry.h>
#include <process_arg_registry.h>
struct Kill_command : Command
{
Child_registry &_children;
Process_arg_registry &_process_args;
void _destroy_child(Child *child, Terminal::Session &terminal)
{
tprintf(terminal, "destroying subsystem '%s'\n", child->name());
_process_args.list.remove(&child->argument);
_children.remove(child);
Genode::destroy(Genode::env()->heap(), child);
}
Kill_command(Child_registry &children, Process_arg_registry &process_args)
Kill_command(Child_registry &children)
:
Command("kill", "destroy subsystem"),
_children(children),
_process_args(process_args)
_children(children)
{
add_parameter(new Parameter("--all", Parameter::VOID, "kill all subsystems"));
}
void _for_each_argument(Argument_fn const &fn) const override
{
auto child_name_fn = [&] (char const *child_name) {
Argument arg(child_name, "");
fn(arg);
};
_children.for_each_child_name(child_name_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal)
{
bool const kill_all = cmd.parameter_exists("--all");
@ -68,8 +73,6 @@ struct Kill_command : Command
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
}
List<Argument> &arguments() { return _process_args.list; }
};
#endif /* _KILL_COMMAND_H_ */

View File

@ -35,18 +35,14 @@ using Genode::off_t;
struct Completable
{
template <size_t MAX_LEN> struct String
{
char buf[MAX_LEN];
String(char const *string) { strncpy(buf, string, sizeof(buf)); }
};
typedef Genode::String<64> Name;
typedef Genode::String<160> Short_help;
enum { NAME_MAX_LEN = 64, SHORT_HELP_MAX_LEN = 160 };
String<NAME_MAX_LEN> const _name;
String<SHORT_HELP_MAX_LEN> const _short_help;
Name const _name;
Short_help const _short_help;
char const *name() const { return _name.buf; }
char const *short_help() const { return _short_help.buf; }
Name name() const { return _name; }
Short_help short_help() const { return _short_help; }
Completable(char const *name, char const *short_help)
: _name(name), _short_help(short_help) { }
@ -56,7 +52,7 @@ struct Completable
/**
* Representation of normal command-line argument
*/
struct Argument : List<Argument>::Element, Completable
struct Argument : Completable
{
Argument(char const *name, char const *short_help)
: Completable(name, short_help) { }
@ -103,6 +99,14 @@ struct Command : List<Command>::Element, Completable
{
List<Parameter> _parameters;
/**
* Functor that takes a command 'Argument' object as argument
*/
struct Argument_fn
{
virtual void operator () (Argument const &) const = 0;
};
Command(char const *name, char const *short_help)
: Completable(name, short_help) { }
@ -112,16 +116,28 @@ struct Command : List<Command>::Element, Completable
List<Parameter> &parameters() { return _parameters; }
/**
* To be overridden by commands that accept auto-completion of arguments
*/
virtual List<Argument> &arguments()
{
static List<Argument> empty;
return empty;
}
virtual void execute(Command_line &, Terminal::Session &terminal) = 0;
/**
* Command-specific support for 'for_each_argument'
*/
virtual void _for_each_argument(Argument_fn const &fn) const { };
/**
* Execute functor 'fn' for each command argument
*/
template <typename FN>
void for_each_argument(FN const &fn) const
{
struct _Fn : Argument_fn
{
FN const &fn;
void operator () (Argument const &arg) const override { fn(arg); }
_Fn(FN const &fn) : fn(fn) { }
} _fn(fn);
_for_each_argument(_fn);
}
};
@ -162,22 +178,24 @@ struct Argument_tracker
* Return true if there is exactly one complete match and no additional
* partial matches
*/
template <typename T>
static bool _one_match(char const *str, size_t str_len,
List<T> &list)
static bool _one_matching_argument(char const *str, size_t str_len,
Command const &command)
{
unsigned complete_cnt = 0, partial_cnt = 0;
Token tag(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(tag.start(), curr->name(), tag.len()) == 0) {
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0) {
partial_cnt++;
if (strlen(curr->name()) == tag.len())
if (strlen(arg.name().string()) == str_len)
complete_cnt++;
}
}
};
return partial_cnt == 1 && complete_cnt == 1;;
command.for_each_argument(argument_fn);
return partial_cnt == 1 && complete_cnt == 1;
}
public:
@ -191,8 +209,8 @@ struct Argument_tracker
{
Token tag(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next())
if (strcmp(tag.start(), curr->name(), tag.len()) == 0
&& strlen(curr->name()) == tag.len())
if (strcmp(tag.start(), curr->name().string(), tag.len()) == 0
&& strlen(curr->name().string()) == tag.len())
return curr;
return 0;
@ -238,7 +256,7 @@ struct Argument_tracker
}
if (!token_may_be_incomplete
|| _one_match(token.start(), token.len(), _command.arguments()))
|| _one_matching_argument(token.start(), token.len(), _command))
_state = EXPECT_SPACE_BEFORE_ARG;
}
break;
@ -469,7 +487,7 @@ class Line_editor
{
Token cmd(_buf, _cursor_pos);
for (Command *curr = _commands.first(); curr; curr = curr->next())
if (strcmp(cmd.start(), curr->name(), cmd.len()) == 0
if (strcmp(cmd.start(), curr->name().string(), cmd.len()) == 0
&& _cursor_pos > cmd.len())
return curr;
return 0;
@ -482,7 +500,7 @@ class Line_editor
unsigned num_partial_matches = 0;
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name(), token.len()) != 0)
if (strcmp(token.start(), curr->name().string(), token.len()) != 0)
continue;
num_partial_matches++;
@ -490,6 +508,22 @@ class Line_editor
return num_partial_matches;
}
unsigned _num_matching_arguments(char const *str, size_t str_len,
Command const &command) const
{
unsigned num_matches = 0;
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0)
num_matches++;
};
command.for_each_argument(argument_fn);
return num_matches;
}
/**
* Determine the name-column width of list of partial matches
*/
@ -501,16 +535,35 @@ class Line_editor
size_t max_name_len = 0;
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name(), token.len()) != 0)
if (strcmp(token.start(), curr->name().string(), token.len()) != 0)
continue;
size_t const name_len = strlen(curr->name())
size_t const name_len = strlen(curr->name().string())
+ strlen(curr->name_suffix());
max_name_len = max(max_name_len, name_len);
}
return max_name_len;
}
unsigned _width_of_matching_arguments(char const *str, size_t str_len,
Command const &command) const
{
size_t max_name_len = 0;
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0) {
size_t const name_len = strlen(arg.name().string());
if (name_len > max_name_len)
max_name_len = name_len;
}
};
command.for_each_argument(argument_fn);
return max_name_len;
}
template <typename T>
char const *_any_partial_match_name(char const *str, size_t str_len,
List<T> &list)
@ -518,12 +571,28 @@ class Line_editor
Token token(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next())
if (strcmp(token.start(), curr->name(), token.len()) == 0)
return curr->name();
if (strcmp(token.start(), curr->name().string(), token.len()) == 0)
return curr->name().string();
return 0;
}
Argument::Name _any_matching_argument(char const *str, size_t str_len,
Command const &command) const
{
Argument::Name name;
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0)
name = arg.name();
};
command.for_each_argument(argument_fn);
return name;
}
template <typename T>
void _list_partial_matches(char const *str, size_t str_len,
unsigned pad, List<T> &list)
@ -531,23 +600,47 @@ class Line_editor
Token token(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name(), token.len()) != 0)
if (strcmp(token.start(), curr->name().string(), token.len()) != 0)
continue;
_write_newline();
_write_spaces(2);
_write(curr->name());
_write(curr->name().string());
_write_spaces(1);
_write(curr->name_suffix());
/* pad short help with whitespaces */
size_t const name_len = strlen(curr->name())
size_t const name_len = strlen(curr->name().string())
+ strlen(curr->name_suffix());
_write_spaces(pad + 3 - name_len);
_write(curr->short_help());
_write(curr->short_help().string());
}
}
void _list_matching_arguments(char const *str, size_t str_len,
unsigned pad, Command const &command)
{
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0) {
_write_newline();
_write_spaces(2);
_write(arg.name().string());
_write_spaces(1);
_write(arg.name_suffix());
/* pad short help with whitespaces */
size_t const name_len = strlen(arg.name().string())
+ strlen(arg.name_suffix());
_write_spaces(pad + 3 - name_len);
_write(arg.short_help().string());
}
};
command.for_each_argument(argument_fn);
}
template <typename T>
void _do_completion(char const *str, size_t str_len, List<T> &list)
{
@ -556,7 +649,7 @@ class Line_editor
/* look up completable token */
T *partial_match = 0;
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name(), token.len()) == 0) {
if (strcmp(token.start(), curr->name().string(), token.len()) == 0) {
partial_match = curr;
break;
}
@ -565,8 +658,27 @@ class Line_editor
if (!partial_match)
return;
for (unsigned i = token.len(); i < strlen(partial_match->name()); i++)
_insert_character(partial_match->name()[i]);
for (unsigned i = token.len(); i < strlen(partial_match->name().string()); i++)
_insert_character(partial_match->name().string()[i]);
_insert_character(' ');
}
void _do_argument_completion(char const *str, size_t str_len,
Command const &command)
{
Argument::Name partial_match;
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0)
partial_match = arg.name();
};
command.for_each_argument(argument_fn);
for (unsigned i = str_len; i < strlen(partial_match.string()); i++)
_insert_character(partial_match.string()[i]);
_insert_character(' ');
}
@ -577,7 +689,7 @@ class Line_editor
_num_partial_matches(str, str_len, command.parameters());
unsigned const matching_arguments =
_num_partial_matches(str, str_len, command.arguments());
_num_matching_arguments(str, str_len, command);
/* matches are ambiguous */
if (matching_arguments + matching_parameters > 1) {
@ -586,13 +698,17 @@ class Line_editor
* Try to complete additional characters that are common among
* all matches.
*/
char buf[Completable::NAME_MAX_LEN];
char buf[Completable::Name::size()];
strncpy(buf, str, Genode::min(sizeof(buf), str_len + 1));
/* pick any representative as a template to take characters from */
char const *name = _any_partial_match_name(str, str_len, command.parameters());
if (!name)
name = _any_partial_match_name(str, str_len, command.arguments());
Argument::Name arg_name;
if (!name) {
arg_name = _any_matching_argument(str, str_len, command);
if (strlen(arg_name.string()))
name = arg_name.string();
}
size_t i = str_len;
for (; (i < sizeof(buf) - 1) && (i < strlen(name)); i++) {
@ -605,7 +721,7 @@ class Line_editor
break;
if (matching_arguments !=
_num_partial_matches(buf, i + 1, command.arguments()))
_num_matching_arguments(buf, i + 1, command))
break;
_insert_character(buf[i]);
@ -624,10 +740,10 @@ class Line_editor
*/
size_t const pad =
max(_width_of_partial_matches(str, str_len, command.parameters()),
_width_of_partial_matches(str, str_len, command.arguments()));
_width_of_matching_arguments(str, str_len, command));
_list_partial_matches(str, str_len, pad, command.parameters());
_list_partial_matches(str, str_len, pad, command.arguments());
_list_matching_arguments(str, str_len, pad, command);
_write_newline();
_fresh_prompt();
@ -639,7 +755,7 @@ class Line_editor
_do_completion(str, str_len, command.parameters());
if (matching_arguments == 1)
_do_completion(str, str_len, command.arguments());
_do_argument_completion(str, str_len, command);
}
void _perform_completion()

View File

@ -14,6 +14,8 @@
/* Genode includes */
#include <os/config.h>
#include <cap_session/connection.h>
#include <vfs/file_system_factory.h>
#include <vfs/dir_file_system.h>
/* public CLI-monitor includes */
#include <cli_monitor/ram.h>
@ -48,8 +50,8 @@ static inline Command *lookup_command(char const *buf, Command_registry &registr
{
Token token(buf);
for (Command *curr = registry.first(); curr; curr = curr->next())
if (strcmp(token.start(), curr->name(), token.len()) == 0
&& strlen(curr->name()) == token.len())
if (strcmp(token.start(), curr->name().string(), token.len()) == 0
&& strlen(curr->name().string()) == token.len())
return curr;
return 0;
}
@ -70,6 +72,30 @@ static size_t ram_preservation_from_config()
}
/**
* Return singleton instance of the subsystem config registry
*/
static Subsystem_config_registry &subsystem_config_registry()
{
try {
/* initialize virtual file system */
static Vfs::Dir_file_system
root_dir(Genode::config()->xml_node().sub_node("vfs"),
Vfs::global_file_system_factory());
static Subsystem_config_registry inst(root_dir);
return inst;
} catch (Genode::Xml_node::Nonexistent_sub_node) {
PERR("missing '<vfs>' configuration");
throw;
}
}
int main(int argc, char **argv)
{
/* look for dynamic linker */
@ -86,7 +112,6 @@ int main(int argc, char **argv)
static Terminal::Connection terminal;
static Command_registry commands;
static Child_registry children;
static Process_arg_registry process_args;
/* initialize platform-specific commands */
init_extension(commands);
@ -112,20 +137,18 @@ int main(int argc, char **argv)
/* initialize generic commands */
commands.insert(new Help_command);
Kill_command kill_command(children, process_args);
Kill_command kill_command(children);
commands.insert(&kill_command);
commands.insert(new Gdb_command(ram, cap, children,
Genode::config()->xml_node(),
process_args,
subsystem_config_registry(),
yield_response_sig_cap,
kill_gdb_sig_cap));
commands.insert(new Start_command(ram, cap, children,
Genode::config()->xml_node(),
process_args,
subsystem_config_registry(),
yield_response_sig_cap));
commands.insert(new Status_command(ram, children));
commands.insert(new Yield_command(children, process_args));
commands.insert(new Ram_command(children, process_args));
commands.insert(new Yield_command(children));
commands.insert(new Ram_command(children));
enum { COMMAND_MAX_LEN = 1000 };
static char buf[COMMAND_MAX_LEN];
@ -180,7 +203,6 @@ int main(int argc, char **argv)
dynamic_cast<Gdb_command_child*>(child);
if (gdb_command_child && gdb_command_child->kill_requested()) {
tprintf(terminal, "Destroying GDB subsystem after an error occured.\n");
process_args.list.remove(&gdb_command_child->argument);
children.remove(gdb_command_child);
Genode::destroy(Genode::env()->heap(), gdb_command_child);
line_editor.reset();

View File

@ -1,25 +0,0 @@
/*
* \brief Registry of process names used as arguments
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 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 _PROCESS_ARG_REGISTRY_H_
#define _PROCESS_ARG_REGISTRY_H_
/**
* Registry of arguments referring to the currently running processes
*/
struct Process_arg_registry
{
Genode::List<Argument> list;
};
#endif /* _PROCESS_ARG_REGISTRY_H_ */

View File

@ -16,18 +16,15 @@
/* local includes */
#include <child_registry.h>
#include <process_arg_registry.h>
struct Ram_command : Command
{
Child_registry &_children;
Process_arg_registry &_process_args;
Ram_command(Child_registry &children, Process_arg_registry &process_args)
Ram_command(Child_registry &children)
:
Command("ram", "set RAM quota of subsystem"),
_children(children),
_process_args(process_args)
_children(children)
{
add_parameter(new Parameter("--quota", Parameter::NUMBER, "new RAM quota"));
add_parameter(new Parameter("--limit", Parameter::NUMBER, "on-demand quota limit"));
@ -83,6 +80,16 @@ struct Ram_command : Command
}
}
void _for_each_argument(Argument_fn const &fn) const override
{
auto child_name_fn = [&] (char const *child_name) {
Argument arg(child_name, "");
fn(arg);
};
_children.for_each_child_name(child_name_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal)
{
char label[128];
@ -116,8 +123,6 @@ struct Ram_command : Command
_set_quota(terminal, *child, quota);
}
}
List<Argument> &arguments() { return _process_args.list; }
};
#endif /* _RAM_COMMAND_H_ */

View File

@ -17,201 +17,206 @@
/* Genode includes */
#include <util/xml_node.h>
struct Start_command : Command
/* local includes */
#include <subsystem_config_registry.h>
class Start_command : public Command
{
typedef Genode::Xml_node Xml_node;
typedef Genode::Signal_context_capability Signal_context_capability;
private:
Ram &_ram;
Child_registry &_children;
Genode::Cap_session &_cap;
Xml_node _config;
Process_arg_registry &_process_args;
List<Argument> _arguments;
Signal_context_capability _yield_response_sigh_cap;
typedef Genode::Xml_node Xml_node;
typedef Genode::Signal_context_capability Signal_context_capability;
Start_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
Xml_node config, Process_arg_registry &process_args,
Signal_context_capability yield_response_sigh_cap)
:
Command("start", "create new subsystem"),
_ram(ram), _children(children), _cap(cap), _config(config),
_process_args(process_args),
_yield_response_sigh_cap(yield_response_sigh_cap)
{
/* scan config for possible subsystem arguments */
try {
Xml_node node = _config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
Ram &_ram;
Child_registry &_children;
Genode::Cap_session &_cap;
Subsystem_config_registry &_subsystem_configs;
List<Argument> _arguments;
Signal_context_capability _yield_response_sigh_cap;
char name[Parameter::NAME_MAX_LEN];
void _execute_subsystem(char const *name, Command_line &cmd,
Terminal::Session &terminal,
Genode::Xml_node subsystem_node)
{
size_t count = 1;
Genode::Number_of_bytes ram = 0;
Genode::Number_of_bytes ram_limit = 0;
/* read default RAM quota from config */
try {
Xml_node rsc = subsystem_node.sub_node("resource");
for (;; rsc = rsc.next("resource")) {
if (rsc.attribute("name").has_value("RAM")) {
rsc.attribute("quantum").value(&ram);
if (rsc.has_attribute("limit"))
rsc.attribute("limit").value(&ram_limit);
break;
}
}
} catch (...) { }
cmd.parameter("--count", count);
cmd.parameter("--ram", ram);
cmd.parameter("--ram-limit", ram_limit);
/* acount for cli_monitor local metadata */
size_t preserve_ram = 100*1024;
if (count * (ram + preserve_ram) > Genode::env()->ram_session()->avail()) {
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
return;
}
bool const verbose = cmd.parameter_exists("--verbose");
/*
* Determine binary name
*
* Use subsystem name by default, override with '<binary>' declaration.
*/
char binary_name[128];
strncpy(binary_name, name, sizeof(binary_name));
try {
Xml_node bin = subsystem_node.sub_node("binary");
bin.attribute("name").value(binary_name, sizeof(binary_name));
} catch (...) { }
for (unsigned i = 0; i < count; i++) {
/* generate unique child name */
char label[Child_registry::CHILD_NAME_MAX_LEN];
_children.unique_child_name(name, label, sizeof(label));
tprintf(terminal, "starting new subsystem '%s'\n", label);
if (verbose) {
tprintf(terminal, " RAM quota: ");
tprint_bytes(terminal, ram);
tprintf(terminal,"\n");
if (ram_limit) {
tprintf(terminal, " RAM limit: ");
tprint_bytes(terminal, ram_limit);
tprintf(terminal,"\n");
}
tprintf(terminal, " binary: %s\n", binary_name);
}
Child *child = 0;
try {
child = new (Genode::env()->heap())
Child(_ram, label, binary_name, _cap, ram, ram_limit,
_yield_response_sigh_cap);
}
catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
binary_name);
return;
}
catch (Child::Quota_exceeded) {
tprintf(terminal, "Error: insufficient memory, need ");
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
tprintf(terminal, ", have ");
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
tprintf(terminal, "\n");
return;
}
catch (Genode::Allocator::Out_of_memory) {
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
return;
}
/* configure child */
try {
Xml_node config_node = subsystem_node.sub_node("config");
child->configure(config_node.addr(), config_node.size());
if (verbose)
tprintf(terminal, " config: inline\n");
} catch (...) {
if (verbose)
tprintf(terminal, " config: none\n");
}
_children.insert(child);
child->start();
}
}
public:
Start_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
Subsystem_config_registry &subsustem_configs,
Signal_context_capability yield_response_sigh_cap)
:
Command("start", "create new subsystem"),
_ram(ram), _children(children), _cap(cap),
_subsystem_configs(subsustem_configs),
_yield_response_sigh_cap(yield_response_sigh_cap)
{
add_parameter(new Parameter("--count", Parameter::NUMBER, "number of instances"));
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
}
void _for_each_argument(Argument_fn const &fn) const override
{
/* functor for processing a subsystem configuration */
auto process_subsystem_config_fn = [&] (Genode::Xml_node node) {
char name[Parameter::Name::size()];
try { node.attribute("name").value(name, sizeof(name)); }
catch (Xml_node::Nonexistent_attribute) {
PWRN("Missing name in '<subsystem>' configuration");
continue;
return;
}
char const *prefix = "config: ";
size_t const prefix_len = strlen(prefix);
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
char help[Parameter::Short_help::size() + prefix_len];
strncpy(help, prefix, ~0);
try { node.attribute("help").value(help + prefix_len,
sizeof(help) - prefix_len); }
catch (Xml_node::Nonexistent_attribute) {
PWRN("Missing help in '<subsystem>' configuration");
continue;
return;
}
_arguments.insert(new Argument(name, help));
}
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
Argument arg(name, help);
fn(arg);
};
add_parameter(new Parameter("--count", Parameter::NUMBER, "number of instances"));
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
}
/**
* Lookup subsystem in config
*/
Xml_node _subsystem_node(char const *name)
{
Xml_node node = _config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
if (node.attribute("name").has_value(name))
return node;
}
}
void execute(Command_line &cmd, Terminal::Session &terminal)
{
size_t count = 1;
Genode::Number_of_bytes ram = 0;
Genode::Number_of_bytes ram_limit = 0;
char name[128];
name[0] = 0;
if (cmd.argument(0, name, sizeof(name)) == false) {
tprintf(terminal, "Error: no configuration name specified\n");
return;
/* scan subsystem config registry for possible subsystem arguments */
_subsystem_configs.for_each_config(process_subsystem_config_fn);
}
char buf[128];
if (cmd.argument(1, buf, sizeof(buf))) {
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
return;
}
/* check if a configuration for the subsystem exists */
try { _subsystem_node(name); }
catch (Xml_node::Nonexistent_sub_node) {
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
return;
}
/* read default RAM quota from config */
try {
Xml_node rsc = _subsystem_node(name).sub_node("resource");
for (;; rsc = rsc.next("resource")) {
if (rsc.attribute("name").has_value("RAM")) {
rsc.attribute("quantum").value(&ram);
if (rsc.has_attribute("limit"))
rsc.attribute("limit").value(&ram_limit);
break;
}
}
} catch (...) { }
cmd.parameter("--count", count);
cmd.parameter("--ram", ram);
cmd.parameter("--ram-limit", ram_limit);
/* acount for cli_monitor local metadata */
size_t preserve_ram = 100*1024;
if (count * (ram + preserve_ram) > Genode::env()->ram_session()->avail()) {
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
return;
}
bool const verbose = cmd.parameter_exists("--verbose");
/*
* Determine binary name
*
* Use subsystem name by default, override with '<binary>' declaration.
*/
char binary_name[128];
strncpy(binary_name, name, sizeof(binary_name));
try {
Xml_node bin = _subsystem_node(name).sub_node("binary");
bin.attribute("name").value(binary_name, sizeof(binary_name));
} catch (...) { }
for (unsigned i = 0; i < count; i++) {
/* generate unique child name */
char label[Child_registry::CHILD_NAME_MAX_LEN];
_children.unique_child_name(name, label, sizeof(label));
tprintf(terminal, "starting new subsystem '%s'\n", label);
if (verbose) {
tprintf(terminal, " RAM quota: ");
tprint_bytes(terminal, ram);
tprintf(terminal,"\n");
if (ram_limit) {
tprintf(terminal, " RAM limit: ");
tprint_bytes(terminal, ram_limit);
tprintf(terminal,"\n");
}
tprintf(terminal, " binary: %s\n", binary_name);
void execute(Command_line &cmd, Terminal::Session &terminal)
{
char name[128];
name[0] = 0;
if (cmd.argument(0, name, sizeof(name)) == false) {
tprintf(terminal, "Error: no configuration name specified\n");
return;
}
char buf[128];
if (cmd.argument(1, buf, sizeof(buf))) {
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
return;
}
Child *child = 0;
try {
child = new (Genode::env()->heap())
Child(_ram, label, binary_name, _cap, ram, ram_limit,
_yield_response_sigh_cap);
}
catch (Genode::Rom_connection::Rom_connection_failed) {
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
binary_name);
return;
}
catch (Child::Quota_exceeded) {
tprintf(terminal, "Error: insufficient memory, need ");
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
tprintf(terminal, ", have ");
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
tprintf(terminal, "\n");
return;
}
catch (Genode::Allocator::Out_of_memory) {
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
return;
}
_subsystem_configs.for_config(name, [&] (Genode::Xml_node node)
{
_execute_subsystem(name, cmd, terminal, node);
});
/* configure child */
try {
Xml_node config_node = _subsystem_node(name).sub_node("config");
child->configure(config_node.addr(), config_node.size());
if (verbose)
tprintf(terminal, " config: inline\n");
} catch (...) {
if (verbose)
tprintf(terminal, " config: none\n");
} catch (Subsystem_config_registry::Nonexistent_subsystem_config) {
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
}
_process_args.list.insert(&child->argument);
_children.insert(child);
child->start();
}
}
List<Argument> &arguments() { return _arguments; }
List<Argument> &arguments() { return _arguments; }
};
#endif /* _START_COMMAND_H_ */

View File

@ -0,0 +1,161 @@
/*
* \brief Registry of subsystem configuration
* \author Norman Feske
* \date 2015-01-27
*/
/*
* Copyright (C) 2015 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 _SUBSYSTEM_CONFIG_REGISTRY_H_
#define _SUBSYSTEM_CONFIG_REGISTRY_H_
/* Genode includes */
#include <vfs/file_system.h>
#include <vfs/vfs_handle.h>
class Subsystem_config_registry
{
public:
/**
* Exception type
*/
class Nonexistent_subsystem_config { };
private:
Vfs::File_system &_fs;
enum { CONFIG_BUF_SIZE = 32*1024 };
char _config_buf[CONFIG_BUF_SIZE];
char const *_subsystems_path() { return "/subsystems"; }
char const *_subsystem_suffix() { return ".subsystem"; }
/**
* Return index of ".subsystem" suffix in dirent name
*
* \return index, or 0 if no matching suffix could be found
*/
unsigned _subsystem_suffix(Vfs::Directory_service::Dirent const &dirent)
{
unsigned found = 0;
for (unsigned i = 0; i < sizeof(dirent.name) && dirent.name[i]; i++)
if (Genode::strcmp(_subsystem_suffix(), &dirent.name[i]) == 0)
found = i;
return found;
}
public:
/**
* Constructor
*/
Subsystem_config_registry(Vfs::File_system &fs)
:
_fs(fs)
{ }
/**
* Execute functor 'fn' for specified subsystem name
*
* The functor is called with the subsystem XML node as argument
*
* \throw Nonexistent_subsystem_config
*/
template <typename FN>
void for_config(char const *name, FN const &fn)
{
/*
* Load subsystem configuration
*/
Genode::Path<256> path(_subsystems_path());
path.append("/");
path.append(name);
path.append(_subsystem_suffix());
Vfs::Vfs_handle *handle = nullptr;
Vfs::Directory_service::Open_result const open_result =
_fs.open(path.base(),
Vfs::Directory_service::OPEN_MODE_RDONLY,
&handle);
Vfs::Vfs_handle::Guard handle_guard(handle);
if (open_result != Vfs::Directory_service::OPEN_OK) {
PERR("could not open '%s', err=%d", path.base(), open_result);
throw Nonexistent_subsystem_config();
}
Vfs::file_size out_count = 0;
Vfs::File_io_service::Read_result read_result =
handle->fs().read(handle, _config_buf, sizeof(_config_buf), out_count);
if (read_result != Vfs::File_io_service::READ_OK) {
PERR("could not read '%s', err=%d", path.base(), read_result);
throw Nonexistent_subsystem_config();
}
try {
Genode::Xml_node subsystem_node(_config_buf, out_count);
fn(subsystem_node);
} catch (Genode::Xml_node::Invalid_syntax) {
PERR("subsystem configuration has invalid syntax");
throw Nonexistent_subsystem_config();
} catch (Genode::Xml_node::Nonexistent_sub_node) {
PERR("invalid subsystem configuration");
throw Nonexistent_subsystem_config();
}
}
/**
* Call specified functor for each subsystem config
*/
template <typename FN>
void for_each_config(FN const &fn)
{
/* iterate over the directory entries */
for (unsigned i = 0;; i++) {
Vfs::Directory_service::Dirent dirent;
Vfs::Directory_service::Dirent_result dirent_result =
_fs.dirent(_subsystems_path(), i, dirent);
if (dirent_result != Vfs::Directory_service::DIRENT_OK) {
PERR("could not access directory '%s'", _subsystems_path());
return;
}
if (dirent.type == Vfs::Directory_service::DIRENT_TYPE_END)
return;
unsigned const subsystem_suffix = _subsystem_suffix(dirent);
/* if file has a matching suffix, apply 'fn' */
if (subsystem_suffix) {
/* subsystem name is file name without the suffix */
char name[sizeof(dirent.name)];
Genode::strncpy(name, dirent.name, subsystem_suffix + 1);
try {
for_config(name, fn);
} catch (Nonexistent_subsystem_config) { }
}
}
}
};
#endif /* _PROCESS_ARG_REGISTRY_H_ */

View File

@ -1,6 +1,6 @@
TARGET = cli_monitor
SRC_CC = main.cc
LIBS = base cli_monitor config
LIBS = base cli_monitor config vfs ld
INC_DIR += $(PRG_DIR)
ifeq ($(findstring arm, $(SPECS)), arm)

View File

@ -16,23 +16,30 @@
/* local includes */
#include <child_registry.h>
#include <process_arg_registry.h>
struct Yield_command : Command
{
Child_registry &_children;
Process_arg_registry &_process_args;
Child_registry &_children;
Yield_command(Child_registry &children, Process_arg_registry &process_args)
Yield_command(Child_registry &children)
:
Command("yield", "instruct subsystem to yield resources"),
_children(children),
_process_args(process_args)
_children(children)
{
add_parameter(new Parameter("--ram", Parameter::NUMBER, "RAM quota to free"));
add_parameter(new Parameter("--greedy", Parameter::VOID, "withdraw yielded RAM quota"));
}
void _for_each_argument(Argument_fn const &fn) const override
{
auto child_name_fn = [&] (char const *child_name) {
Argument arg(child_name, "");
fn(arg);
};
_children.for_each_child_name(child_name_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal)
{
char label[128];
@ -64,8 +71,6 @@ struct Yield_command : Command
tprint_bytes(terminal, ram);
tprintf(terminal, "\n");
}
List<Argument> &arguments() { return _process_args.list; }
};
#endif /* _YIELD_COMMAND_H_ */