diff --git a/repos/os/src/app/cli_monitor/child_registry.h b/repos/os/src/app/cli_monitor/child_registry.h index 83b00f09a..d52327087 100644 --- a/repos/os/src/app/cli_monitor/child_registry.h +++ b/repos/os/src/app/cli_monitor/child_registry.h @@ -63,6 +63,18 @@ class Child_registry : public List snprintf(suffix, sizeof(suffix), ".%d", cnt + 1); } } + + /** + * Call functor 'fn' for each child + * + * The functor receives the child name as 'char const *'. + */ + template + 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_ */ diff --git a/repos/os/src/app/cli_monitor/gdb_command.h b/repos/os/src/app/cli_monitor/gdb_command.h index e6a070a5f..4f87a4af9 100644 --- a/repos/os/src/app/cli_monitor/gdb_command.h +++ b/repos/os/src/app/cli_monitor/gdb_command.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include 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 _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(), + 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()); + + if (elf_binary.is_dynamically_linked()) { + + snprintf(gdb_main_breakpoint_args_buf, + sizeof(gdb_main_breakpoint_args_buf), + "\n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n \ + \n", + binary_name); + } else { + + snprintf(gdb_main_breakpoint_args_buf, + sizeof(gdb_main_breakpoint_args_buf), + "\n \ + \n \ + \n \ + \n", + binary_name); + } + } catch (...) { + throw Binary_elf_check_failed(); + } + + /* + * Insert the '' node for 'noux' + */ + + config_bytes_written += snprintf(&config_buf[config_bytes_written], + sizeof(config_buf) - config_bytes_written, "\n \ + \n \ + \n \ + \n \ + \n \ + \n \ + %s \ + \n \ + ", + 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 '' node for 'gdb_monitor' + */ + + config_bytes_written += Genode::snprintf(&config_buf[config_bytes_written], + (sizeof(config_buf) - config_bytes_written), "\n \ + \n \ + \n \ + %s \ + \n \ + \n \ + \ + ", + 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 '' 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 '' 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 '' 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(), - 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()); + _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), - "\n \ - \n \ - \n \ - \n \ - \n \ - \n \ - \n \ - \n \ - \n \ - \n", - binary_name); - } else { - - snprintf(gdb_main_breakpoint_args_buf, - sizeof(gdb_main_breakpoint_args_buf), - "\n \ - \n \ - \n \ - \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 '' node for 'noux' - */ - - config_bytes_written += snprintf(&config_buf[config_bytes_written], - sizeof(config_buf) - config_bytes_written, "\n \ - \n \ - \n \ - \n \ - \n \ - \n \ - %s \ - \n \ - ", - 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 '' node for 'gdb_monitor' - */ - - config_bytes_written += Genode::snprintf(&config_buf[config_bytes_written], - (sizeof(config_buf) - config_bytes_written), "\n \ - \n \ - \n \ - %s \ - \n \ - \n \ - \ - ", - 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 '' 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 &arguments() { return _arguments; } }; #endif /* _GDB_COMMAND_H_ */ diff --git a/repos/os/src/app/cli_monitor/kill_command.h b/repos/os/src/app/cli_monitor/kill_command.h index ec1a0758e..7d9b5b1d8 100644 --- a/repos/os/src/app/cli_monitor/kill_command.h +++ b/repos/os/src/app/cli_monitor/kill_command.h @@ -16,31 +16,36 @@ /* local includes */ #include -#include 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 &arguments() { return _process_args.list; } }; #endif /* _KILL_COMMAND_H_ */ diff --git a/repos/os/src/app/cli_monitor/line_editor.h b/repos/os/src/app/cli_monitor/line_editor.h index edbda312e..7fa08aae9 100644 --- a/repos/os/src/app/cli_monitor/line_editor.h +++ b/repos/os/src/app/cli_monitor/line_editor.h @@ -35,18 +35,14 @@ using Genode::off_t; struct Completable { - template 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 const _name; - String 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::Element, Completable +struct Argument : Completable { Argument(char const *name, char const *short_help) : Completable(name, short_help) { } @@ -103,6 +99,14 @@ struct Command : List::Element, Completable { List _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::Element, Completable List ¶meters() { return _parameters; } - /** - * To be overridden by commands that accept auto-completion of arguments - */ - virtual List &arguments() - { - static List 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 + 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 - static bool _one_match(char const *str, size_t str_len, - List &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 char const *_any_partial_match_name(char const *str, size_t str_len, List &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 void _list_partial_matches(char const *str, size_t str_len, unsigned pad, List &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 void _do_completion(char const *str, size_t str_len, List &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() diff --git a/repos/os/src/app/cli_monitor/main.cc b/repos/os/src/app/cli_monitor/main.cc index d0a29ff15..64df9e700 100644 --- a/repos/os/src/app/cli_monitor/main.cc +++ b/repos/os/src/app/cli_monitor/main.cc @@ -14,6 +14,8 @@ /* Genode includes */ #include #include +#include +#include /* public CLI-monitor includes */ #include @@ -48,8 +50,8 @@ static inline Command *lookup_command(char const *buf, Command_registry ®istr { 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 '' 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(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(); diff --git a/repos/os/src/app/cli_monitor/process_arg_registry.h b/repos/os/src/app/cli_monitor/process_arg_registry.h deleted file mode 100644 index 2ec5b5cdc..000000000 --- a/repos/os/src/app/cli_monitor/process_arg_registry.h +++ /dev/null @@ -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 list; -}; - -#endif /* _PROCESS_ARG_REGISTRY_H_ */ diff --git a/repos/os/src/app/cli_monitor/ram_command.h b/repos/os/src/app/cli_monitor/ram_command.h index 7f4d103ac..7d3c471c5 100644 --- a/repos/os/src/app/cli_monitor/ram_command.h +++ b/repos/os/src/app/cli_monitor/ram_command.h @@ -16,18 +16,15 @@ /* local includes */ #include -#include 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 &arguments() { return _process_args.list; } }; #endif /* _RAM_COMMAND_H_ */ diff --git a/repos/os/src/app/cli_monitor/start_command.h b/repos/os/src/app/cli_monitor/start_command.h index 8f3b11362..48bf65249 100644 --- a/repos/os/src/app/cli_monitor/start_command.h +++ b/repos/os/src/app/cli_monitor/start_command.h @@ -17,201 +17,206 @@ /* Genode includes */ #include -struct Start_command : Command +/* local includes */ +#include + +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 _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 _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 '' 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 '' 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 '' 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 '' 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 &arguments() { return _arguments; } + List &arguments() { return _arguments; } }; #endif /* _START_COMMAND_H_ */ diff --git a/repos/os/src/app/cli_monitor/subsystem_config_registry.h b/repos/os/src/app/cli_monitor/subsystem_config_registry.h new file mode 100644 index 000000000..83bd5ccfa --- /dev/null +++ b/repos/os/src/app/cli_monitor/subsystem_config_registry.h @@ -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 +#include + +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 + 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 + 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_ */ diff --git a/repos/os/src/app/cli_monitor/target.mk b/repos/os/src/app/cli_monitor/target.mk index 1f3fe5304..aa16e27fc 100644 --- a/repos/os/src/app/cli_monitor/target.mk +++ b/repos/os/src/app/cli_monitor/target.mk @@ -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) diff --git a/repos/os/src/app/cli_monitor/yield_command.h b/repos/os/src/app/cli_monitor/yield_command.h index 13b99011a..24159e56e 100644 --- a/repos/os/src/app/cli_monitor/yield_command.h +++ b/repos/os/src/app/cli_monitor/yield_command.h @@ -16,23 +16,30 @@ /* local includes */ #include -#include 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 &arguments() { return _process_args.list; } }; #endif /* _YIELD_COMMAND_H_ */