Remove os/src/app/cli_monitor

Nowadays, we use standard command-line tools like vim to edit init
configurations dynamically, which alleviates the need for a custom CLI.
The CLI-monitor component was too limited for use cases like Sculpt
anyway.

The patch also removes the ancient (and untested for long time)
terminal_mux.run script, which used to be the only remaining user of the
CLI monitor.

Issue #3512
This commit is contained in:
Norman Feske 2019-10-07 15:23:20 +02:00 committed by Christian Helmuth
parent 4bcc75365c
commit 5bb366513b
21 changed files with 1 additions and 3307 deletions

View File

@ -1,164 +0,0 @@
#
# \brief Example for using the terminal_mux server over UART
# \author Norman Feske
# \date 2013-02-20
#
assert_spec x86
#
# On Linux, we don't have a UART driver, on which this run script depends.
#
if {[have_spec linux]} { puts "Run script does not support Linux"; exit 0 }
set build_components {
core init noux lib/libc_noux app/cli_monitor test/bomb test/signal
test/resource_yield timer drivers/uart server/terminal_mux
server/terminal_log noux-pkg/vim
}
build $build_components
create_boot_directory
append config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="LOG"/>
<service name="RM"/>
<service name="CPU"/>
<service name="PD"/>
<service name="IRQ"/>
<service name="IO_PORT"/>
<service name="IO_MEM"/>
</parent-provides>
<default-route>
<any-service> <any-child/> <parent/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
<route> <any-service> <parent/> </any-service> </route>
</start>
<start name="pc_uart_drv">
<resource name="RAM" quantum="1M"/>
<provides>
<service name="Uart"/>
<service name="Terminal"/>
</provides>
<config>
<policy label_prefix="terminal_mux" uart="1" detect_size="yes"/>
</config>
<route> <any-service> <parent/> <any-child/> </any-service> </route>
</start>
<start name="terminal_mux">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Terminal"/>
</provides>
<route>
<service name="Terminal"> <child name="pc_uart_drv" /> </service>
<any-service> <parent/> <any-child /> </any-service>
</route>
</start>
<start name="terminal_log">
<resource name="RAM" quantum="2M"/>
<provides>
<service name="LOG"/>
</provides>
<route>
<service name="Terminal"> <child name="terminal_mux" /> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="cli_monitor">
<resource name="RAM" quantum="100M"/>
<config>
<preservation name="RAM" quantum="16M" />
<vfs>
<dir name="subsystems">
<inline name="noux.subsystem">
<subsystem name="noux" help="VIM executed in the noux runtime">
<resource name="RAM" quantum="16M" />
<config>
<fstab> <tar name="vim.tar" /> </fstab>
<start name="/bin/vim">
<env name="TERM" value="screen" />
<!-- Deactivate the loading of plugins. Otherwise, vim will
attempt to use a sub shell for pattern matching -->
<arg value="--noplugin" />
<!-- Do not use swap file. Any attempt to create of would
fail because we are on a read-only file system -->
<arg value="-n" />
<!-- Use the nocompatible mode, which is much nicer than
the plain vi mode -->
<arg value="-N" />
<!-- Permanently display status bar -->
<arg value="--cmd" />
<arg value="set laststatus=2" />
<!-- Enable highlighted search results -->
<arg value="--cmd" />
<arg value="set hls" />
</start>
</config>
</subsystem>
</inline>
<inline name="ram_eater.subsystem">
<subsystem name="ram_eater" help="resource-yield test program">
<resource name="RAM" quantum="25M" />
<binary name="test-resource_yield" />
<config child="yes" expand="yes" period_ms="100" />
</subsystem>
</inline>
<inline name="signal.subsystem">
<subsystem name="signal" help="signalling test program">
<resource name="RAM" quantum="5M" />
<binary name="test-signal" />
</subsystem>
</inline>
</dir>
</vfs>
</config>
<route>
<any-service> <child name="terminal_mux" /> <any-child/> <parent/> </any-service>
</route>
</start>
</config>
}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core init timer ld.lib.so noux terminal_mux terminal_log
test-signal cli_monitor test-resource_yield posix.lib.so
libc.lib.so vfs.lib.so libm.lib.so libc_noux.lib.so ncurses.lib.so
vim.tar pc_uart_drv
}
set fiasco_serial_esc_arg ""
build_boot_image $boot_modules
append qemu_args " -nographic "
#
# On all kernels, we write the core debug output to the kdb.log file,
# and use qemu's stdio as the UART used by terminal_mux.
#
append qemu_args " -serial file:kdb.log "
append qemu_args " -serial mon:stdio"
run_genode_until forever

View File

