genode/repos/os/src/lib/vfs/terminal_file_system.h

295 lines
7.2 KiB
C++

/*
* \brief Terminal file system
* \author Christian Prochaska
* \author Norman Feske
* \author Christian Helmuth
* \date 2012-05-23
*/
/*
* Copyright (C) 2012-2019 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__VFS__TERMINAL_FILE_SYSTEM_H_
#define _INCLUDE__VFS__TERMINAL_FILE_SYSTEM_H_
#include <terminal_session/connection.h>
#include <vfs/single_file_system.h>
#include <vfs/dir_file_system.h>
#include <vfs/readonly_value_file_system.h>
#include <util/xml_generator.h>
namespace Vfs { class Terminal_file_system; }
struct Vfs::Terminal_file_system
{
typedef String<64> Name;
struct Local_factory;
struct Data_file_system;
struct Compound_file_system;
};
/**
* File system node for processing the terminal data read/write streams
*/
class Vfs::Terminal_file_system::Data_file_system : public Single_file_system
{
private:
Name const _name;
Genode::Entrypoint &_ep;
Terminal::Connection &_terminal;
struct Terminal_vfs_handle : Single_vfs_handle
{
Terminal::Connection &terminal;
bool notifying { false };
bool blocked { false };
Terminal_vfs_handle(Terminal::Connection &term,
Directory_service &ds,
File_io_service &fs,
Genode::Allocator &alloc,
int flags)
:
Single_vfs_handle(ds, fs, alloc, flags),
terminal(term)
{ }
bool read_ready() override {
return terminal.avail(); }
Read_result read(char *dst, file_size count,
file_size &out_count) override
{
if (!terminal.avail()) {
blocked = true;
return READ_QUEUED;
}
out_count = terminal.read(dst, count);
return READ_OK;
}
Write_result write(char const *src, file_size count,
file_size &out_count) override
{
out_count = terminal.write(src, count);
return WRITE_OK;
}
};
typedef Genode::Registered<Terminal_vfs_handle> Registered_handle;
typedef Genode::Registry<Registered_handle> Handle_registry;
Handle_registry _handle_registry { };
Genode::Io_signal_handler<Data_file_system> _read_avail_handler {
_ep, *this, &Data_file_system::_handle_read_avail };
void _handle_read_avail()
{
_handle_registry.for_each([this] (Registered_handle &handle) {
if (handle.blocked) {
handle.blocked = false;
handle.io_progress_response();
}
if (handle.notifying) {
handle.notifying = false;
handle.read_ready_response();
}
});
}
public:
Data_file_system(Genode::Entrypoint &ep,
Terminal::Connection &terminal,
Name const &name)
:
Single_file_system(Node_type::TRANSACTIONAL_FILE, name.string(),
Node_rwx::rw(), Genode::Xml_node("<data/>")),
_name(name), _ep(ep), _terminal(terminal)
{
/* register for read-avail notification */
_terminal.read_avail_sigh(_read_avail_handler);
}
static const char *name() { return "data"; }
char const *type() override { return "data"; }
Open_result open(char const *path, unsigned flags,
Vfs_handle **out_handle,
Allocator &alloc) override
{
if (!_single_file(path))
return OPEN_ERR_UNACCESSIBLE;
try {
*out_handle = new (alloc)
Registered_handle(_handle_registry, _terminal, *this, *this, alloc, flags);
return OPEN_OK;
}
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
}
/********************************
** File I/O service interface **
********************************/
bool notify_read_ready(Vfs_handle *vfs_handle) override
{
Terminal_vfs_handle *handle =
static_cast<Terminal_vfs_handle*>(vfs_handle);
if (!handle)
return false;
handle->notifying = true;
return true;
}
Ftruncate_result ftruncate(Vfs_handle *, file_size) override
{
return FTRUNCATE_OK;
}
bool check_unblock(Vfs_handle *, bool rd, bool wr, bool) override
{
return ((rd && _terminal.avail()) || wr);
}
};
struct Vfs::Terminal_file_system::Local_factory : File_system_factory
{
typedef Genode::String<64> Label;
Label const _label;
Name const _name;
Genode::Env &_env;
Terminal::Connection _terminal { _env, _label.string() };
Data_file_system _data_fs { _env.ep(), _terminal, _name };
struct Info
{
Terminal::Session::Size size;
void print(Genode::Output &out) const
{
char buf[128] { };
Genode::Xml_generator xml(buf, sizeof(buf), "terminal", [&] () {
xml.attribute("rows", size.lines());
xml.attribute("columns", size.columns());
});
Genode::print(out, Genode::Cstring(buf));
}
};
Readonly_value_file_system<Info> _info_fs { "info", Info{} };
Readonly_value_file_system<unsigned> _rows_fs { "rows", 0 };
Readonly_value_file_system<unsigned> _columns_fs { "columns", 0 };
Genode::Io_signal_handler<Local_factory> _size_changed_handler {
_env.ep(), *this, &Local_factory::_handle_size_changed };
void _handle_size_changed()
{
Info const info { .size = _terminal.size() };
_info_fs .value(info);
_rows_fs .value(info.size.lines());
_columns_fs.value(info.size.columns());
}
static Name name(Xml_node config)
{
return config.attribute_value("name", Name("terminal"));
}
Local_factory(Vfs::Env &env, Xml_node config)
:
_label(config.attribute_value("label", Label(""))),
_name(name(config)),
_env(env.env())
{
_terminal.size_changed_sigh(_size_changed_handler);
_handle_size_changed();
}
Vfs::File_system *create(Vfs::Env&, Xml_node node) override
{
if (node.has_type("data")) return &_data_fs;
if (node.has_type("info")) return &_info_fs;
if (node.has_type("rows")) return &_rows_fs;
if (node.has_type("columns")) return &_columns_fs;
return nullptr;
}
};
class Vfs::Terminal_file_system::Compound_file_system : private Local_factory,
public Vfs::Dir_file_system
{
private:
typedef Terminal_file_system::Name Name;
typedef String<200> Config;
static Config _config(Name const &name)
{
char buf[Config::capacity()] { };
/*
* By not using the node type "dir", we operate the
* 'Dir_file_system' in root mode, allowing multiple sibling nodes
* to be present at the mount point.
*/
Genode::Xml_generator xml(buf, sizeof(buf), "compound", [&] () {
xml.node("data", [&] () {
xml.attribute("name", name); });
xml.node("dir", [&] () {
xml.attribute("name", Name(".", name));
xml.node("info", [&] () {});
xml.node("rows", [&] () {});
xml.node("columns", [&] () {});
});
});
return Config(Genode::Cstring(buf));
}
public:
Compound_file_system(Vfs::Env &vfs_env, Genode::Xml_node node)
:
Local_factory(vfs_env, node),
Vfs::Dir_file_system(vfs_env,
Xml_node(_config(Local_factory::name(node)).string()),
*this)
{ }
static const char *name() { return "terminal"; }
char const *type() override { return name(); }
};
#endif /* _INCLUDE__VFS__TERMINAL_FILE_SYSTEM_H_ */