mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-07 19:34:56 +00:00
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:
parent
4bcc75365c
commit
5bb366513b
@ -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
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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> ¶meters() { 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_ */
|
@ -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); }
|
@ -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 &) { }
|
||||
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -1,4 +0,0 @@
|
||||
TARGET = cli_monitor
|
||||
SRC_CC = main.cc
|
||||
LIBS = base vfs
|
||||
INC_DIR += $(PRG_DIR)
|
@ -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_ */
|
@ -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_ */
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user