@ -1,355 +0,0 @@
/*
* \brief Child handling
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__CLI_MONITOR__CHILD_H_
#define _INCLUDE__CLI_MONITOR__CHILD_H_
/* Genode includes */
#include <util/list.h>
#include <base/registry.h>
#include <base/child.h>
#include <init/child_policy.h>
#include <os/child_policy_dynamic_rom.h>
#include <cpu_session/connection.h>
#include <pd_session/connection.h>
#include <base/session_label.h>
/* CLI-monitor includes */
#include <cli_monitor/ram.h>
namespace Cli_monitor { class Child_base; }
class Cli_monitor::Child_base : public Genode::Child_policy
{
public:
/*
* XXX derive donated quota from information to be provided by
* the used 'Connection' interfaces
*/
enum { DONATED_RAM_QUOTA = 128*1024 };
class Quota_exceeded : public Genode::Exception { };
typedef Genode::size_t size_t;
typedef Genode::Cap_quota Cap_quota;
typedef Genode::Registered<Genode::Parent_service> Parent_service;
private:
Genode::Env &_env;
Ram &_ram;
Genode::Allocator &_alloc;
Genode::Session_label const _label;
Binary_name const _binary_name;
Genode::Pd_session_capability _ref_pd_cap;
Genode::Pd_session &_ref_pd;
Cap_quota _cap_quota;
size_t _ram_quota;
size_t _ram_limit;
struct Parent_services : Genode::Registry<Parent_service>
{
Genode::Allocator &_alloc;
Parent_services(Genode::Allocator &alloc) : _alloc(alloc) { }
~Parent_services()
{
for_each([&] (Parent_service &s) { Genode::destroy(_alloc, &s); });
}
} _parent_services { _alloc };
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
Genode::Rpc_entrypoint _entrypoint;
Genode::Child_policy_dynamic_rom_file _config_policy;
/**
* If set to true, immediately withdraw resources yielded by the child
*/
bool _withdraw_on_yield_response = false;
/**
* Arguments of current resource request from the child
*/
Genode::Parent::Resource_args _resource_args { };
Genode::Signal_context_capability _yield_response_sigh_cap;
Genode::Signal_context_capability _exit_sig_cap;
/* true if child is scheduled for destruction */
bool _exited = false;
Genode::Child _child;
Genode::Service &_matching_service(Genode::Service::Name const &name,
Genode::Session_label const &label)
{
Genode::Service *service = nullptr;
/* check for config file request */
if ((service = _config_policy.resolve_session_request(name, label)))
return *service;
/* populate session-local parent service registry on demand */
_parent_services.for_each([&] (Parent_service &s) {
if (s.name() == name)
service = &s; });
if (service)
return *service;
return *new (_alloc) Parent_service(_parent_services, _env, name);
}
public:
/**
* Constructor
*
* \param ref_ram used as reference account for the child'd RAM
* session and for allocating the backing store
* for the child's configuration
* \param alloc allocator used to fill parent-service registry
* on demand
*/
Child_base(Genode::Env &env,
Ram &ram,
Genode::Allocator &alloc,
Name const &label,
Binary_name const &binary_name,
Genode::Pd_session &ref_pd,
Genode::Pd_session_capability ref_pd_cap,
Genode::Region_map &local_rm,
Cap_quota cap_quota,
Genode::size_t ram_quota,
Genode::size_t ram_limit,
Genode::Signal_context_capability yield_response_sig_cap,
Genode::Signal_context_capability exit_sig_cap)
:
_env(env), _ram(ram), _alloc(alloc),
_label(label), _binary_name(binary_name),
_ref_pd_cap (ref_pd_cap), _ref_pd (ref_pd),
_cap_quota(cap_quota), _ram_quota(ram_quota), _ram_limit(ram_limit),
_entrypoint(&ref_pd, ENTRYPOINT_STACK_SIZE, _label.string(), false),
_config_policy(local_rm, "config", _entrypoint, &_env.ram()),
_yield_response_sigh_cap(yield_response_sig_cap),
_exit_sig_cap(exit_sig_cap),
_child(local_rm, _entrypoint, *this)
{ }
Genode::Session_label label() const { return _label; }
void configure(char const *config, size_t config_len)
{
_config_policy.load(config, config_len);
}
void start()
{
_entrypoint.activate();
}
/**
* Issue yield request to the child
*/
void yield(size_t amount, bool greedy)
{
if (requested_ram_quota())
return; /* resource request in flight */
char buf[128];
Genode::snprintf(buf, sizeof(buf), "ram_quota=%ld", amount);
_withdraw_on_yield_response = greedy;
_child.yield(buf);
}
/**
* Return amount of RAM currently requested by the child
*/
size_t requested_ram_quota() const
{
return Genode::Arg_string::find_arg(_resource_args.string(), "ram_quota").ulong_value(0);
}
/**
* Withdraw quota from the child
*
* \throw Ram::Transfer_quota_failed
*/
void withdraw_ram_quota(size_t amount)
{
if (!amount)
return;
_ram.withdraw_from(_child.pd_session_cap(), amount);
_ram_quota -= amount;
}
/**
* Upgrade quota of child
*
* \throw Ram::Transfer_quota_failed
*/
void upgrade_ram_quota(size_t amount)
{
_ram.transfer_to(_child.pd_session_cap(), amount);
_ram_quota += amount;
/* wake up child if resource request is in flight */
size_t const req = requested_ram_quota();
if (req && _child.pd().avail_ram().value >= req) {
_child.notify_resource_avail();
/* clear request state */
_resource_args = Genode::Parent::Resource_args("");
}
}
/**
* Try to respond to a current resource request issued by the child
*
* This method evaluates the conditions, under which a resource
* request can be answered: There must be enough room between the
* current quota and the configured limit, and there must be enough
* slack memory available. If both conditions are met, the quota
* of the child gets upgraded.
*/
void try_response_to_resource_request()
{
size_t const req = requested_ram_quota();
if (!req)
return; /* no resource request in flight */
/*
* Respond to the current request if the requested quota fits
* within the limit and if there is enough free quota available.
*/
if (req <= _ram.status().avail && req + _ram_quota <= _ram_limit) {
try { upgrade_ram_quota(req); }
catch (Ram::Transfer_quota_failed) { }
}
}
/**
* Set limit for on-demand RAM quota expansion
*/
void ram_limit(size_t limit)
{
_ram_limit = limit;
try_response_to_resource_request();
}
struct Ram_status
{
size_t quota = 0, limit = 0, xfer = 0, used = 0, avail = 0, req = 0;
Ram_status() { }
Ram_status(size_t quota, size_t limit, size_t xfer, size_t used,
size_t avail, size_t req)
:
quota(quota), limit(limit), xfer(xfer), used(used),
avail(avail), req(req)
{ }
};
/**
* Return RAM quota status of the child
*
* XXX should be a const method, but the 'Pd_session' accessors
* are not const
*/
Ram_status ram_status()
{
return Ram_status(_ram_quota,
_ram_limit,
_ram_quota - _child.pd().ram_quota().value,
_child.pd().used_ram().value,
_child.pd().avail_ram().value,
requested_ram_quota());
}
/**
* Return true if child exited and should be destructed
*/
bool exited() const { return _exited; }
/****************************
** Child_policy interface **
****************************/
Name name() const override { return _label; }
Binary_name binary_name() const override { return _binary_name; }
Genode::Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
Genode::Pd_session &ref_pd() override { return _ref_pd; }
void init(Genode::Pd_session &session, Genode::Pd_session_capability cap) override
{
session.ref_account(_ref_pd_cap);
_ref_pd.transfer_quota(cap, _cap_quota);
_ref_pd.transfer_quota(cap, Genode::Ram_quota{_ram_quota});
}
Route resolve_session_request(Genode::Service::Name const &name,
Genode::Session_label const &label) override
{
return Route { .service = _matching_service(name, label),
.label = label,
.diag = Genode::Session::Diag() };
}
void yield_response() override
{
if (_withdraw_on_yield_response) {
enum { RESERVE = 4*1024*1024 };
size_t amount = _child.pd().avail_ram().value < RESERVE
? 0 : _child.pd().avail_ram().value - RESERVE;
/* try to immediately withdraw freed-up resources */
try { withdraw_ram_quota(amount); }
catch (Ram::Transfer_quota_failed) { }
}
/* propagate yield-response signal */
Genode::Signal_transmitter(_yield_response_sigh_cap).submit();
}
void resource_request(Genode::Parent::Resource_args const &args) override
{
_resource_args = args;
try_response_to_resource_request();
}
void exit(int exit_value) override
{
Genode::log("subsystem \"", name(), "\" exited with value ", exit_value);
_exited = true;
/* trigger destruction of the child */
Genode::Signal_transmitter(_exit_sig_cap).submit();
}
};
#endif /* _INCLUDE__CLI_MONITOR__CHILD_H_ */

View File

@ -1,140 +0,0 @@
/*
* \brief RAM management
* \author Norman Feske
* \date 2013-10-14
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__CLI_MONITOR__RAM_H_
#define _INCLUDE__CLI_MONITOR__RAM_H_
/* Genode includes */
#include <pd_session/client.h>
namespace Cli_monitor { class Ram; }
class Cli_monitor::Ram
{
private:
typedef Genode::size_t size_t;
Genode::Pd_session &_pd;
Genode::Pd_session_capability _pd_cap;
Genode::Lock mutable _lock { };
Genode::Signal_context_capability _yield_sigh;
Genode::Signal_context_capability _resource_avail_sigh;
size_t _preserve;
void _validate_preservation()
{
if (_pd.avail_ram().value < _preserve)
Genode::Signal_transmitter(_yield_sigh).submit();
/* verify to answer outstanding resource requests too */
if (_pd.avail_ram().value > _preserve)
Genode::Signal_transmitter(_resource_avail_sigh).submit();
}
public:
struct Status
{
size_t quota, used, avail, preserve;
Status(size_t quota, size_t used, size_t avail, size_t preserve)
: quota(quota), used(used), avail(avail), preserve(preserve) { }
};
Ram(Genode::Pd_session &pd,
Genode::Pd_session_capability pd_cap,
size_t preserve,
Genode::Signal_context_capability yield_sigh,
Genode::Signal_context_capability resource_avail_sigh)
:
_pd(pd), _pd_cap(pd_cap),
_yield_sigh(yield_sigh),
_resource_avail_sigh(resource_avail_sigh),
_preserve(preserve)
{ }
size_t preserve() const
{
Genode::Lock::Guard guard(_lock);
return _preserve;
}
void preserve(size_t preserve)
{
Genode::Lock::Guard guard(_lock);
_preserve = preserve;
_validate_preservation();
}
Status status() const
{
Genode::Lock::Guard guard(_lock);
return Status(_pd.ram_quota().value, _pd.used_ram().value,
_pd.avail_ram().value, _preserve);
}
void validate_preservation()
{
Genode::Lock::Guard guard(_lock);
_validate_preservation();
}
/**
* Exception type
*/
class Transfer_quota_failed { };
/**
* \throw Transfer_quota_failed
*/
void withdraw_from(Genode::Pd_session_capability from, size_t amount)
{
using namespace Genode;
Lock::Guard guard(_lock);
try { Pd_session_client(from).transfer_quota(_pd_cap, Ram_quota{amount}); }
catch (...) { throw Transfer_quota_failed(); }
Signal_transmitter(_resource_avail_sigh).submit();
}
/**
* \throw Transfer_quota_failed
*/
void transfer_to(Genode::Pd_session_capability to, size_t amount)
{
Genode::Lock::Guard guard(_lock);
if (_pd.avail_ram().value < (_preserve + amount)) {
Genode::Signal_transmitter(_yield_sigh).submit();
throw Transfer_quota_failed();
}
try { _pd.transfer_quota(to, Genode::Ram_quota{amount}); }
catch (...) { throw Transfer_quota_failed(); }
}
size_t avail() const { return _pd.avail_ram().value; }
};
#endif /* _INCLUDE__CLI_MONITOR__RAM_H_ */

View File

@ -1,65 +0,0 @@
/*
* \brief Child carrying application-specific context information
* \author Norman Feske
* \date 2014-10-02
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CHILD_H_
#define _CHILD_H_
/* public CLI-monitor includes */
#include <cli_monitor/child.h>
/* local includes */
#include <line_editor.h>
namespace Cli_monitor { struct Child; }
struct Cli_monitor::Child : Child_base, private List<Child>::Element
{
friend class List<Child>;
using List<Child>::Element::next;
Argument argument;
Child(Genode::Env &env,
Ram &ram,
Genode::Allocator &alloc,
Name const &label,
Binary_name const &binary,
Genode::Pd_session &ref_pd,
Genode::Pd_session_capability ref_pd_cap,
Genode::Region_map &local_rm,
Cap_quota cap_quota,
Genode::size_t ram_quota,
Genode::size_t ram_limit,
Genode::Signal_context_capability yield_response_sig_cap,
Genode::Signal_context_capability exit_sig_cap)
:
Child_base(env,
ram,
alloc,
label,
binary,
ref_pd,
ref_pd_cap,
local_rm,
cap_quota,
ram_quota,
ram_limit,
yield_response_sig_cap,
exit_sig_cap),
argument(label.string(), "subsystem")
{ }
};
#endif /* _CHILD_H_ */

View File

@ -1,83 +0,0 @@
/*
* \brief Registry of running children
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CHILD_REGISTRY_H_
#define _CHILD_REGISTRY_H_
/* Genode includes */
#include <util/list.h>
/* local includes */
#include <child.h>
namespace Cli_monitor { class Child_registry; }
class Cli_monitor::Child_registry : public List<Child>
{
private:
/**
* Return true if a child with the specified name already exists
*/
bool _child_name_exists(const char *label)
{
for (Child *child = first() ; child; child = child->next())
if (child->name() == label)
return true;
return false;
}
public:
enum { CHILD_NAME_MAX_LEN = 64 };
/**
* Produce new unique child name
*/
void unique_child_name(const char *prefix, char *dst, int dst_len)
{
char buf[CHILD_NAME_MAX_LEN];
char suffix[8];
suffix[0] = 0;
for (int cnt = 1; true; cnt++) {
/* build program name composed of prefix and numeric suffix */
snprintf(buf, sizeof(buf), "%s%s", prefix, suffix);
/* if such a program name does not exist yet, we are happy */
if (!_child_name_exists(buf)) {
strncpy(dst, buf, dst_len);
return;
}
/* increase number of suffix */
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

@ -1,173 +0,0 @@
/*
* \brief Utility for command-line parsing
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _COMMAND_LINE_H_
#define _COMMAND_LINE_H_
#include <line_editor.h>
namespace Cli_monitor { class Command_line; }
class Cli_monitor::Command_line
{
private:
char const *_cmd_line;
Command &_command;
bool _parameter_is_known(Token token)
{
return Argument_tracker::lookup(token, _command.parameters()) != 0;
}
Token _tag_token(char const *tag)
{
for (Token token(_cmd_line); token; token = token.next())
if (strcmp(token.start(), tag, token.len()) == 0
&& strlen(tag) == token.len()
&& _parameter_is_known(token))
return token;
return Token();
}
Token _value_token(char const *tag)
{
return _tag_token(tag).next().next();
}
static bool _is_parameter(Token token)
{
return token[0] == '-' && token[1] == '-';
}
public:
/**
* Constructor
*
* \param cmd_line null-terminated command line string
* \param command meta data about the command
*/
Command_line(char const *cmd_line, Command &command)
: _cmd_line(cmd_line), _command(command) { }
/**
* Return true if tag is specified at the command line
*/
bool parameter_exists(char const *tag)
{
return _tag_token(tag);
}
/**
* Return number argument specified for the given tag
*/
template <typename T>
bool parameter(char const *tag, T &result)
{
Token value = _value_token(tag);
return value && Genode::ascii_to(value.start(), result) != 0;
}
/**
* Return string argument specified for the given tag
*/
bool parameter(char const *tag, char *result, size_t result_len)
{
Token value = _value_token(tag);
if (!value)
return false;
value.string(result, result_len);
return true;
}
bool argument(unsigned index, char *result, size_t result_len)
{
Argument_tracker argument_tracker(_command);
/* argument counter */
unsigned cnt = 0;
for (Token token(_cmd_line); token; token = token.next()) {
argument_tracker.supply_token(token);
if (!argument_tracker.valid())
return false;
if (!argument_tracker.expect_arg())
continue;
Token arg = token.next();
if (!arg)
return false;
/*
* The 'arg' token could either the tag of a parameter or
* an argument. We only want to count the arguments. So
* we skip tokens that have the usual form a parameter tag.
*/
if (_is_parameter(arg))
continue;
if (cnt == index) {
arg.string(result, result_len);
return true;
}
cnt++;
}
return false;
}
/**
* Validate parameter tags
*
* \return tag token of first unexpected parameter, or
* invalid token if no unexpected parameter was found
*/
Token unexpected_parameter()
{
Argument_tracker argument_tracker(_command);
for (Token token(_cmd_line); token; token = token.next()) {
argument_tracker.supply_token(token);
if (!argument_tracker.valid())
return token;
if (!argument_tracker.expect_arg())
continue;
Token arg = token.next();
/* ignore non-parameter tokens (i.e., normal arguments) */
if (!_is_parameter(arg))
continue;
/* if parameter with the given tag exists, we are fine */
if (_parameter_is_known(arg))
continue;
/* we hit an unknown parameter tag */
return arg;
}
return Token();
}
};
#endif /* _COMMAND_LINE_H_ */

View File

@ -1,114 +0,0 @@
/*
* \brief Utilities for formatting output to terminal
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FORMAT_UTIL_H_
#define _FORMAT_UTIL_H_
/* local includes */
#include <terminal_util.h>
namespace Cli_monitor {
/**
* Print rational number with two fractional decimals
*/
static inline size_t format_number(char *dst, size_t len, size_t const value,
size_t const quotient, char const *unit)
{
size_t const integer = value / quotient;
size_t const n = snprintf(dst, len, "%ld.", integer);
size_t const remainder = ((value - (integer * quotient))*100) / quotient;
if (len == n) return n;
return n + snprintf(dst + n, len - n, "%s%ld%s",
remainder < 10 ? "0" : "", remainder, unit);
}
/**
* Print number of bytes using the best suitable unit
*/
static inline size_t format_bytes(char *dst, size_t len, size_t bytes)
{
enum { KB = 1024, MB = 1024*KB };
if (bytes > MB)
return format_number(dst, len, bytes, MB, " MiB");
if (bytes > KB)
return format_number(dst, len, bytes, KB, " KiB");
return snprintf(dst, len, "%ld bytes", bytes);
}
/**
* Print number in MiB, without unit
*/
static inline size_t format_mib(char *dst, size_t len, size_t bytes)
{
enum { KB = 1024, MB = 1024*KB };
return format_number(dst, len, bytes, MB , "");
}
static inline size_t format_bytes(size_t bytes)
{
char buf[128];
return format_bytes(buf, sizeof(buf), bytes);
}
static inline size_t format_mib(size_t bytes)
{
char buf[128];
return format_mib(buf, sizeof(buf), bytes);
}
static inline void tprint_bytes(Terminal::Session &terminal, size_t bytes)
{
char buf[128];
format_bytes(buf, sizeof(buf), bytes);
Terminal::tprintf(terminal, "%s", buf);
}
static inline void tprint_mib(Terminal::Session &terminal, size_t bytes)
{
char buf[128];
format_mib(buf, sizeof(buf), bytes);
Terminal::tprintf(terminal, "%s", buf);
}
static inline void tprint_status_bytes(Terminal::Session &terminal,
char const *label, size_t bytes)
{
Terminal::tprintf(terminal, label);
tprint_bytes(terminal, bytes);
Terminal::tprintf(terminal, "\n");
}
static void tprint_padding(Terminal::Session &terminal, size_t pad, char c = ' ')
{
char const buf[2] = { c, 0 };
for (unsigned i = 0; i < pad; i++)
Terminal::tprintf(terminal, buf);
}
}
#endif /* _FORMAT_UTIL_H_ */

View File

@ -1,31 +0,0 @@
/*
* \brief Help command
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _HELP_COMMAND_H_
#define _HELP_COMMAND_H_
namespace Cli_monitor { struct Help_command; }
struct Cli_monitor::Help_command : Command
{
Help_command() : Command("help", "brief help information") { }
void execute(Command_line &, Terminal::Session &terminal) override
{
tprintf(terminal, " Press [tab] for a list of commands.\n");
tprintf(terminal, " When given a command, press [tab] for a list of arguments.\n");
}
};
#endif /* _HELP_COMMAND_H_ */

View File

@ -1,84 +0,0 @@
/*
* \brief Kill command
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _KILL_COMMAND_H_
#define _KILL_COMMAND_H_
/* local includes */
#include <child_registry.h>
namespace Cli_monitor { struct Kill_command; }
struct Cli_monitor::Kill_command : Command
{
Child_registry &_children;
Genode::Allocator &_alloc;
Parameter _kill_all_param { "--all", Parameter::VOID, "kill all subsystems" };
void _destroy_child(Child *child, Terminal::Session &terminal)
{
tprintf(terminal, "destroying subsystem '%s'\n", child->name().string());
_children.remove(child);
Genode::destroy(_alloc, child);
}
Kill_command(Child_registry &children, Genode::Allocator &alloc)
:
Command("kill", "destroy subsystem"), _children(children), _alloc(alloc)
{
add_parameter(_kill_all_param);
}
void _for_each_argument(Argument_fn const &fn) const override
{
auto child_name_fn = [&] (Child_base::Name const &child_name) {
Argument arg(child_name.string(), "");
fn(arg);
};
_children.for_each_child_name(child_name_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal) override
{
bool const kill_all = cmd.parameter_exists("--all");
if (kill_all) {
for (Child *child = _children.first(); child; child = _children.first())
_destroy_child(child, terminal);
return;
}
char label[128];
label[0] = 0;
if (cmd.argument(0, label, sizeof(label)) == false) {
tprintf(terminal, "Error: no subsystem name specified\n");
return;
}
/* lookup child by its unique name */
for (Child *child = _children.first(); child; child = child->next()) {
if (child->name() == label) {
_destroy_child(child, terminal);
return;
}
}
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
}
};
#endif /* _KILL_COMMAND_H_ */

View File

@ -1,890 +0,0 @@
/*
* \brief Line editor for command-line interfaces
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _LINE_EDITOR_H_
#define _LINE_EDITOR_H_
/* Genode includes */
#include <terminal_session/connection.h>
#include <util/string.h>
#include <util/list.h>
#include <util/token.h>
#include <util/misc_math.h>
#include <base/snprintf.h>
namespace Cli_monitor {
using Genode::List;
using Genode::max;
using Genode::strlen;
using Genode::strncpy;
using Genode::snprintf;
using Genode::strcmp;
using Genode::size_t;
using Genode::off_t;
using Genode::Interface;
struct Completable;
struct Argument;
struct Parameter;
struct Command_line;
struct Command;
struct Command_registry;
struct Scanner_policy;
struct Argument_tracker;
struct Line_editor;
typedef Genode::Token<Scanner_policy> Token;
}
struct Cli_monitor::Completable
{
typedef Genode::String<64> Name;
typedef Genode::String<160> Short_help;
Name const _name;
Short_help const _short_help;
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) { }
};
/**
* Representation of normal command-line argument
*/
struct Cli_monitor::Argument : Completable
{
Argument(char const *name, char const *short_help)
: Completable(name, short_help) { }
char const *name_suffix() const { return ""; }
};
/**
* Representation of a parameter of the form '--tag value'
*/
struct Cli_monitor::Parameter : List<Parameter>::Element, Completable
{
enum Type { IDENT, NUMBER, VOID };
Type const _type;
Parameter(char const *name, Type type, char const *short_help)
:
Completable(name, short_help), _type(type)
{ }
bool needs_value() const { return _type != VOID; }
char const *name_suffix() const
{
switch (_type) {
case IDENT: return "<identifier>";
case NUMBER: return "<number>";
case VOID: return "";
}
return "";
}
};
/**
* Representation of a command that can have arguments and parameters
*/
struct Cli_monitor::Command : private List<Command>::Element,
private Completable
{
List<Parameter> _parameters { };
friend class List<Command>;
using List<Command>::Element::next;
using Completable::name;
using Completable::short_help;
/**
* Functor that takes a command 'Argument' object as argument
*/
struct Argument_fn : Interface
{
virtual void operator () (Argument const &) const = 0;
};
Command(char const *name, char const *short_help)
: Completable(name, short_help) { }
virtual ~Command() { }
void add_parameter(Parameter &par) { _parameters.insert(&par); }
char const *name_suffix() const { return ""; }
List<Parameter> &parameters() { return _parameters; }
virtual void execute(Command_line &, Terminal::Session &terminal) = 0;
/**
* Command-specific support for 'for_each_argument'
*/
virtual void _for_each_argument(Argument_fn const &) 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);
}
};
struct Cli_monitor::Command_registry : List<Command> { };
/**
* Scanner policy that accepts '-', '.' and '_' as valid identifier characters
*/
struct Cli_monitor::Scanner_policy
{
static bool identifier_char(char c, unsigned i)
{
return Genode::is_letter(c) || (c == '_') || (c == '-') || (c == '.')
|| (i && Genode::is_digit(c));
}
};
/**
* State machine used for sequentially parsing command-line arguments
*/
struct Cli_monitor::Argument_tracker
{
private:
Command &_command;
enum State { EXPECT_COMMAND, EXPECT_SPACE_BEFORE_ARG, EXPECT_ARG,
EXPECT_SPACE_BEFORE_VAL, EXPECT_VAL, INVALID };
State _state;
/**
* Return true if there is exactly one complete match and no additional
* partial matches
*/
static bool _one_matching_argument(char const *str, size_t str_len,
Command const &command)
{
unsigned complete_cnt = 0, partial_cnt = 0;
auto argument_fn = [&] (Argument const &arg) {
if (strcmp(arg.name().string(), str, str_len) == 0) {
partial_cnt++;
if (strlen(arg.name().string()) == str_len)
complete_cnt++;
}
};
command.for_each_argument(argument_fn);
return partial_cnt == 1 && complete_cnt == 1;
}
public:
Argument_tracker(Command &command)
: _command(command), _state(EXPECT_COMMAND) { }
template <typename T>
static T *lookup(char const *str, size_t str_len,
List<T> &list)
{
Token tag(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next())
if (strcmp(tag.start(), curr->name().string(), tag.len()) == 0
&& strlen(curr->name().string()) == tag.len())
return curr;
return 0;
}
template <typename T>
static T *lookup(Token token, List<T> &list)
{
return lookup(token.start(), token.len(), list);
}
void supply_token(Token token, bool token_may_be_incomplete = false)
{
switch (_state) {
case INVALID: break;
case EXPECT_COMMAND:
if (token.type() == Token::IDENT) {
_state = EXPECT_SPACE_BEFORE_ARG;
break;
}
_state = INVALID;
break;
case EXPECT_SPACE_BEFORE_ARG:
if (token.type() == Token::WHITESPACE)
_state = EXPECT_ARG;
break;
case EXPECT_ARG:
if (token.type() == Token::IDENT) {
Parameter *parameter =
lookup(token.start(), token.len(), _command.parameters());
if (parameter && parameter->needs_value()) {
_state = EXPECT_SPACE_BEFORE_VAL;
break;
}
if (!token_may_be_incomplete
|| _one_matching_argument(token.start(), token.len(), _command))
_state = EXPECT_SPACE_BEFORE_ARG;
}
break;
case EXPECT_SPACE_BEFORE_VAL:
if (token.type() == Token::WHITESPACE)
_state = EXPECT_VAL;
break;
case EXPECT_VAL:
if (token.type() == Token::IDENT
|| token.type() == Token::NUMBER) {
_state = EXPECT_SPACE_BEFORE_ARG;
}
break;
}
}
bool valid() const { return _state != INVALID; }
bool expect_arg() const { return _state == EXPECT_ARG; }
bool expect_space() const { return _state == EXPECT_SPACE_BEFORE_ARG
|| _state == EXPECT_SPACE_BEFORE_VAL; }
};
/**
* Editing and completion logic
*/
class Cli_monitor::Line_editor
{
private:
char const *_prompt;
size_t const _prompt_len;
char * const _buf;
size_t const _buf_size;
unsigned _cursor_pos = 0;
Terminal::Session &_terminal;
Command_registry &_commands;
bool _complete = false;
/**
* State tracker for escape sequences within user input
*
* This tracker is used to decode special keys (e.g., cursor keys).
*/
struct Seq_tracker
{
enum State { INIT, GOT_ESC, GOT_FIRST } _state = INIT;
char _normal = 0, _first = 0, _second = 0;
bool _sequence_complete { false };
Seq_tracker() { }
void input(char c)
{
switch (_state) {
case INIT:
if (c == 27)
_state = GOT_ESC;
else
_normal = c;
_sequence_complete = false;
break;
case GOT_ESC:
_first = c;
_state = GOT_FIRST;
break;
case GOT_FIRST:
_second = c;
_state = INIT;
_sequence_complete = true;
break;
}
}
bool normal() const { return _state == INIT && !_sequence_complete; }
char normal_char() const { return _normal; }
bool _fn_complete(char match_first, char match_second) const
{
return _sequence_complete
&& _first == match_first
&& _second == match_second;
}
bool key_up() const { return _fn_complete(91, 65); }
bool key_down() const { return _fn_complete(91, 66); }
bool key_right() const { return _fn_complete(91, 67); }
bool key_left() const { return _fn_complete(91, 68); }
bool key_delete() const { return _fn_complete(91, 51); }
};
Seq_tracker _seq_tracker { };
void _write(char c) { _terminal.write(&c, sizeof(c)); }
void _write(char const *s) { _terminal.write(s, strlen(s)); }
void _write_spaces(unsigned num)
{
for (unsigned i = 0; i < num; i++)
_write(' ');
}
void _write_newline() { _write(10); }
void _clear_until_end_of_line() { _write("\e[K "); }
void _move_cursor_to(unsigned pos)
{
char seq[10];
snprintf(seq, sizeof(seq), "\e[%ldG", pos + _prompt_len);
_write(seq);
}
void _delete_character()
{
strncpy(&_buf[_cursor_pos], &_buf[_cursor_pos + 1], _buf_size);
_move_cursor_to(_cursor_pos);
_write(&_buf[_cursor_pos]);
_clear_until_end_of_line();
_move_cursor_to(_cursor_pos);
}
void _insert_character(char c)
{
/* insert regular character */
if (_cursor_pos >= _buf_size - 1)
return;
/* make room in the buffer */
for (unsigned i = _buf_size - 1; i > _cursor_pos; i--)
_buf[i] = _buf[i - 1];
_buf[_cursor_pos] = c;
/* update terminal */
_write(&_buf[_cursor_pos]);
_cursor_pos++;
_move_cursor_to(_cursor_pos);
}
void _fresh_prompt()
{
_write(_prompt);
_write(_buf);
_move_cursor_to(_cursor_pos);
}
void _handle_key()
{
enum { BACKSPACE = 8,
TAB = 9,
LINE_FEED = 10,
CARRIAGE_RETURN = 13 };
if (_seq_tracker.key_left()) {
if (_cursor_pos > 0) {
_cursor_pos--;
_write(BACKSPACE);
}
return;
}
if (_seq_tracker.key_right()) {
if (_cursor_pos < strlen(_buf)) {
_cursor_pos++;
_move_cursor_to(_cursor_pos);
}
return;
}
if (_seq_tracker.key_delete())
_delete_character();
if (!_seq_tracker.normal())
return;
char const c = _seq_tracker.normal_char();
if (c == TAB) {
_perform_completion();
return;
}
if (c == CARRIAGE_RETURN || c == LINE_FEED) {
if (strlen(_buf) > 0) {
_write(LINE_FEED);
_complete = true;
}
return;
}
if (c == BACKSPACE) {
if (_cursor_pos > 0) {
_cursor_pos--;
_delete_character();
}
return;
}
if (c == 126)
return;
_insert_character(c);
}
template <typename COMPLETABLE>
COMPLETABLE *_lookup_matching(char const *str, size_t str_len,
List<COMPLETABLE> &list)
{
Token tag(str, str_len);
COMPLETABLE *curr = list.first();
for (; curr; curr = curr->next()) {
if (strcmp(tag.start(), curr->name(), tag.len()) == 0
&& strlen(curr->name()) == tag.len())
return curr;
}
return nullptr;
}
Command *_lookup_matching_command()
{
Token cmd(_buf, _cursor_pos);
for (Command *curr = _commands.first(); curr; curr = curr->next())
if (strcmp(cmd.start(), curr->name().string(), cmd.len()) == 0
&& _cursor_pos > cmd.len())
return curr;
return nullptr;
}
template <typename T>
unsigned _num_partial_matches(char const *str, size_t str_len, List<T> &list)
{
Token token(str, str_len);
unsigned num_partial_matches = 0;
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name().string(), token.len()) != 0)
continue;
num_partial_matches++;
}
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
*/
template <typename T>
size_t _width_of_partial_matches(char const *str, size_t str_len,
List<T> &list)
{
Token token(str, str_len);
size_t max_name_len = 0;
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name().string(), token.len()) != 0)
continue;
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)
{
Token token(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next())
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)
{
Token token(str, str_len);
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name().string(), token.len()) != 0)
continue;
_write_newline();
_write_spaces(2);
_write(curr->name().string());
_write_spaces(1);
_write(curr->name_suffix());
/* pad short help with whitespaces */
size_t const name_len = strlen(curr->name().string())
+ strlen(curr->name_suffix());
_write_spaces(pad + 3 - name_len);
_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)
{
Token token(str, str_len);
/* look up completable token */
T *partial_match = 0;
for (T *curr = list.first(); curr; curr = curr->next()) {
if (strcmp(token.start(), curr->name().string(), token.len()) == 0) {
partial_match = curr;
break;
}
}
if (!partial_match)
return;
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(' ');
}
void _complete_argument(char const *str, size_t str_len, Command &command)
{
unsigned const matching_parameters =
_num_partial_matches(str, str_len, command.parameters());
unsigned const matching_arguments =
_num_matching_arguments(str, str_len, command);
/* matches are ambiguous */
if (matching_arguments + matching_parameters > 1) {
/*
* Try to complete additional characters that are common among
* all matches.
*/
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());
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++) {
buf[i + 0] = name[i];
buf[i + 1] = 0;
if (matching_parameters !=
_num_partial_matches(buf, i + 1, command.parameters()))
break;
if (matching_arguments !=
_num_matching_arguments(buf, i + 1, command))
break;
_insert_character(buf[i]);
}
/*
* If we managed to do a partial completion, let's yield
* control to the user.
*/
if (i > str_len)
return;
/*
* No automatic completion was possible, print list of possible
* parameters and arguments
*/
size_t const pad =
max(_width_of_partial_matches(str, str_len, command.parameters()),
_width_of_matching_arguments(str, str_len, command));
_list_partial_matches(str, str_len, pad, command.parameters());
_list_matching_arguments(str, str_len, pad, command);
_write_newline();
_fresh_prompt();
return;
}
if (matching_parameters == 1)
_do_completion(str, str_len, command.parameters());
if (matching_arguments == 1)
_do_argument_completion(str, str_len, command);
}
void _perform_completion()
{
Command *command = _lookup_matching_command();
if (!command) {
unsigned const matches = _num_partial_matches(_buf, _cursor_pos, _commands);
if (matches == 1)
_do_completion(_buf, _cursor_pos, _commands);
if (matches > 1) {
unsigned const pad =
_width_of_partial_matches(_buf, _cursor_pos, _commands);
_list_partial_matches(_buf, _cursor_pos, pad, _commands);
_write_newline();
_fresh_prompt();
}
return;
}
/*
* We hava a valid command, now try to complete the parameters...
*/
/* determine token under the cursor */
Argument_tracker argument_tracker(*command);
Token token(_buf, _buf_size);
for (; token; token = token.next()) {
argument_tracker.supply_token(token, true);
if (!argument_tracker.valid())
return;
unsigned long const token_pos = (unsigned long)(token.start() - _buf);
/* we have reached the token under the cursor */
if (token.type() == Token::IDENT
&& _cursor_pos >= token_pos
&& _cursor_pos <= token_pos + token.len()) {
if (argument_tracker.expect_arg()) {
char const *start = token.start();
size_t const len = _cursor_pos - token_pos;
_complete_argument(start, len, *command);
return;
}
}
}
/* the cursor is positioned at beginning of new argument */
if (argument_tracker.expect_arg())
_complete_argument("", 0, *command);
if (argument_tracker.expect_space())
_insert_character(' ');
}
public:
/**
* Constructor
*
* \param prompt string to be printed at the beginning of the line
* \param buf destination buffer
* \param buf_size destination buffer size
* \param terminal terminal used as output device
* \param commands meta information about commands and their arguments
*/
Line_editor(char const *prompt, char *buf, size_t buf_size,
Terminal::Session &terminal, Command_registry &commands)
:
_prompt(prompt), _prompt_len(strlen(prompt)),
_buf(buf), _buf_size(buf_size),
_terminal(terminal), _commands(commands)
{
reset();
}
/**
* Reset prompt to initial state after construction
*/
void reset()
{
_buf[0] = 0;
_complete = false;
_cursor_pos = 0;
_seq_tracker = Seq_tracker();
_fresh_prompt();
}
/**
* Supply a character of user input
*/
void submit_input(char c)
{
_seq_tracker.input(c);
_handle_key();
}
/**
* Returns true if the editing is complete, i.e., the user pressed the
* return key.
*/
bool completed() const { return _complete; }
};
#endif /* _LINE_EDITOR_H_ */

View File

@ -1,225 +0,0 @@
/*
* \brief Simple command-line interface for managing Genode subsystems
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <vfs/simple_env.h>
#include <base/component.h>
/* public CLI-monitor includes */
#include <cli_monitor/ram.h>
/* local includes */
#include <line_editor.h>
#include <command_line.h>
#include <format_util.h>
#include <status_command.h>
#include <kill_command.h>
#include <start_command.h>
#include <help_command.h>
#include <yield_command.h>
#include <ram_command.h>
namespace Cli_monitor {
struct Main;
using namespace Genode;
}
/******************
** Main program **
******************/
struct Cli_monitor::Main
{
Genode::Env &_env;
Terminal::Connection _terminal { _env };
Command_registry _commands { };
Child_registry _children { };
Command *_lookup_command(char const *buf)
{
Token token(buf);
for (Command *curr = _commands.first(); curr; curr = curr->next())
if (strcmp(token.start(), curr->name().string(), token.len()) == 0
&& strlen(curr->name().string()) == token.len())
return curr;
return 0;
}
enum { COMMAND_MAX_LEN = 1000 };
char _command_buf[COMMAND_MAX_LEN];
Line_editor _line_editor {
"genode> ", _command_buf, sizeof(_command_buf), _terminal, _commands };
void _handle_terminal_read_avail();
Signal_handler<Main> _terminal_read_avail_handler {
_env.ep(), *this, &Main::_handle_terminal_read_avail };
/**
* Handler for child yield responses, or RAM resource-avail signals
*/
void _handle_yield_response()
{
for (Child *child = _children.first(); child; child = child->next())
child->try_response_to_resource_request();
}
Signal_handler<Main> _yield_response_handler {
_env.ep(), *this, &Main::_handle_yield_response };
void _handle_child_exit()
{
Child *next = nullptr;
for (Child *child = _children.first(); child; child = next) {
next = child->next();
if (child->exited()) {
_children.remove(child);
Genode::destroy(_heap, child);
}
}
}
Signal_handler<Main> _child_exit_handler {
_env.ep(), *this, &Main::_handle_child_exit };
void _handle_yield_broadcast()
{
/*
* Compute argument of yield request to be broadcasted to all
* processes.
*/
size_t amount = 0;
/* amount needed to reach preservation limit */
Ram::Status ram_status = _ram.status();
if (ram_status.avail < ram_status.preserve)
amount += ram_status.preserve - ram_status.avail;
/* sum of pending resource requests */
for (Child *child = _children.first(); child; child = child->next())
amount += child->requested_ram_quota();
for (Child *child = _children.first(); child; child = child->next())
child->yield(amount, true);
}
Signal_handler<Main> _yield_broadcast_handler {
_env.ep(), *this, &Main::_handle_yield_broadcast };
Genode::Attached_rom_dataspace _config { _env, "config" };
Xml_node _vfs_config() const
{
try { return _config.xml().sub_node("vfs"); }
catch (Genode::Xml_node::Nonexistent_sub_node) {
Genode::error("missing '<vfs>' configuration");
throw;
}
}
size_t _ram_preservation_from_config() const
{
if (!_config.xml().has_sub_node("preservation"))
return 0;
return _config.xml().sub_node("preservation")
.attribute_value("name", Genode::Number_of_bytes(0));
}
Ram _ram { _env.pd(), _env.pd_session_cap(), _ram_preservation_from_config(),
_yield_broadcast_handler, _yield_response_handler };
Heap _heap { _env.ram(), _env.rm() };
Vfs::Simple_env _vfs_env { _env, _heap, _vfs_config() };
Subsystem_config_registry _subsystem_config_registry { _vfs_env.root_dir(), _heap, _env.ep() };
template <typename T>
struct Registered : T
{
template <typename... ARGS>
Registered(Command_registry &commands, ARGS &&... args)
: T(args...) { commands.insert(this); }
};
/* initialize generic commands */
Registered<Help_command> _help_command { _commands };
Registered<Kill_command> _kill_command { _commands, _children, _heap };
Registered<Start_command> _start_command { _commands, _env, _ram, _heap,
_env.pd(), _env.pd_session_cap(),
_env.rm(), _children,
_subsystem_config_registry,
_yield_response_handler,
_child_exit_handler };
Registered<Status_command> _status_command { _commands, _ram, _children };
Registered<Yield_command> _yield_command { _commands, _children };
Registered<Ram_command> _ram_command { _commands, _children, _ram };
Main(Env &env) : _env(env)
{
_terminal.read_avail_sigh(_terminal_read_avail_handler);
}
};
void Cli_monitor::Main::_handle_terminal_read_avail()
{
/* supply pending terminal input to line editor */
while (_terminal.avail() && !_line_editor.completed()) {
char c = 0;
_terminal.read(&c, 1);
_line_editor.submit_input(c);
}
if (!_line_editor.completed())
return;
Command *command = _lookup_command(_command_buf);
if (!command) {
Token cmd_name(_command_buf);
tprintf(_terminal, "Error: unknown command \"");
_terminal.write(cmd_name.start(), cmd_name.len());
tprintf(_terminal, "\"\n");
_line_editor.reset();
return;
}
/* validate parameters against command meta data */
Command_line cmd_line(_command_buf, *command);
Token unexpected = cmd_line.unexpected_parameter();
if (unexpected) {
tprintf(_terminal, "Error: unexpected parameter \"");
_terminal.write(unexpected.start(), unexpected.len());
tprintf(_terminal, "\"\n");
_line_editor.reset();
return;
}
command->execute(cmd_line, _terminal);
/*
* The command might result in a change of the RAM usage. Validate
* that the preservation is satisfied.
*/
_ram.validate_preservation();
_line_editor.reset();
}
void Component::construct(Genode::Env &env) { static Cli_monitor::Main main(env); }

View File

@ -1,17 +0,0 @@
/*
* \brief Dummy implementation of CLI extension interface
* \author Norman Feske
* \date 2013-03-21
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <extension.h>
void Cli_monitor::init_extension(Command_registry &) { }

View File

@ -1,135 +0,0 @@
/*
* \brief RAM command
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _RAM_COMMAND_H_
#define _RAM_COMMAND_H_
/* local includes */
#include <child_registry.h>
namespace Cli_monitor { struct Ram_command; }
struct Cli_monitor::Ram_command : Command
{
Child_registry &_children;
Ram &_ram;
Parameter _quota_param { "--quota", Parameter::NUMBER, "new RAM quota" };
Parameter _limit_param { "--limit", Parameter::NUMBER, "on-demand quota limit" };
Ram_command(Child_registry &children, Ram &ram)
:
Command("ram", "set RAM quota of subsystem"),
_children(children), _ram(ram)
{
add_parameter(_quota_param);
add_parameter(_limit_param);
}
void _set_quota(Terminal::Session &terminal, Child &child, size_t const new_quota)
{
size_t const old_quota = child.ram_status().quota;
if (new_quota > old_quota) {
size_t amount = new_quota - old_quota;
size_t const avail = _ram.avail();
if (amount > avail) {
tprintf(terminal, "upgrade of '%s' exceeds available quota of ",
child.name().string());
tprint_bytes(terminal, avail);
tprintf(terminal, "\n");
amount = avail;
}
tprintf(terminal, "upgrading quota of '%s' to ", child.name().string());
tprint_bytes(terminal, old_quota + amount);
tprintf(terminal, "\n");
try {
child.upgrade_ram_quota(amount); }
catch (Ram::Transfer_quota_failed) {
tprintf(terminal, "Error: transfer_quota failed\n"); }
} if (new_quota < old_quota) {
size_t amount = old_quota - new_quota;
size_t const avail = child.ram_status().avail;
if (amount > avail) {
tprintf(terminal, "withdrawal of ");
tprint_bytes(terminal, amount);
tprintf(terminal, " exceeds available quota of ");
tprint_bytes(terminal, avail);
tprintf(terminal, "\n");
amount = avail;
}
tprintf(terminal, "depleting quota of '%s' to ", child.name().string());
tprint_bytes(terminal, old_quota - amount);
tprintf(terminal, "\n");
try {
child.withdraw_ram_quota(amount); }
catch (Ram::Transfer_quota_failed) {
tprintf(terminal, "Error: transfer_quota failed\n"); }
}
}
void _for_each_argument(Argument_fn const &fn) const override
{
auto child_name_fn = [&] (Child_base::Name const &child_name) {
Argument arg(child_name.string(), "");
fn(arg);
};
_children.for_each_child_name(child_name_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal) override
{
char label[128];
label[0] = 0;
if (cmd.argument(0, label, sizeof(label)) == false) {
tprintf(terminal, "Error: no subsystem name specified\n");
return;
}
/* lookup child by its unique name */
Child *child = _children.first();
for (; child; child = child->next())
if (child->name() == label)
break;
if (!child) {
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
return;
}
bool const limit_specified = cmd.parameter_exists("--limit");
Genode::Number_of_bytes limit = 0;
if (limit_specified) {
cmd.parameter("--limit", limit);
child->ram_limit(limit);
}
if (cmd.parameter_exists("--quota")) {
Genode::Number_of_bytes quota = 0;
cmd.parameter("--quota", quota);
_set_quota(terminal, *child, quota);
}
}
};
#endif /* _RAM_COMMAND_H_ */

View File

@ -1,255 +0,0 @@
/*
* \brief Start command
* \author Norman Feske
* \date 2013-03-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _START_COMMAND_H_
#define _START_COMMAND_H_
/* Genode includes */
#include <util/xml_node.h>
/* local includes */
#include <subsystem_config_registry.h>
namespace Cli_monitor { class Start_command; }
class Cli_monitor::Start_command : public Command
{
private:
typedef Genode::Xml_node Xml_node;
typedef Genode::Signal_context_capability Signal_context_capability;
typedef Genode::Dataspace_capability Dataspace_capability;
Genode::Env &_env;
Ram &_ram;
Genode::Allocator &_alloc;
Child_registry &_children;
Genode::Pd_session &_ref_pd;
Genode::Pd_session_capability _ref_pd_cap;
Genode::Region_map &_local_rm;
Subsystem_config_registry &_subsystem_configs;
List<Argument> _arguments { };
Signal_context_capability _yield_response_sigh_cap;
Signal_context_capability _exit_sig_cap;
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;
size_t caps = subsystem_node.attribute_value("caps", 0UL);
/* 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) > _ram.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.
*/
typedef Genode::String<128> Binary_name;
Binary_name binary_name;
try {
Xml_node bin = subsystem_node.sub_node("binary");
binary_name = bin.attribute_value("name", 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.string());
}
Child *child = 0;
try {
child = new (_alloc)
Child(_env, _ram, _alloc, label, binary_name,
_ref_pd, _ref_pd_cap, _local_rm,
Genode::Cap_quota{caps}, ram, ram_limit,
_yield_response_sigh_cap, _exit_sig_cap);
}
catch (Genode::Service_denied) {
tprintf(terminal, "Error: could not start child \"%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, _ram.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");
config_node.with_raw_node([&] (char const *start, size_t length) {
child->configure(start, length); });
if (verbose)
tprintf(terminal, " config: inline\n");
} catch (...) {
if (verbose)
tprintf(terminal, " config: none\n");
}
_children.insert(child);
child->start();
}
}
Parameter _count_param { "--count", Parameter::NUMBER, "number of instances" };
Parameter _ram_param { "--ram", Parameter::NUMBER, "initial RAM quota" };
Parameter _ram_limit_param { "--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota" };
Parameter _verbose_param { "--verbose", Parameter::VOID, "show diagnostics" };
public:
Start_command(Genode::Env &env,
Ram &ram,
Genode::Allocator &alloc,
Genode::Pd_session &ref_pd,
Genode::Pd_session_capability ref_pd_cap,
Genode::Region_map &local_rm,
Child_registry &children,
Subsystem_config_registry &subsustem_configs,
Signal_context_capability yield_response_sigh_cap,
Signal_context_capability exit_sig_cap)
:
Command("start", "create new subsystem"),
_env(env), _ram(ram), _alloc(alloc), _children(children),
_ref_pd(ref_pd), _ref_pd_cap(ref_pd_cap), _local_rm(local_rm),
_subsystem_configs(subsustem_configs),
_yield_response_sigh_cap(yield_response_sigh_cap),
_exit_sig_cap(exit_sig_cap)
{
add_parameter(_count_param);
add_parameter(_ram_param);
add_parameter(_ram_limit_param);
add_parameter(_verbose_param);
}
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) {
if (!node.has_attribute("name")) {
Genode::warning("Missing name in '<subsystem>' configuration");
return;
}
typedef Genode::String<64> Name;
Name const name = node.attribute_value("name", Name());
char const *prefix = "config: ";
size_t const prefix_len = strlen(prefix);
char help[Parameter::Short_help::size() + prefix_len];
strncpy(help, prefix, ~0);
try {
Genode::Xml_attribute const help_attr = node.attribute("help");
help_attr.with_raw_value([&] (char const *start, size_t len) {
strncpy(help + prefix_len, start,
Genode::min(len, Parameter::Short_help::size())); });
}
catch (Xml_node::Nonexistent_attribute) {
Genode::warning("Missing help in '<subsystem>' configuration");
return;
}
Argument arg(name.string(), help);
fn(arg);
};
/* scan subsystem config registry for possible subsystem arguments */
_subsystem_configs.for_each_config(process_subsystem_config_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal) override
{
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;
}
try {
_subsystem_configs.for_config(name, [&] (Genode::Xml_node node)
{
_execute_subsystem(name, cmd, terminal, node);
});
} catch (Subsystem_config_registry::Nonexistent_subsystem_config) {
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
}
}
List<Argument> &arguments() { return _arguments; }
};
#endif /* _START_COMMAND_H_ */

View File

@ -1,157 +0,0 @@
/*
* \brief Status command
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _STATUS_COMMAND_H_
#define _STATUS_COMMAND_H_
/* local includes */
#include <table.h>
#include <child_registry.h>
namespace Cli_monitor { struct Status_command; }
struct Cli_monitor::Status_command : Command
{
Child_registry &_children;
Ram &_ram;
Status_command(Ram &ram, Child_registry &children)
:
Command("status", "show runtime status"),
_children(children), _ram(ram)
{ }
void execute(Command_line &, Terminal::Session &terminal) override
{
using Terminal::tprintf;
Ram::Status const ram_status = _ram.status();
tprint_status_bytes(terminal, " RAM quota: ", ram_status.quota);
tprint_status_bytes(terminal, " used: ", ram_status.used);
tprint_status_bytes(terminal, " avail: ", ram_status.avail);
tprint_status_bytes(terminal, " preserve: ", ram_status.preserve);
tprintf(terminal, "\n");
struct Child_info
{
enum Column { NAME, QUOTA, LIMIT, XFER, USED, AVAIL, STATUS };
constexpr static size_t num_columns() { return STATUS + 1; }
char const *name = 0;
Child::Ram_status ram_status { };
static char const *label(Column column)
{
switch (column) {
case NAME: return "process";
case QUOTA: return "quota";
case LIMIT: return "limit";
case XFER: return "xfer";
case USED: return "alloc";
case AVAIL: return "avail";
case STATUS: return "status";
};
return "";
}
size_t len(Column column) const
{
switch (column) {
case NAME: return strlen(name);
case QUOTA: return format_mib(ram_status.quota);
case LIMIT:
return ram_status.limit ? format_mib(ram_status.limit) : 0;
case XFER: return format_mib(ram_status.xfer);
case USED: return format_mib(ram_status.used);
case AVAIL: return format_mib(ram_status.avail);
case STATUS:
{
size_t const req = ram_status.req;
return req ? strlen("req ") + format_mib(req) : 0;
}
};
return 0;
}
static bool left_aligned(Column column)
{
switch (column) {
case NAME: return true;
case QUOTA: return false;
case LIMIT: return false;
case XFER: return false;
case USED: return false;
case AVAIL: return false;
case STATUS: return true;
};
return false;
}
void print_cell(Terminal::Session &terminal, Column column)
{
switch (column) {
case NAME: tprintf(terminal, "%s", name); break;
case QUOTA: tprint_mib(terminal, ram_status.quota); break;
case LIMIT:
if (ram_status.limit)
tprint_mib(terminal, ram_status.limit);
break;
case XFER: tprint_mib(terminal, ram_status.xfer); break;
case USED: tprint_mib(terminal, ram_status.used); break;
case AVAIL: tprint_mib(terminal, ram_status.avail); break;
case STATUS:
if (ram_status.req) {
tprintf(terminal, "req ");
tprint_mib(terminal, ram_status.req);
}
break;
};
}
Child_info() { }
Child_info(char const *name, Child::Ram_status ram_status)
:
name(name), ram_status(ram_status)
{ }
};
/*
* Take snapshot of child information.
*/
size_t num_children = 0;
for (Child *c = _children.first(); c; c = c->next())
num_children++;
Child_info child_info[num_children];
unsigned i = 0;
for (Child *c = _children.first(); c && i < num_children; c = c->next(), i++)
child_info[i] = Child_info(c->name().string(), c->ram_status());
/*
* Print table
*/
if (num_children) {
Table<Child_info>::print(terminal, child_info, num_children);
tprintf(terminal, "\n");
}
}
};
#endif /* _STATUS_COMMAND_H_ */

View File

@ -1,195 +0,0 @@
/*
* \brief Registry of subsystem configuration
* \author Norman Feske
* \date 2015-01-27
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SUBSYSTEM_CONFIG_REGISTRY_H_
#define _SUBSYSTEM_CONFIG_REGISTRY_H_
/* Genode includes */
#include <vfs/file_system.h>
#include <vfs/vfs_handle.h>
namespace Cli_monitor { class Subsystem_config_registry; }
class Cli_monitor::Subsystem_config_registry
{
public:
/**
* Exception type
*/
class Nonexistent_subsystem_config { };
private:
Vfs::File_system &_fs;
Genode::Allocator &_alloc;
Genode::Entrypoint &_ep;
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.buf) && dirent.name.buf[i]; i++)
if (Genode::strcmp(_subsystem_suffix(), &dirent.name.buf[i]) == 0)
found = i;
return found;
}
public:
/**
* Constructor
*/
Subsystem_config_registry(Vfs::File_system &fs, Genode::Allocator &alloc,
Genode::Entrypoint &ep)
:
_fs(fs), _alloc(alloc), _ep(ep)
{ }
/**
* 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)
{
using Genode::error;
/*
* 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, _alloc);
Vfs::Vfs_handle::Guard handle_guard(handle);
if (open_result != Vfs::Directory_service::OPEN_OK) {
error("could not open '", path, "', err=", (int)open_result);
throw Nonexistent_subsystem_config();
}
Vfs::file_size out_count = 0;
handle->fs().queue_read(handle, sizeof(_config_buf));
Vfs::File_io_service::Read_result read_result;
while ((read_result =
handle->fs().complete_read(handle, _config_buf,
sizeof(_config_buf),
out_count)) ==
Vfs::File_io_service::READ_QUEUED)
_ep.wait_and_dispatch_one_io_signal();
if (read_result != Vfs::File_io_service::READ_OK) {
error("could not read '", path, "', err=", (int)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) {
error("subsystem configuration has invalid syntax");
throw Nonexistent_subsystem_config();
} catch (Genode::Xml_node::Nonexistent_sub_node) {
error("invalid subsystem configuration");
throw Nonexistent_subsystem_config();
}
}
/**
* Call specified functor for each subsystem config
*/
template <typename FN>
void for_each_config(FN const &fn)
{
using Genode::error;
Vfs::Vfs_handle *dir_handle;
if (_fs.opendir(_subsystems_path(), false, &dir_handle, _alloc) !=
Vfs::Directory_service::OPENDIR_OK) {
error("could not access directory '", _subsystems_path(), "'");
return;
}
/* iterate over the directory entries */
for (unsigned i = 0;; i++) {
Vfs::Directory_service::Dirent dirent { };
dir_handle->seek(i * sizeof(dirent));
dir_handle->fs().queue_read(dir_handle, sizeof(dirent));
Vfs::file_size out_count;
while (dir_handle->fs().complete_read(dir_handle,
(char*)&dirent,
sizeof(dirent),
out_count) ==
Vfs::File_io_service::READ_QUEUED)
_ep.wait_and_dispatch_one_io_signal();
if (dirent.type == Vfs::Directory_service::Dirent_type::END) {
_fs.close(dir_handle);
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.buf)];
Genode::strncpy(name, dirent.name.buf, subsystem_suffix + 1);
try {
for_config(name, fn);
} catch (Nonexistent_subsystem_config) { }
}
}
_fs.close(dir_handle);
}
};
#endif /* _PROCESS_ARG_REGISTRY_H_ */

View File

@ -1,101 +0,0 @@
/*
* \brief Utility for printing a table to the terminal
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _TABLE_H_
#define _TABLE_H_
#include <terminal_session/terminal_session.h>
namespace Cli_monitor { template <typename TI> class Table; }
template <typename TI>
class Cli_monitor::Table
{
private:
static void _print_cell(TI &info, Terminal::Session &terminal,
typename TI::Column column, size_t column_size)
{
size_t const padding = column_size - info.len(column);
if (!TI::left_aligned(column))
tprint_padding(terminal, padding);
info.print_cell(terminal, column);
if (TI::left_aligned(column))
tprint_padding(terminal, padding);
}
/**
* Print centered title of table column
*/
static void _print_label(Terminal::Session &terminal,
typename TI::Column column, size_t column_size)
{
size_t const padding = column_size - strlen(TI::label(column));
size_t const left_padding = padding / 2;
tprint_padding(terminal, left_padding);
tprintf(terminal, "%s", TI::label(column));
tprint_padding(terminal, padding - left_padding);
}
public:
static void print(Terminal::Session &terminal, TI info[], unsigned num_rows)
{
/*
* Determine formatting of table
*/
size_t column_size[TI::num_columns()];
for (unsigned i = 0; i < TI::num_columns(); i++)
column_size[i] = strlen(TI::label((typename TI::Column)i));
for (unsigned i = 0; i < num_rows; i++) {
for (unsigned j = 0; j < TI::num_columns(); j++)
column_size[j] = max(column_size[j],
info[i].len((typename TI::Column)j));
}
/*
* Print table
*/
tprintf(terminal, " ");
for (unsigned j = 0; j < TI::num_columns(); j++) {
_print_label(terminal, (typename TI::Column)j, column_size[j]);
if (j < TI::num_columns() - 1) tprintf(terminal, " | ");
}
tprintf(terminal, "\n");
tprintf(terminal, " ");
for (unsigned j = 0; j < TI::num_columns(); j++) {
for (unsigned i = 0; i < column_size[j]; i++)
tprintf(terminal, "-");
if (j < TI::num_columns() - 1) tprintf(terminal, "-+-");
}
tprintf(terminal, "\n");
for (unsigned i = 0; i < num_rows; i++) {
tprintf(terminal, " ");
for (unsigned j = 0; j < TI::num_columns(); j++) {
_print_cell(info[i], terminal, (typename TI::Column)j, column_size[j]);
if (j < TI::num_columns() - 1) tprintf(terminal, " | ");
}
tprintf(terminal, "\n");
}
}
};
#endif /* _TABLE_H_ */

View File

@ -1,4 +0,0 @@
TARGET = cli_monitor
SRC_CC = main.cc
LIBS = base vfs
INC_DIR += $(PRG_DIR)

View File

@ -1,36 +0,0 @@
/*
* \brief Convenience functions for operating on a terminal session
* \author Norman Feske
* \date 2013-03-19
*/
#ifndef _TERMINAL_UTIL_H_
#define _TERMINAL_UTIL_H_
/* Genode includes */
#include <terminal_session/terminal_session.h>
#include <base/snprintf.h>
namespace Terminal {
static inline void tprintf(Session &terminal, const char *format_args, ...)
{
using namespace Genode;
enum { MAX_LEN = 256 };
char buf[MAX_LEN];
/* process format string */
va_list list;
va_start(list, format_args);
String_console sc(buf, MAX_LEN);
sc.vprintf(format_args, list);
va_end(list);
terminal.write(buf, strlen(buf));
}
}
#endif /* _TERMINAL_UTIL_H_ */

View File

@ -1,82 +0,0 @@
/*
* \brief Yield command
* \author Norman Feske
* \date 2013-10-05
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _YIELD_COMMAND_H_
#define _YIELD_COMMAND_H_
/* local includes */
#include <child_registry.h>
namespace Cli_monitor { struct Yield_command; }
struct Cli_monitor::Yield_command : Command
{
Child_registry &_children;
Parameter _ram_param { "--ram", Parameter::NUMBER, "RAM quota to free" };
Parameter _greedy_param { "--greedy", Parameter::VOID, "withdraw yielded RAM quota" };
Yield_command(Child_registry &children)
:
Command("yield", "instruct subsystem to yield resources"),
_children(children)
{
add_parameter(_ram_param);
add_parameter(_greedy_param);
}
void _for_each_argument(Argument_fn const &fn) const override
{
auto child_name_fn = [&] (Child_base::Name const &child_name) {
Argument arg(child_name.string(), "");
fn(arg);
};
_children.for_each_child_name(child_name_fn);
}
void execute(Command_line &cmd, Terminal::Session &terminal) override
{
char label[128];
label[0] = 0;
if (cmd.argument(0, label, sizeof(label)) == false) {
tprintf(terminal, "Error: no subsystem name specified\n");
return;
}
Genode::Number_of_bytes ram = 0;
cmd.parameter("--ram", ram);
bool const greedy = cmd.parameter_exists("--greedy");
/* lookup child by its unique name */
Child *child = _children.first();
for (; child; child = child->next())
if (child->name() == label)
break;
if (!child) {
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
return;
}
child->yield(ram, greedy);
tprintf(terminal, "requesting '%s' to yield ", child->name().string());
tprint_bytes(terminal, ram);
tprintf(terminal, "\n");
}
};
#endif /* _YIELD_COMMAND_H_ */

View File

@ -18,7 +18,7 @@ the file "/log".
! <provides><service name="LOG"/></provides>
! <config>
! <policy label_prefix="nic_drv" truncate="no"/>
! <policy label_prefix="cli_monitor -> " merge="yes"/>
! <policy label_prefix="runtime -> " merge="yes"/>
! <default-policy truncate="yes"/>
! </config>
! </start>