l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

2
src/l4/pkg/cons/Control Normal file
View File

@@ -0,0 +1,2 @@
requires: stdlibs libstdc++ cxx_libc_io cxx_io l4virtio
Maintainer: adam@os.inf.tu-dresden.de

View File

@@ -0,0 +1,10 @@
menu "cons console multiplexer"
config CONS_USE_ASYNC_FE
bool "Support and use asynchronous interface"
help
This enables an asynchronous interface for cons using threads.
Only enable this if you know what you are doing. If in doubt select N.
endmenu

View File

@@ -0,0 +1,23 @@
## This file states the license of this package and possibly its subpackages
## in machine and human readable format. The PackageName refers to the package
## whose license is defined by PackageLicenseConcluded.
## For more information about this file format visit the SPDX website at
## https://spdx.org
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentNamespace: spdx:kernkonzept/cons-049d582b-e400-4632-8664-ee21b589b74e
DocumentName: cons
Creator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
Created: 2018-12-21T00:00:00Z
## Package Information
PackageName: cons
SPDXID: SPDXRef-cons
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only
PackageLicenseConcluded: GPL-2.0-only
FilesAnalyzed: false
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION

4
src/l4/pkg/cons/Makefile Normal file
View File

@@ -0,0 +1,4 @@
PKGDIR = .
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/subdir.mk

21
src/l4/pkg/cons/README.md Normal file
View File

@@ -0,0 +1,21 @@
# L4Re console multiplexer
cons is the console multiplexer for the L4Re operating system. It allows to
multiplex console output from different clients to different in and output
servers.
# Documentation
This package is part of the L4Re operating system. For documentation and
build instructions see the
[L4Re wiki](https://kernkonzept.com/L4Re/guides/l4re).
# Contributions
We welcome contributions. Please see our contributors guide on
[how to contribute](https://kernkonzept.com/L4Re/contributing/l4re).
# License
Detailed licensing and copyright information can be found in the
[LICENSE](LICENSE.spdx) file.

View File

@@ -0,0 +1,29 @@
# Security Policy
This document outlines security procedures for the open-source projects of the
L4Re Operating System Framework as found on https://github.com/kernkonzept.
# Reporting a vulnerability
Security is very important to us and we take all security vulnerabilities
seriously. Thank you for improving the security of our open source software. If
you have discovered a security issue, we appreciate your efforts and your
responsible disclosure.
Please report a security vulnerability by sending an encrypted email to our
security team using our [public
key](https://www.kernkonzept.com/dl/security-at-kernkonzept.pub)
to **security@kernkonzept.com**. The fingerprint of our public key is
````
C4DC 2909 A22E D080 C012 5373 4055 CBA2 A4FD 855B
````
Please include the following in your report:
* A description of the vulnerability
* Steps to reproduce the vulnerability
A member of Kernkonzept's security team will confirm the vulnerability,
determine its impact, and develop a fix. The fix will be applied to the master
branch, tested, and released.

View File

@@ -0,0 +1,6 @@
// vim:set ft=cpp: -*- Mode: C++ -*-
/**
* \page l4re_servers L4Re Servers
*
* - \subpage l4re_servers_cons
*/

View File

@@ -0,0 +1,2 @@
INPUT += %PKGDIR%/doc/cons.dox
INPUT += %PKGDIR%/doc/usage.md

View File

@@ -0,0 +1,143 @@
# Cons, the Console Multiplexer {#l4re_servers_cons}
`cons` is an interactive multiplexer for console input and output. It buffers the
output from different %L4 clients and allows to switch between them to redirect
input.
## Multiplexers and Frontends
cons is able to connect multiple clients to multiple console I/O servers. All
clients are connected to all configured multiplexers
Multiplexers and frontends come in pairs where the actual I/O is handled by the
frontend. From the point-of-view of cons, a frontend consists of an IPC channel
to a server that speaks an appropriate server protocol. By default the
L4.Env.log capability is used. Only frontends that speak the L4::Vcon protocol
are supported.
A multiplexer handles and routes the I/O of each client to and from its
respective frontend.
Each client's console settings (e.g. color, visibility) apply to all
multiplexers and cannot be changed individually. A user can connect to all
clients through all multiplexers. It is not possible to assign individual
clients to distinct multiplexers.
## Client sessions
For clients cons implements the L4::Vcon and the Virtio console interface. A
client session can be requested through a `create` call to cons' factory. cons
binds its factory capability to the `cons` capability. See the example below on
how this can be set up.
## Starting the service
The cons server can be started with Lua like this:
local log_server = L4.default_loader:new_channel()
L4.default_loader:start({
caps = {
cons = log_server:svr(),
},
log = L4.Env.log,
},
"rom/cons")
First an IPC gate (`log_server`) is created which is used between the cons
server and a client to request a new session. The server side is assigned to the
mandatory `cons` capability of cons. This example explicitly assigns the
kernel's log capability (`L4.Env.log`) to cons' `log` capability in order to
allow cons to provide input and output for its clients. The default log factory
(usually provided by `moe`) doesn't provide input capabilities.
## Command Line Options
cons accepts the following command line switches:
* `-a`, `--show-all`
Initially show output from all clients.
* `-B <size>`, `--defaultbufsize <size>`
Default buffer size per client in bytes. Default: 40960
* `-c <client>`, `--autoconnect <client>`
Automatically connect to the client with the given name.
That means that output of this client will be visible and
input will be routed to it.
* `-f <cap>`, `--frontend <cap>`
Set the frontend for the current multiplexer. Output for the multiplexer
is then sent to the capability with the given name `<cap>`. The server
connected to the capability needs to understand the L4::Vcon protocol.
* `-k`, `--keep`
Keep the console buffer when a client disconnects.
* `-l`, `--no-line-buffering`
By default, merge the client output to entire lines. If the client writes
characters without a final newline, the following client output is merged
with the current line content. Specifying this switch disables the line
buffered mode by default.
* `--line-buffering-ms <timeout>`
Timeout in milliseconds before buffered client output is written even
without a newline. Default value is 50.
* `-m <prompt name>`, `--mux <prompt name>`
Add a new multiplexer named `<prompt name>`. This is necessary if output
should be sent to different frontends. This option must be used in conjunction
with the `-f` frontend option
* `-n`, `--defaultname`
Default name for the multiplexer prompt. Default: `cons`.
* `-t`, `--timestamp`
Prefix the output with timestamps.
## Connecting a client
create(backend_type, ["client_name"], ["color"], ["option"] [,"option"] ...)
* `backend_type`
The type of backend that should be created for the client. The type is a
positive integer and currently the following types are supported:
* `L4.Proto.Log`: L4::Vcon client
* `1`: Virtio console client
cons accepts the following per-client options:
* `bufsz=n`
Use a buffer of `n` bytes for this client, deviating from the default
buffer size.
* `keep` / `no-keep`
The console buffer is kept / thrown away when the client disconnects.
* `key=<key>`
Assign `<key>` as keyboard shortcut to this client.
* `line-buffering` / `no-linux-buffering`
Line buffering is enabled / disabled for this client.
* `show` / `hide`
Output from this client is initially shown / hidden.
* `timestamp` / `no-timestamp`
Do / do not prefix the output of this client with timestamps.

View File

@@ -0,0 +1,4 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,13 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET := cons
SRC_CC := controller.cc mux_impl.cc main.cc client.cc vcon_client.cc \
vcon_fe_base.cc vcon_fe.cc registry.cc virtio_client.cc
SRC_CC-$(CONFIG_CONS_USE_ASYNC_FE) += async_vcon_fe.cc
REQUIRES_LIBS = libstdc++ cxx_libc_io cxx_io
REQUIRES_LIBS-$(CONFIG_CONS_USE_ASYNC_FE) = libpthread
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,57 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "async_vcon_fe.h"
#include <l4/re/error_helper>
#include <pthread.h>
#include <cstdio>
using L4Re::chksys;
void *Async_vcon_fe::setup()
{
int err = l4_error(_vcon->bind(0, obj_cap()));
if (err == -L4_EBADPROTO)
printf("WARNING: frontend without input\n");
// we just do not care about errors here too
l4_vcon_attr_t attr = { 0, 0, 0 };
_vcon->get_attr(&attr);
attr.l_flags &= ~(L4_VCON_ECHO | L4_VCON_ICANON);
attr.o_flags &= ~(L4_VCON_ONLCR | L4_VCON_OCRNL | L4_VCON_ONLRET);
attr.o_flags |= L4_VCON_ONLCR;
attr.i_flags &= ~(L4_VCON_INLCR | L4_VCON_IGNCR | L4_VCON_ICRNL);
_vcon->set_attr(&attr);
handle_pending_input();
_initialized = true;
return NULL;
}
void *Async_vcon_fe::_setup(void *_self)
{
Async_vcon_fe *self = static_cast<Async_vcon_fe*>(_self);
return self->setup();
}
Async_vcon_fe::Async_vcon_fe(L4::Cap<L4::Vcon> con, L4Re::Util::Object_registry *r)
: Vcon_fe_base(con, r), _initialized(false)
{
pthread_t tid;
pthread_create(&tid, NULL, Async_vcon_fe::_setup, this);
}
int
Async_vcon_fe::write(char const *buf, unsigned sz)
{
if (!_initialized)
return sz;
return do_write(buf, sz);
}

View File

@@ -0,0 +1,22 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "vcon_fe_base.h"
class Async_vcon_fe : public Vcon_fe_base
{
public:
Async_vcon_fe(L4::Cap<L4::Vcon> con, L4Re::Util::Object_registry *r);
int write(char const *buf, unsigned sz);
private:
static void *_setup(void *);
void *setup(void);
bool _initialized;
};

View File

@@ -0,0 +1,167 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "client.h"
#include "controller.h"
#include <l4/re/env.h>
#include <l4/sys/kip.h>
#include <climits>
#include <cstring>
#include <time.h>
template<typename Client>
void
Client_timeout<Client>::expired()
{ _client->timeout_expired(); }
Client::Client(std::string const &tag, int color, int rsz, int wsz, Key key,
bool line_buffering, unsigned line_buffering_ms,
L4::Ipc_svr::Server_iface *sif, Controller *ctl)
: _col(color), _tag(tag), _line_buffering(line_buffering),
_line_buffering_ms(line_buffering_ms), _key(key), _wb(wsz), _rb(rsz),
_first_unwritten(_wb.head()), _timeout(this), _sif(sif), _ctl(ctl)
{
_attr.i_flags = L4_VCON_ICRNL;
_attr.o_flags = L4_VCON_ONLRET | L4_VCON_ONLCR;
_attr.l_flags = L4_VCON_ECHO;
}
Client::~Client()
{
if (output_mux())
output_mux()->disconnect(this);
if (_ctl)
_ctl->remove_client(this);
}
bool
Client::collected()
{
_dead = true;
if (_keep)
return false;
return true;
}
static constexpr int Max_timestamp_len = 25;
void
Client::print_timestamp()
{
time_t t = time(NULL);
struct tm *tt = localtime(&t);
char b[Max_timestamp_len];
int l = tt ? strftime(b, sizeof(b), "[%Y-%m-%d %T] ", tt)
: snprintf(b, sizeof(b), "[unknown] ");
if (l)
wbuf()->put(b, l);
}
void
Client::do_output(Buf::Index until)
{
if (!_output)
return;
wbuf()->write(_first_unwritten, until, this);
_first_unwritten = until;
if (!(_attr.l_flags & L4_VCON_ICANON))
_output->flush(this);
}
void
Client::cooked_write(const char *buf, long size) throw()
{
if (size < 0)
size = strlen(buf);
Client::Buf *w = wbuf();
Buf::Index last_nl = _first_unwritten;
while (size)
{
// If doing output, we must be careful not to overwrite parts of the
// circular write buffer that have not yet been written to the output.
// Therefore, we do not write everything at once, but divide processing
// into batches.
long max_batch_size = _output
// The range from the write buffer's head to the first unwritten
// character can be safely written to. Adjust that distance for the
// possibility that we have to print a timestamp and write an
// additional \r character for \n.
? w->distance(w->head(), _first_unwritten - 1) - Max_timestamp_len - 1
// Not doing output, so no need to limit the maximum batch size.
: LONG_MAX;
long batch_size = 0;
for (; batch_size < size && batch_size < max_batch_size; batch_size++)
{
if (_new_line && timestamp())
{
print_timestamp();
max_batch_size -= Max_timestamp_len;
}
char c = *buf++;
if (_attr.o_flags & L4_VCON_ONLCR && c == '\n')
{
w->put('\r');
max_batch_size--;
}
if (_attr.o_flags & L4_VCON_OCRNL && c == '\r')
c = '\n';
if (_attr.o_flags & L4_VCON_ONLRET && c == '\r')
continue;
w->put(c);
_new_line = c == '\n';
if (_new_line)
last_nl = w->head();
}
// Decrement size for characters processed from buf in this batch.
size -= batch_size;
if (_output)
{
Buf::Index write_until = w->head();
// If line buffering is enabled only print complete lines, except when an
// incomplete line spans the entire write buffer.
if (_line_buffering && batch_size > 0)
write_until = last_nl;
// Output characters processed up to and in this batch.
if (write_until != _first_unwritten)
do_output(write_until);
}
}
// If line buffering is enabled, and there is an incomplete line pending in
// the write buffer, enqueue the line buffer timeout.
if (_output && _line_buffering && w->head() != _first_unwritten && _sif)
{
_sif->remove_timeout(&_timeout);
_sif->add_timeout(&_timeout,
l4_kip_clock(l4re_kip()) + _line_buffering_ms * 1000);
}
}
void
Client::timeout_expired()
{
do_output(wbuf()->head());
}

View File

@@ -0,0 +1,382 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <string>
#include <l4/sys/vcon>
#include <l4/sys/cxx/ipc_server_loop>
#include <l4/cxx/ipc_timeout_queue>
#include "output_mux.h"
#include <cstring>
#include <l4/cxx/string>
#include <vector>
class Controller;
template<typename Client>
class Client_timeout : public L4::Ipc_svr::Timeout_queue::Timeout
{
public:
Client_timeout(Client *client)
: _client(client)
{}
void expired() override;
private:
Client *_client;
};
class Client
{
public:
class Key
{
public:
Key() : _key(0) {}
explicit Key(int k) : _key(k) {}
bool is_nil() const { return !_key; }
bool operator == (Key k) const { return !is_nil() && _key == k._key; }
void operator = (char v) { _key = v; }
char v() const { return _key; }
private:
char _key;
};
class Buf
{
public:
class Index
{
public:
bool operator == (Index v) const { return v.i == i; }
bool operator != (Index v) const { return v.i != i; }
Index operator ++ ()
{
if (++i == _b->_bufsz)
i = 0;
return *this;
}
Index operator ++ (int)
{
int n = i;
++(*this);
return Index(n, _b);
}
Index operator -- ()
{
if (i == 0)
i = _b->_bufsz;
--i;
return *this;
}
Index operator + (int v)
{
int n = i + v;
if (n > _b->_bufsz || n < 0)
n %= _b->_bufsz;
return Index(n, _b);
}
Index operator - (int v)
{
return this->operator+(-v);
}
private:
friend class Buf;
explicit Index(int i, Buf const *b) : i(i), _b(b) {}
int i;
Buf const *_b;
};
explicit Buf(size_t sz)
: _buf(new char [sz]), _bufsz(sz)
{}
Buf() = delete;
~Buf() { delete [] _buf; }
Index head() const { return Index(_head, this); }
Index tail() const { return Index(_tail, this); }
char operator [] (Index const &i) const { return _buf[i.i]; }
template< typename O >
int write(Index const &s, Index const &e, O *o) const
{
int l = 0;
if (s.i < e.i)
l += o->write(_buf + s.i, e.i - s.i);
else if (s.i > e.i)
{
l += o->write(_buf + s.i, _bufsz - s.i);
l += o->write(_buf, e.i);
}
return l;
}
bool put_break()
{
if (!break_points.size() || break_points.back() != _head)
break_points.push_back(_head);
return _tail == _head;
}
bool is_next_break(int offset) const
{
return break_points.size()
&& ((_head + offset) % _bufsz) == break_points[0];
}
void clear_next_break()
{
break_points.erase(break_points.begin());
}
bool put(char d)
{
int was_empty = _tail == _head;
++_sum_bytes;
if (d == '\n')
++_sum_lines;
_buf[_head] = d;
if (++_head == _bufsz)
_head = 0;
if (break_points.size() && break_points[0] == _head)
clear_next_break();
if (_head == _tail && ++_tail == _bufsz)
_tail = 0;
return was_empty;
}
bool put(const char *d, int len)
{
int was_empty = _tail == _head;
_sum_bytes += len;
while (len--)
{
if ((_head + 1) % _bufsz == _tail)
_tail = (_tail + 1) % _bufsz;
if (*d == '\n')
_sum_lines++;
_buf[_head++] = *d++;
if(_head == _bufsz)
_head = 0;
if (break_points.size() && break_points[0] == _head)
clear_next_break();
}
return was_empty;
}
int get(int offset, char const **d) const
{
if (offset < 0 || (unsigned)offset >= _sum_bytes)
return 0;
offset = (_tail + offset) % _bufsz;
*d = &_buf[offset];
if (offset > _head)
return _bufsz - offset;
return _head - offset;
}
Index find_backwards(char v, Index p) const
{
while (p != tail())
{
--p;
if ((*this)[p] == v)
return p;
}
return p;
}
Index find_forwards(char v, Index p) const
{
while (p != head())
{
if ((*this)[p] == v)
return p;
++p;
}
return p;
}
bool empty() const { return head() == tail(); }
int distance() const { return distance(tail(), head()); }
int distance(Index start, Index end) const
{
if (end.i >= start.i)
return end.i - start.i;
return (end.i + _bufsz) - start.i;
}
void clear(int l)
{
if (distance() >= _bufsz)
_tail = _head;
else
_tail = (_tail + l) % _bufsz;
}
unsigned long stat_bytes() const { return _sum_bytes; }
unsigned long stat_lines() const { return _sum_lines; }
private:
Buf(Buf const &) = delete;
Buf &operator = (Buf const &) = delete;
char *_buf;
int _bufsz;
int _head = 0, _tail = 0;
std::vector<int> break_points;
unsigned long _sum_bytes = 0, _sum_lines = 0;
};
void timeout_expired();
struct Equal_key
{
Client::Key k;
Equal_key(Client::Key const &k) : k(k) {}
bool operator () (Client const *c) const { return k == c->key(); }
};
struct Equal_tag
{
cxx::String n;
Equal_tag(cxx::String const &n) : n(n) {}
bool operator () (Client const *c) const
{ return n == c->tag().c_str(); }
};
struct Equal_tag_idx
{
cxx::String n;
Equal_tag_idx(cxx::String const &n) : n(n) {}
bool operator () (Client const *c) const
{
if (n == c->tag().c_str() && c->idx == 0)
return true;
cxx::String::Index r = n.rfind(":");
if (n.eof(r))
return false;
if (n.head(r) != c->tag().c_str())
return false;
int idx = 0;
n.substr(r+1).from_dec(&idx);
return idx == c->idx;
}
};
Client() = delete;
Client(std::string const &tag, int color, int rsz, int wsz, Key key,
bool line_buffering, unsigned line_buffering_ms,
L4::Ipc_svr::Server_iface *sif, Controller *ctl);
virtual ~Client();
bool collected();
bool keep() const { return _keep; }
bool dead() const { return _dead; }
bool timestamp() const { return _timestamp; }
void keep(bool keep) { _keep = keep; }
void timestamp(bool ts) { _timestamp = ts; }
void output_mux(Output_mux *m) { _output = m; }
Output_mux *output_mux() const { return _output; }
int color() const { return _col; }
std::string const &tag() const { return _tag; }
Key const key() const { return _key; }
void key(Key key) { _key = key; }
bool output_line_preempted() const { return _p; }
void preempt_output_line() { _p = true; }
void output_line_done() { _p = false; }
Buf *rbuf() { return &_rb; }
Buf const *rbuf() const { return &_rb; }
Buf *wbuf() { return &_wb; }
Buf const *wbuf() const { return &_wb; }
l4_vcon_attr_t const *attr() const { return &_attr; }
virtual void trigger() const = 0;
int write(char const *buf, int len)
{ _output->write(this, buf, len); return len; }
int write(char const *buf)
{ return write(buf, strlen(buf)); }
void cooked_write(const char *buf, long size = -1) throw();
void skip_unwritten()
{ _first_unwritten = wbuf()->head(); }
int idx = 0;
private:
int _col;
std::string _tag;
bool _p = false;
bool _keep = false;
bool _timestamp = false;
bool _new_line = true;
bool _dead = false;
bool _line_buffering = false;
unsigned _line_buffering_ms = 50;
Key _key;
Buf _wb, _rb;
Buf::Index _first_unwritten;
void print_timestamp();
void do_output(Buf::Index until);
Client_timeout<Client> _timeout;
L4::Ipc_svr::Server_iface *_sif;
Controller *_ctl;
protected:
l4_vcon_attr_t _attr;
Output_mux *_output = nullptr;
};

View File

@@ -0,0 +1,782 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <cstdio>
#include "controller.h"
#include <algorithm>
#include <vector>
#include "globmatch.h"
Controller::Cmd Controller::_cmds[] =
{
{ "c", 0, &Controller::cmd_connect, &Controller::complete_console_name_1 },
{ "cat", "Dump buffer of channel", &Controller::cmd_cat, &Controller::complete_console_name_1 },
{ "clear", "Clear screen", &Controller::cmd_clear, 0 },
{ "connect", "Connect to channel", &Controller::cmd_connect, &Controller::complete_console_name_1 },
{ "drop", "Drop kept client", &Controller::cmd_drop, &Controller::complete_console_name_1 },
{ "grep", "Search for text", &Controller::cmd_grep, &Controller::complete_console_name_grep },
{ "help", "Help screen", &Controller::cmd_help, 0 },
{ "hide", "Hide channel output", &Controller::cmd_hide, &Controller::complete_console_name_1 },
{ "hideall", "Hide all channels output", &Controller::cmd_hideall, 0 },
{ "info", "Info screen", &Controller::cmd_info, 0 },
{ "keep", "Keep client from garbage collection", &Controller::cmd_keep, &Controller::complete_console_name_1 },
{ "key", "Set key shortcut for channel", &Controller::cmd_key, &Controller::complete_console_name_1 },
{ "kick", 0, &Controller::cmd_kick, &Controller::complete_console_name_1 },
{ "list", "List channels", &Controller::cmd_list, 0 },
{ "ls", 0, &Controller::cmd_list, 0 },
{ "show", "Show channel output", &Controller::cmd_show, &Controller::complete_console_name_1 },
{ "showall", "Show all channels output", &Controller::cmd_showall, 0 },
{ "tail", "Show last lines of output", &Controller::cmd_tail, &Controller::complete_console_name_1 },
{ "timestamp", "Prefix log with timestamp", &Controller::cmd_timestamp, &Controller::complete_console_name_1 },
{ 0, 0, 0, 0 }
};
namespace {
class Cmd_name_iter : public String_set_iter
{
public:
explicit Cmd_name_iter(Controller::Cmd const *cmd) : _cmd(cmd) {}
cxx::String value(cxx::String) const
{ return _cmd->name; }
void next()
{ ++_cmd; }
bool has_more() const
{ return _cmd->name; }
private:
Controller::Cmd const *_cmd;
};
class Client_name_iter : public String_set_iter
{
public:
explicit Client_name_iter(Controller::Client_list const *c)
: _client(c->begin()), _e(c->end())
{}
cxx::String value(cxx::String buf) const
{
if ((*_client)->idx == 0)
return (*_client)->tag().c_str();
snprintf((char *)buf.start(), buf.len(), "%s:%d",
(*_client)->tag().c_str(), (*_client)->idx);
return buf;
}
void next()
{ ++_client; }
bool has_more() const
{ return _client != _e; }
private:
Controller::Client_const_iter _client, _e;
};
}
void
Controller::remove_client(Client_ptr client)
{
for (auto it = clients.begin(); it != clients.end(); ++it)
if (client == *it)
{
clients.erase(it);
break;
}
}
Client *
Controller::get_client(Mux *mux, int argc, int idx, Arg *a)
{
if (argc <= idx)
{
mux->printf("%.*s: invalid number of arguments (need %d, got %d)\n",
a[0].a.len(), a[0].a.start(), idx + 1, argc);
return 0;
}
Client_iter c = std::find_if(clients.begin(), Client_iter(clients.end()),
Client::Equal_tag_idx(a[idx].a));
if (c != clients.end())
return *c;
mux->printf("%.*s: console '%.*s' not found\n",
a[0].a.len(), a[0].a.start(), a[idx].a.len(), a[idx].a.start());
return 0;
}
Controller::Cmd const *
Controller::match_cmd(Mux *, cxx::String const &s) const
{
for (Cmd const *i = _cmds; i->name; ++i)
{
cxx::String n(i->name);
if (s == n)
return i;
}
return 0;
}
unsigned
Controller::split_input(cxx::String const &s, Arg *args,
const unsigned max_args,
bool ignore_empty_last_arg)
{
cxx::String::Index e = s.start();
unsigned num_args = 0;
unsigned prespace;
while (!s.eof(e))
{
prespace = 0;
while (!s.eof(e) && isspace(s[e]))
{
++e;
++prespace;
}
if (s.eof(e))
{
if (num_args < max_args)
{
args[num_args].prespace = prespace;
args[num_args++].a = cxx::String();
}
break;
}
cxx::String::Index arg_start = e;
while (!s.eof(e) && !isspace(s[e]))
++e;
if (num_args < max_args)
{
args[num_args].prespace = prespace;
args[num_args++].a = cxx::String(arg_start, e);
}
else
break;
}
if (ignore_empty_last_arg
&& num_args && args[num_args - 1].a.len() == 0)
--num_args;
return num_args;
}
int
Controller::complete(Mux *mux, unsigned cur_pos, Input_buf *ibuf)
{
(void)cur_pos;
enum { Max_args = 16 };
Arg args[Max_args];
int num_args = split_input(ibuf->string(), args, Max_args, false);
char outbuf[100];
cxx::String outarg(outbuf, sizeof(outbuf));
int arg_to_complete;
int cnt;
if (num_args < 2)
{
Cmd_name_iter i(_cmds);
cnt = complete(mux, args[0].a, &i, outarg);
arg_to_complete = 0;
}
else
{
Cmd const *c = match_cmd(mux, args[0].a);
if (!c || !c->complete_func)
return 0;
arg_to_complete = num_args - 1;
cnt = (this->*(c->complete_func))(mux, num_args, arg_to_complete,
args, outarg);
}
if (cnt > 0)
{
if (num_args == 0)
ibuf->string().len(0);
else
{
if (cnt == 1 && outarg.len() < (int)sizeof(outbuf))
{
char *e = (char *)outarg.end();
*e = ' ';
outarg.len(outarg.len() + 1);
}
ibuf->replace(args[arg_to_complete].a, outarg);
}
}
return cnt;
}
int
Controller::cmd(Mux *mux, cxx::String const &s)
{
cxx::String::Index i = s.start();
while (!s.eof(i) && isspace(s[i]))
++i;
cxx::String::Index e = cxx::String(i, s.end()).find_match(isspace);
cxx::String cmd(cxx::String(i, e));
if (cmd.empty())
return 0;
enum { Max_args = 16 };
Arg args[Max_args];
unsigned num_args = split_input(s, args, Max_args, true);
if (Cmd const *c = match_cmd(mux, cmd))
(this->*(c->func))(mux, num_args, args);
else
mux->printf("Unknown command '%.*s'. Use 'help'.\n", cmd.len(), cmd.start());
return 0;
}
int
Controller::cmd_info(Mux *mux, int, Arg *)
{
mux->printf("Cons -- Vcon multiplexer\n");
return 0;
}
int
Controller::cmd_help(Mux *mux, int, Arg *)
{
for (Cmd const *i = _cmds; i->name; ++i)
if (i->helptext)
mux->printf("%15s - %s\n", i->name, i->helptext);
mux->printf("\nKey shortcuts when connected:\n");
mux->printf(" Ctrl-E . - Disconnect\n");
mux->printf(" Ctrl-E e - Inject Ctrl-E\n");
mux->printf(" Ctrl-E c - Inject Ctrl-C\n");
mux->printf(" Ctrl-E z - Inject Ctrl-Z\n");
mux->printf(" Ctrl-E q - Inject ESC\n");
mux->printf(" Ctrl-E l - Inject Break sequence\n");
mux->printf("\nGlobal key shortcuts:\n");
mux->printf(" Ctrl-E h - Hide all output (except current)\n");
mux->printf(" Ctrl-E s - Show all output\n");
bool first = true;
for (auto const i : clients)
if (!i->key().is_nil())
{
if (first)
{
mux->printf("\nUser defined key shortcuts:\n");
first = false;
}
mux->printf(" Ctrl-E %c - Connect to console '%s'\n",
i->key().v(), i->tag().c_str());
}
return 0;
}
int
Controller::cmd_list(Mux *mux, int argc, Arg *args)
{
bool long_mode = false;
std::vector<std::string> patterns;
for (int i = 1; i < argc; ++i)
{
if (args[i].a.len() == 2
&& args[i].a[0] == '-' && args[i].a[1] == 'l')
long_mode = true;
if (args[i].a.len() > 0 && args[i].a[0] != '-')
patterns.push_back(std::string(args[i].a.start(), args[i].a.len()));
}
auto sorted = clients;
std::sort(sorted.begin(), sorted.end(),
[](const Client_ptr a, const Client_ptr b) {
return a->tag().compare(b->tag()) < 0;
} );
Client_list output_list;
if (patterns.empty())
output_list = sorted;
else
for (auto p : patterns)
{
Client_list plist;
for (auto const c : sorted)
if (glob::match(c->tag(), p))
plist.push_back(c);
for (auto const q : plist)
output_list.push_back(q);
}
for (auto const i : output_list)
{
mux->printf("%14s%s%.0d %c%c%c [%8s] out:%5ld/%6ld in:%5ld/%5ld%s",
i->tag().c_str(), i->idx ? ":" : "", i->idx,
i->key().is_nil() ? ' ': '(',
i->key().is_nil() ? ' ': i->key().v(),
i->key().is_nil() ? ' ': ')',
i->output_mux() ? i->output_mux()->name() : "",
i->wbuf()->stat_lines(), i->wbuf()->stat_bytes(),
i->rbuf()->stat_lines(), i->rbuf()->stat_bytes(),
i->dead() ? " [X]" : "");
if (long_mode)
mux->printf(" pend=%d attr:o=%lo,i=%lo,l=%lo",
i->rbuf()->distance(),
i->attr()->o_flags, i->attr()->i_flags,
i->attr()->l_flags);
mux->printf("\n");
}
return L4_EOK;
}
int
Controller::complete_console_name(Mux *mux, unsigned, unsigned argnr, Arg *arg,
cxx::String &completed_arg,
unsigned num_arg) const
{
if (argnr != num_arg)
return 0;
auto sorted = clients;
std::sort(sorted.begin(), sorted.end(),
[](const Client_ptr a, const Client_ptr b) {
return a->tag().compare(b->tag()) < 0;
} );
Client_name_iter i(&sorted);
return complete(mux, arg[argnr].a, &i, completed_arg);
}
int
Controller::complete_console_name_1(Mux *mux, unsigned argc, unsigned argnr,
Arg *arg,
cxx::String &completed_arg) const
{
return complete_console_name(mux, argc, argnr, arg, completed_arg, 1);
}
int
Controller::complete(Mux *mux, cxx::String arg, String_set_iter *i,
cxx::String &completed_arg) const
{
int cnt = 0, l = 0;
char buf[40], first[40];
cxx::String found;
for (; i->has_more(); i->next())
{
cxx::String cname
= i->value(cxx::String(found.empty() ? first : buf,
found.empty() ? sizeof(first) : sizeof(buf)));
if (arg.empty() || cname.starts_with(arg))
{
++cnt;
if (cnt == 2) // oh at least two hits, print the first one too
l += mux->printf("\n%.*s ", found.len(), found.start());
if (cnt > 1)
l += mux->printf("%.*s ", cname.len(), cname.start());
if (l > 68)
{
l = 0;
mux->printf("\n");
}
if (!found.empty())
{
int x;
for (x = 0; x < found.len() && x < cname.len(); ++x)
if (found[x] != cname[x])
break;
found.len(x);
}
else
found = cname;
}
}
int len = found.len();
if (len > completed_arg.len())
return 0;
memcpy((void *)completed_arg.start(), found.start(), len);
completed_arg.len(len);
if (cnt > 1)
mux->printf("\n");
return cnt;
}
int
Controller::cmd_cat(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
mux->cat(v, true);
return 0;
}
int
Controller::cmd_tail(Mux *mux, int argc, Arg *a)
{
Client *c = get_client(mux, argc, 1, a);
if (c)
{
int numlines = 20;
if (argc > 2)
{
if (!a[2].a.from_dec(&numlines))
{
mux->printf("Invalid argument '%.*s'\n",
a[2].a.len(), a[2].a.start());
return 0;
}
}
mux->tail(c, numlines, true);
}
return 0;
}
int
Controller::cmd_timestamp(Mux *mux, int argc, Arg *a)
{
Client *c = get_client(mux, argc, 1, a);
if (!c)
return 0;
c->timestamp(a[2].a != "off");
return 0;
}
int
Controller::cmd_clear(Mux *mux, int, Arg *)
{
mux->printf("\033[H\033[2J");
return 0;
}
int
Controller::cmd_connect(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
{
mux->connect(v);
return 0;
}
return -L4_ENODEV;
}
int
Controller::cmd_show(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
mux->show(v);
return 0;
}
int
Controller::cmd_hide(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
mux->hide(v);
return 0;
}
int
Controller::cmd_showall(Mux *mux, int, Arg *)
{
// show all not yet displayed clients on /mux/
for (auto const i : clients)
if (!i->output_mux())
mux->show(i);
return 0;
}
int
Controller::cmd_hideall(Mux *mux, int, Arg *)
{
for (auto const i : clients)
if (i->output_mux() == mux)
mux->hide(i);
return 0;
}
int
Controller::cmd_keep(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
v->keep(true);
return 0;
}
int Controller::cmd_key(Mux *mux, int argc, Arg *a)
{
if (argc < 2)
{
mux->printf("Usage: key channel character\n");
return 0;
}
if (Client *v = get_client(mux, argc, 1, a))
v->key(Client::Key(a[2].a[0]));
return 0;
}
int
Controller::cmd_kick(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
v->trigger();
return 0;
}
int
Controller::cmd_drop(Mux *mux, int argc, Arg *a)
{
if (Client *v = get_client(mux, argc, 1, a))
{
v->keep(false);
if (v->dead())
delete v;
}
return 0;
}
int
Controller::complete_console_name_grep(Mux *mux, unsigned, unsigned argnr,
Arg *arg,
cxx::String &completed_arg) const
{
int cnt = 0;
for (unsigned i = 1; i < argnr + 1; ++i)
{
if (!arg[i].a.empty() && arg[i].a[0] == '-')
continue;
if (++cnt == 2)
{
auto sorted = clients;
std::sort(sorted.begin(), sorted.end(),
[](const Client_ptr a, const Client_ptr b) {
return a->tag().compare(b->tag()) < 0;
} );
Client_name_iter cl(&clients);
return complete(mux, arg[i].a, &cl, completed_arg);
}
}
return 0;
}
int
Controller::cmd_grep(Mux *mux, int argc, Arg *a)
{
Client *given_client = 0;
cxx::String pattern;
bool opt_line = false;
bool opt_word = false;
bool opt_igncase = false;
bool opt_count = false;
bool opt_inv = false;
unsigned opt_ctx_b = 0;
unsigned opt_ctx_a = 0;
for (int i = 1; i < argc; ++i)
{
if (a[i].a[0] == '-')
{
for (int idx = 1; idx < a[i].a.len(); ++idx)
{
switch (a[i].a[idx])
{
case 'n': opt_line = true; break;
case 'w': opt_word = true; break;
case 'i': opt_igncase = true; break;
case 'c': opt_count = true; break;
case 'v': opt_inv = true; break;
case 'A':
case 'B':
case 'C':
{
if (i + 1 == argc || idx + 1 < a[i].a.len())
{
mux->printf("grep: Missing parameter for option '%c'\n",
a[i].a[idx]);
return 1;
}
switch (a[i].a[idx])
{
case 'A': a[i + 1].a.from_dec(&opt_ctx_a); break;
case 'B': a[i + 1].a.from_dec(&opt_ctx_b); break;
case 'C': a[i + 1].a.from_dec(&opt_ctx_a);
opt_ctx_b = opt_ctx_a;
break;
}
++i;
break;
}
default: mux->printf("grep: Unknown option '%c'\n",
a[i].a[idx]);
return 1;
};
}
}
else if (pattern.empty())
pattern = a[i].a;
else
given_client = get_client(mux, argc, i, a);
// we should support multiple clients here
}
if (opt_igncase)
for (int i = 0; i < pattern.len(); ++i)
const_cast<char &>(pattern[i]) = tolower(pattern[i]);
for (auto const v : clients)
{
if (given_client && &*v != given_client)
continue;
Client::Buf const *b = v->wbuf();
Client::Buf::Index end = b->head();
enum { Flag_none = 0, Flag_print = 1 << 0, Flag_match = 1 << 1, };
typedef std::vector<Grep_lineinfo> Line_vector;
Line_vector lines;
unsigned print_next_lines = 0;
Grep_lineinfo curline = Grep_lineinfo(b->tail());
bool is_match = false;
unsigned count = 0;
Client::Buf::Index i = b->tail();
bool exit_loop = false;
while (1)
{
if (((*b)[i] == '\n' && i + 1 != end)
|| i == end)
{
if (print_next_lines)
{
curline.flags |= Flag_print;
--print_next_lines;
}
if (opt_inv ^ !!is_match)
{
++count;
curline.flags |= Flag_match | Flag_print;
unsigned o = cxx::min(lines.size(),
(Line_vector::size_type)opt_ctx_b);
for (; o; o--)
lines[lines.size() - o].flags |= Flag_print;
if (opt_ctx_a > print_next_lines)
print_next_lines = opt_ctx_a;
}
lines.push_back(curline);
curline = Grep_lineinfo(i + 1);
is_match = false;
}
if (exit_loop)
break;
int idx = 0;
if (opt_igncase)
{
for (; idx < pattern.len(); ++idx)
if (pattern[idx] != tolower((*b)[i + idx]))
break;
}
else
{
for (; idx < pattern.len(); ++idx)
if (pattern[idx] != (*b)[i + idx])
break;
}
if (idx == pattern.len() && (*b)[i] != '\n')
{
if (!opt_word ||
(!isalnum((*b)[i- 1]) && !isalnum((*b)[i + idx])))
is_match = true;
}
++i;
exit_loop = i == end;
}
unsigned last_output_idx = ~0U;
if (!opt_count)
for (unsigned linenr = 0; linenr < lines.size(); ++linenr)
{
if (lines[linenr].flags & Flag_print)
{
if (last_output_idx < linenr
&& last_output_idx != linenr - 1
&& (opt_ctx_a || opt_ctx_b))
mux->printf("--\n");
if (!given_client)
mux->printf("%s%c", v->tag().c_str(),
lines[linenr].flags & Flag_match ? ':' : '-');
if (opt_line)
mux->printf("%d%c", linenr + 1,
lines[linenr].flags & Flag_match ? ':' : '-');
Client::Buf::Index le = lines[linenr].line;
while ((*b)[le] != '\n' && le != end)
mux->printf("%c", (*b)[le++]);
if ((*b)[le] == '\n')
mux->printf("\n");
last_output_idx = linenr;
}
}
else
{
if (!given_client)
mux->printf("%s:", v->tag().c_str());
mux->printf("%d\n", count);
}
}
return 0;
}

View File

@@ -0,0 +1,114 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "frontend.h"
#include "client.h"
#include "mux.h"
#include <l4/cxx/hlist>
#include <l4/cxx/string>
class String_set_iter
{
public:
virtual cxx::String value(cxx::String buf) const = 0;
virtual void next() = 0;
virtual bool has_more() const = 0;
};
class Input_buf
{
public:
virtual int replace(cxx::String &del, cxx::String add) = 0;
virtual cxx::String string() const = 0;
};
class Controller
{
public:
int cmd(Mux *mux, cxx::String const &s);
struct Arg
{
cxx::String a;
unsigned prespace;
};
typedef int (Controller::*Cmd_func)(Mux *mux, int, Arg *);
typedef int (Controller::*Cmd_complete_func)(Mux *mux, unsigned argc,
unsigned argnr, Arg *arg,
cxx::String &completed_arg) const;
struct Cmd
{
const char *name;
const char *helptext;
Cmd_func func;
Cmd_complete_func complete_func;
};
int complete(Mux *mux, unsigned cur_pos, Input_buf *ibuf);
private:
Client *get_client(Mux *mux, int argc, int idx, Arg *a);
int cmd_cat(Mux *mux, int, Arg *);
int cmd_clear(Mux *mux, int, Arg *);
int cmd_connect(Mux *mux, int, Arg *);
int cmd_drop(Mux *mux, int, Arg *);
int cmd_help(Mux *mux, int, Arg *);
int cmd_hide(Mux *mux, int, Arg *);
int cmd_hideall(Mux *mux, int, Arg *);
int cmd_grep(Mux *mux, int, Arg *);
int cmd_info(Mux *mux, int, Arg *);
int cmd_keep(Mux *mux, int, Arg *);
int cmd_key(Mux *mux, int, Arg *);
int cmd_kick(Mux *mux, int, Arg *);
int cmd_list(Mux *mux, int, Arg *);
int cmd_show(Mux *mux, int, Arg *);
int cmd_showall(Mux *mux, int, Arg *);
int cmd_tail(Mux *mux, int, Arg *);
int cmd_timestamp(Mux *mux, int, Arg *);
int complete_console_name(Mux *mux, unsigned argc, unsigned argnr,
Arg *arg, cxx::String &completed_arg,
unsigned num_arg) const;
int complete_console_name_1(Mux *mux, unsigned argc, unsigned argnr,
Arg *arg, cxx::String &completed_arg) const;
int complete_console_name_grep(Mux *mux, unsigned argc, unsigned argnr,
Arg *arg, cxx::String &completed_arg) const;
int complete(Mux *mux, cxx::String arg, String_set_iter *i,
cxx::String &completed_arg) const;
Cmd const *match_cmd(Mux *mux, cxx::String const &s) const;
unsigned split_input(cxx::String const &s, Arg *args,
const unsigned max_args, bool ignore_empty_last_arg);
static Cmd _cmds[];
struct Grep_lineinfo
{
explicit Grep_lineinfo(Client::Buf::Index l) : line(l), flags(0) {}
Client::Buf::Index line;
unsigned flags;
};
public:
typedef Client *Client_ptr;
typedef std::vector<Client_ptr> Client_list;
typedef Client_list::const_iterator Client_const_iter;
typedef Client_list::iterator Client_iter;
Client_list clients;
void remove_client(Client_ptr client);
};

View File

@@ -0,0 +1,30 @@
/*
* (c) 2012-2013 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/re/util/debug>
class Dbg : public L4Re::Util::Dbg
{
public:
enum Level
{
Info = 1,
Warn = 2,
Boot = 4,
Err = 8,
};
struct Dbg_bits { char const *n; unsigned long bits; };
explicit
Dbg(unsigned long mask, char const *subs = 0)
: L4Re::Util::Dbg(mask, "Cons", subs)
{}
static Dbg_bits dbg_bits[];
};

View File

@@ -0,0 +1,27 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/hlist>
#include "input_mux.h"
class Frontend : public cxx::H_list_item
{
public:
virtual int write(char const *buffer, unsigned size) = 0;
void input_mux(Input_mux *m) { _input = m; }
virtual ~Frontend() = 0;
virtual bool check_input() = 0;
protected:
Input_mux *_input;
};
inline Frontend::~Frontend() {}

View File

@@ -0,0 +1,132 @@
/*
***********************************************************************
* C++ Wildcard Pattern Matching Library *
* *
* Author: Arash Partow (2001) *
* URL: https://www.partow.net/programming/wildcardmatching/index.html *
* *
* Copyright notice: *
* Free use of the C++ Wildcard Pattern Matching Library is permitted *
* under the guidelines and in accordance with the most current *
* version of the MIT License. *
* https://www.opensource.org/licenses/MIT *
* *
***********************************************************************
*/
#ifndef INCLUDE_GLOBMATCH_HPP
#define INCLUDE_GLOBMATCH_HPP
#include <cctype>
#include <iterator>
#include <string>
namespace glob
{
namespace details
{
template <typename Compare,
typename Iterator,
typename ValueType = typename std::iterator_traits<Iterator>::value_type>
inline bool match_impl(const Iterator pattern_begin,
const Iterator pattern_end ,
const Iterator data_begin ,
const Iterator data_end ,
const ValueType zero_or_more,
const ValueType exactly_one )
{
typedef typename std::iterator_traits<Iterator>::value_type type;
const Iterator null_itr(0);
Iterator p_itr = pattern_begin;
Iterator d_itr = data_begin;
Iterator np_itr = null_itr;
Iterator nd_itr = null_itr;
for ( ; ; )
{
if (pattern_end != p_itr)
{
const type c = *(p_itr);
if ((data_end != d_itr) && (Compare::cmp(c,*(d_itr)) || (exactly_one == c)))
{
++d_itr;
++p_itr;
continue;
}
else if (zero_or_more == c)
{
while ((pattern_end != p_itr) && (zero_or_more == *(p_itr)))
{
++p_itr;
}
const type d = *(p_itr);
while ((data_end != d_itr) && !(Compare::cmp(d,*(d_itr)) || (exactly_one == d)))
{
++d_itr;
}
// set backtrack iterators
np_itr = p_itr - 1;
nd_itr = d_itr + 1;
continue;
}
}
else if (data_end == d_itr)
return true;
if ((data_end == d_itr) || (null_itr == nd_itr))
return false;
p_itr = np_itr;
d_itr = nd_itr;
}
return true;
}
typedef char char_t;
struct cs_match
{
static inline bool cmp(const char_t c0, const char_t c1)
{
return (c0 == c1);
}
};
struct cis_match
{
static inline bool cmp(const char_t c0, const char_t c1)
{
return (std::tolower(c0) == std::tolower(c1));
}
};
} // namespace details
inline bool match(const std::string& s,
const std::string& p,
const std::string::value_type match_one_or_more = '*',
const std::string::value_type match_exatcly_one = '.')
{
return details::match_impl<details::cs_match>
(
std::cbegin(p), std::cend(p),
std::cbegin(s), std::cend(s),
match_one_or_more,
match_exatcly_one
);
}
} // namespace glob
#endif

View File

@@ -0,0 +1,18 @@
/*
* (c) 2012-2013 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/string>
class Input_mux
{
public:
virtual void input(cxx::String const &buf) = 0;
virtual ~Input_mux() = 0;
};
inline Input_mux::~Input_mux() {}

View File

@@ -0,0 +1,456 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
// TODO: compressed output (maybe 'dump' command, or cat -z or so)
// TODO: store buffer compressed
// TODO: port libxz (http://tukaani.org/xz/)
// TODO: macro to inject some text into the read buffer
#include "mux_impl.h"
#include "frontend.h"
#include "client.h"
#include "controller.h"
#include "debug.h"
#include "vcon_client.h"
#include "vcon_fe.h"
#include "virtio_client.h"
#include "async_vcon_fe.h"
#include "registry.h"
#include "server.h"
#include <l4/re/util/icu_svr>
#include <l4/re/util/vcon_svr>
#include <l4/re/util/object_registry>
#include <l4/re/util/br_manager>
#include <l4/re/error_helper>
#include <l4/sys/typeinfo_svr>
#include <l4/cxx/iostream>
#include <l4/sys/cxx/ipc_epiface>
#include <terminate_handler-l4>
#include <algorithm>
#include <set>
#include <getopt.h>
#include <l4/bid_config.h>
#ifdef CONFIG_CONS_USE_ASYNC_FE
typedef Async_vcon_fe Fe;
#else
typedef Vcon_fe Fe;
#endif
using L4Re::chksys;
struct Config_opts
{
// Show output of all consoles.
bool default_show_all;
// By default, keep a console when a client disconnects.
bool default_keep;
// By default, merge client write requests to a single request containing an
// entire line (until a newline is detected).
bool default_line_buffering = true;
// In line-buffered mode, timeout for write the client output even if no
// newline was detected.
unsigned default_line_buffering_ms = 50;
// By default, show time stamps for all consoles.
bool default_timestamp;
// Currently unused.
std::string auto_connect_console;
};
static Config_opts config;
static L4::Server<L4Re::Util::Br_manager_timeout_hooks> server;
static Registry registry(&server);
class My_mux : public Mux_i, public cxx::H_list_item
{
public:
My_mux(Controller *ctl, char const *name) : Mux_i(ctl, name) {}
void add_auto_connect_console(std::string const &name)
{
_auto_connect_consoles.insert(name);
}
bool is_auto_connect_console(std::string const &name)
{
return _auto_connect_consoles.find(name) != _auto_connect_consoles.end();
}
private:
std::set<std::string> _auto_connect_consoles;
};
class Cons_svr : public L4::Epiface_t<Cons_svr, L4::Factory, Server_object>
{
public:
explicit Cons_svr(const char *name);
bool collected() { return false; }
template< typename CLI >
int create(std::string const &tag, int color, CLI **, size_t bufsz,
Client::Key key, bool line_buffering, unsigned line_buffering_ms);
int op_create(L4::Factory::Rights, L4::Ipc::Cap<void> &obj,
l4_mword_t proto, L4::Ipc::Varg_list_ref args);
Controller *ctl() { return &_ctl; }
void add(My_mux *m)
{ _muxe.add(m); }
int sys_msg(char const *fmt, ...) __attribute__((format(printf, 2, 3)));
private:
typedef cxx::H_list<My_mux> Mux_list;
typedef Mux_list::Iterator Mux_iter;
Mux_list _muxe;
Controller _ctl;
Dbg _info;
Dbg _err;
};
Cons_svr::Cons_svr(const char* name)
: _info(Dbg::Info, name), _err(Dbg::Err, name)
{
Dbg::set_level(~0U);
}
int
Cons_svr::sys_msg(char const *fmt, ...)
{
int r = 0;
for (Mux_iter i = _muxe.begin(); i != _muxe.end(); ++i)
{
va_list args;
va_start(args, fmt);
r = i->vsys_msg(fmt, args);
va_end(args);
}
return r;
}
template< typename CLI >
int
Cons_svr::create(std::string const &tag, int color, CLI **vout, size_t bufsz,
Client::Key key, bool line_buffering, unsigned line_buffering_ms)
{
typedef Controller::Client_iter Client_iter;
Client_iter c = std::find_if(_ctl.clients.begin(),
Client_iter(_ctl.clients.end()),
Client::Equal_key(key));
if (c != _ctl.clients.end())
sys_msg("WARNING: multiple clients with key '%c'\n", key.v());
std::string name = tag.length() > 0 ? tag : "<noname>";
c = std::find_if(_ctl.clients.begin(), Client_iter(_ctl.clients.end()),
Client::Equal_tag(cxx::String(name.data(), name.length())));
CLI *v = new CLI(name, color, bufsz, key, line_buffering, line_buffering_ms,
&registry, &server, &_ctl);
if (!v)
return -L4_ENOMEM;
if (!registry.register_obj(v))
{
delete v;
return -L4_ENOMEM;
}
if (c != _ctl.clients.end())
{
sys_msg("WARNING: multiple clients with tag '%s'\n", name.c_str());
v->idx = (*c)->idx + 1;
_ctl.clients.push_back(v);
}
else
_ctl.clients.push_back(v);
sys_msg("Created vcon channel: %s [%lx]\n",
v->tag().c_str(), v->obj_cap().cap());
*vout = v;
return 0;
}
int
Cons_svr::op_create(L4::Factory::Rights, L4::Ipc::Cap<void> &obj,
l4_mword_t proto, L4::Ipc::Varg_list_ref args)
{
switch (proto)
{
case (l4_mword_t)L4_PROTO_LOG:
case 1:
{
// copied from moe/server/src/alloc.cc
L4::Ipc::Varg tag = args.pop_front();
if (!tag.is_of<char const *>())
return -L4_EINVAL;
L4::Ipc::Varg col = args.pop_front();
int color;
if (col.is_of<char const *>())
{
cxx::String cs(col.value<char const*>(), col.length() - 1);
int c = 7, bright = 0;
if (!cs.empty())
{
switch (cs[0])
{
case 'N': bright = 1; /* FALLTHRU */ case 'n': c = 0; break;
case 'R': bright = 1; /* FALLTHRU */ case 'r': c = 1; break;
case 'G': bright = 1; /* FALLTHRU */ case 'g': c = 2; break;
case 'Y': bright = 1; /* FALLTHRU */ case 'y': c = 3; break;
case 'B': bright = 1; /* FALLTHRU */ case 'b': c = 4; break;
case 'M': bright = 1; /* FALLTHRU */ case 'm': c = 5; break;
case 'C': bright = 1; /* FALLTHRU */ case 'c': c = 6; break;
case 'W': bright = 1; /* FALLTHRU */ case 'w': c = 7; break;
default: c = 0;
}
}
color = (bright << 3) | c;
}
else if (col.is_of_int())
color = col.value<l4_mword_t>();
else
color = 7;
// length() > 0 for a valid string parameter
std::string ts(tag.value<char const*>(), tag.length() - 1);
bool show = config.default_show_all;
bool keep = config.default_keep;
bool line_buffering = config.default_line_buffering;
unsigned line_buffering_ms = config.default_line_buffering_ms;
bool timestamp = config.default_timestamp;
Client::Key key;
size_t bufsz = 0;
for (L4::Ipc::Varg opts: args)
{
if (opts.is_of<char const *>())
{
cxx::String cs(opts.value<char const *>(), opts.length() - 1);
if (cs == "hide")
show = false;
else if (cs == "show")
show = true;
else if (cs == "keep")
keep = true;
else if (cs == "no-keep")
keep = false;
else if (cs == "line-buffering")
line_buffering = true;
else if (cs == "no-line-buffering")
line_buffering = false;
else if (cxx::String::Index t = cs.starts_with("line-buffered-ms="))
cs.substr(t).from_dec(&line_buffering_ms);
else if (cs == "timestamp")
timestamp = true;
else if (cs == "no-timestamp")
timestamp = false;
else if (cxx::String::Index k = cs.starts_with("key="))
key = *k;
else if (cxx::String::Index v = cs.starts_with("bufsz="))
cs.substr(v).from_dec(&bufsz);
}
}
Client *v;
L4::Cap<void> v_cap;
if (proto == 1)
{
Virtio_cons *_v;
if (int r = create(ts, color, &_v, bufsz, key,
line_buffering, line_buffering_ms))
return r;
v = _v;
v_cap = _v->obj_cap();
}
else
{
Vcon_client *_v;
if (int r = create(ts, color, &_v, bufsz, key,
line_buffering, line_buffering_ms))
return r;
v = _v;
v_cap = _v->obj_cap();
}
if (show && _muxe.front())
_muxe.front()->show(v);
if (keep)
v->keep(v);
v->timestamp(timestamp);
for (Mux_iter i = _muxe.begin(); i != _muxe.end(); ++i)
{
if (i->is_auto_connect_console(ts))
{
i->connect(v);
break;
}
}
obj = L4::Ipc::make_cap(v_cap, L4_CAP_FPAGE_RWSD);
return L4_EOK;
}
break;
default:
return -L4_ENOSYS;
}
}
int main(int argc, char const *argv[])
{
printf("Console Server\n");
enum
{
OPT_SHOW_ALL = 'a',
OPT_MUX = 'm',
OPT_FE = 'f',
OPT_KEEP = 'k',
OPT_NO_LINE_BUFFERING = 'l',
OPT_LINE_BUFFERING_MS = 1,
OPT_TIMESTAMP = 't',
OPT_AUTOCONNECT = 'c',
OPT_DEFAULT_NAME = 'n',
OPT_DEFAULT_BUFSIZE = 'B',
};
static option opts[] =
{
{ "show-all", no_argument, 0, OPT_SHOW_ALL },
{ "mux", required_argument, 0, OPT_MUX },
{ "frontend", required_argument, 0, OPT_FE },
{ "keep", no_argument, 0, OPT_KEEP },
{ "no-line-buffering", no_argument, 0, OPT_NO_LINE_BUFFERING },
{ "line-buffering-ms", required_argument, 0, OPT_LINE_BUFFERING_MS },
{ "timestamp", no_argument, 0, OPT_TIMESTAMP },
{ "autoconnect", required_argument, 0, OPT_AUTOCONNECT },
{ "defaultname", required_argument, 0, OPT_DEFAULT_NAME },
{ "defaultbufsize", required_argument, 0, OPT_DEFAULT_BUFSIZE },
{ 0, 0, 0, 0 },
};
Cons_svr *cons = new Cons_svr("cons");
if (!registry.register_obj(cons, "cons"))
{
printf("Registering 'cons' server failed!\n");
return 1;
}
My_mux *current_mux = 0;
Fe *current_fe = 0;
typedef std::set<std::string> Str_vector;
Str_vector ac_consoles;
const char *default_name = "cons";
while (1)
{
int optidx = 0;
int c = getopt_long(argc, const_cast<char *const*>(argv),
"am:f:klc:n:B:t", opts, &optidx);
if (c == -1)
break;
switch (c)
{
case OPT_SHOW_ALL:
config.default_show_all = true;
break;
case OPT_MUX:
current_mux = new My_mux(cons->ctl(), optarg);
cons->add(current_mux);
if (!ac_consoles.empty())
printf("WARNING: Ignoring all previous auto-connect-consoles.\n");
break;
case OPT_FE:
if (!current_mux)
{
printf("ERROR: need to instantiate a muxer (--mux) before\n"
"using the --frontend option.\n");
break;
}
{
L4::Cap<L4::Vcon> cap
= L4Re::Env::env()->get_cap<L4::Vcon>(optarg);
if (!cap)
{
printf("ERROR: Frontend capability '%s' invalid.\n", optarg);
break;
}
current_fe = new Fe(cap, &registry);
current_mux->add_frontend(current_fe);
}
break;
case OPT_KEEP:
config.default_keep = true;
break;
case OPT_TIMESTAMP:
config.default_timestamp = true;
break;
case OPT_AUTOCONNECT:
if (current_mux)
current_mux->add_auto_connect_console(optarg);
else
ac_consoles.insert(optarg);
break;
case OPT_DEFAULT_NAME:
default_name = optarg;
break;
case OPT_DEFAULT_BUFSIZE:
Vcon_client::default_obuf_size(atoi(optarg));
break;
case OPT_NO_LINE_BUFFERING:
config.default_line_buffering = false;
break;
case OPT_LINE_BUFFERING_MS:
config.default_line_buffering_ms = atoi(optarg);
break;
}
}
// now check if we had any explicit options
if (!current_mux)
{
current_mux = new My_mux(cons->ctl(), default_name);
cons->add(current_mux);
current_fe = new Fe(L4Re::Env::env()->log(), &registry);
current_mux->add_frontend(current_fe);
for (Str_vector::const_iterator i = ac_consoles.begin();
i != ac_consoles.end(); ++i)
current_mux->add_auto_connect_console(*i);
}
ac_consoles.clear();
server.loop<L4::Runtime_error>(&registry);
return 0;
}

View File

@@ -0,0 +1,18 @@
/*
* (c) 2012-2013 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "frontend.h"
#include "client.h"
#include "input_mux.h"
#include "output_mux.h"
class Mux : public Input_mux, public Output_mux
{
public:
virtual void add_frontend(Frontend *) = 0;
};

View File

@@ -0,0 +1,583 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "frontend.h"
#include "client.h"
#include "mux_impl.h"
#include <algorithm>
#include <cassert>
void Pbuf::flush()
{
_sink->write(_b, _p);
_p = 0;
}
void Pbuf::checknflush(int n)
{
char *x = 0;
assert(_p + n <= size());
x = (char*)memchr(_b, '\n', _p + n);
if (x)
{
int rem = n - (x - _b + 1 - _p);
_p = x - _b + 1;
flush();
if (rem)
memmove(_b, x + 1, rem);
_p = rem;
}
else
_p += n;
}
void Pbuf::printf(char const *fmt, ...)
{
if (!fits(strlen(fmt) + 50))
flush();
int n;
va_list arg;
va_start(arg, fmt);
n = vsnprintf(_b + _p, size() - _p, fmt, arg);
va_end(arg);
if (n > 0)
checknflush(std::min<unsigned long>(n, size() - _p));
}
void Pbuf::outnstring(char const *str, unsigned long len)
{
assert(len <= size());
if (!fits(len))
flush();
memcpy(_b + _p, str, len);
checknflush(len);
}
namespace {
class Mux_client : public Client
{
public:
Mux_client(Mux *mux) : Client("CONS", 0, 512, 512, Key(), false, 0, nullptr,
nullptr)
{ output_mux(mux); }
void trigger() const {}
};
}
Mux_i::Mux_i(Controller *ctl, char const *name)
: _fe(), _self_client(new Mux_client(this)),
ob(this), _last_output_client(0),
_connected(_self_client), _tag_len(8), _ctl(ctl),
_name(name), _seq_str("[Ctrl-E]")
{
}
void
Mux_i::do_client_output(Client const *v, int taillines, bool add_nl)
{
Client::Buf const *b = v->wbuf();
Client::Buf::Index p = b->tail();
if (taillines != -1)
{
p = b->head();
while (taillines--)
{
p = b->find_backwards('\n', p);
if (p == b->tail())
break;
}
if (p != b->head() && (*b)[p] == '\n')
++p;
}
b->write(p, b->head(), _self_client);
flush(_self_client);
if (add_nl)
{
p = b->head();
if (p != b->tail() && (*b)[--p] != '\n')
write("\r\n", 2);
}
}
void
Mux_i::cat(Client *v, bool add_nl)
{
do_client_output(v, -1, add_nl);
}
void
Mux_i::tail(Client *v, int numlines, bool add_nl)
{
do_client_output(v, numlines, add_nl);
}
void
Mux_i::write_tag(Client *client)
{
std::string const & n = client->tag();
ob.outnstring(n.c_str(), cxx::min<unsigned long>(n.size(), _tag_len));
if (n.size() < _tag_len)
ob.outnstring(" ", _tag_len - n.size());
if (client->output_line_preempted())
ob.printf(": ");
else
ob.printf("| ");
}
void
Mux_i::flush(Client *)
{
ob.flush();
}
void
Mux_i::write(Client *client, const char *msg, unsigned len_msg)
{
bool tagged = client != _connected;
int color = client->color();
if (_last_output_client
&& _last_output_client != client
&& _last_output_client->output_line_preempted()
&& _last_output_client->color()
&& tagged)
ob.printf("\033[0m");
int input_check_cnt = 0;
while (len_msg > 0 && msg[0])
{
if (_last_output_client != client)
{
if (tagged && color)
ob.printf("\033[%s3%dm", (color & 8) ? "01;" : "", (color & 7));
else
ob.printf("\033[0m");
if (_last_output_client != 0)
ob.printf("\r\n");
if (tagged)
write_tag(client);
}
long i;
for (i = 0; i < (long)len_msg; ++i)
if (msg[i] == '\n' || msg[i] == 0 || i == (long)ob.size())
break;
ob.outnstring(msg, i);
if (i < (long)len_msg && msg[i] == '\n')
{
if (tagged && color)
ob.printf("\033[0m\n");
else
ob.printf("\n");
client->output_line_done();
_last_output_client = 0;
++i;
}
else
{
_last_output_client = client;
client->preempt_output_line();
}
msg += i;
len_msg -= i;
input_check_cnt += i;
if (input_check_cnt > 1500)
{
for (Fe_iter i = const_cast<Fe_list&>(_fe).begin();
i != _fe.end(); ++i)
if (i->check_input())
{
ob.printf("[Got input, stopping output.]\n");
return;
}
input_check_cnt = 0;
}
}
}
int
Mux_i::vprintf(const char *fmt, va_list args)
{
char buffer[1024];
int n = vsnprintf(buffer, sizeof(buffer), fmt, args);
if (n > 0)
_self_client->cooked_write(buffer, n);
return n;
}
void
Mux_i::add_frontend(Frontend *f)
{
f->input_mux(this);
_fe.add(f);
if (!is_connected())
prompt();
}
int
Mux_i::vsys_msg(const char *fmt, va_list args)
{
char buffer[1024];
buffer[0] = '\n';
int n = vsnprintf(buffer + 1, sizeof(buffer) - 1, fmt, args);
if (n > 0)
{
_self_client->cooked_write(buffer, n+1);
ob.flush();
}
if (!is_connected())
prompt();
return 0;
}
void
Mux_i::connect(Client *client)
{
client->skip_unwritten();
tail(client, 10, false);
_last_output_client = client;
_pre_connect_output = client->output_mux();
if (_pre_connect_output)
_pre_connect_output->disconnect(client);
_connected = client;
client->output_mux(this);
}
void
Mux_i::disconnect(Client *client, bool show_prompt)
{
if (_connected != client || client == _self_client)
return;
_connected = _self_client;
client->output_mux(_pre_connect_output);
if (show_prompt)
prompt();
}
void
Mux_i::show(Client *c)
{
Output_mux *m = c->output_mux();
if (m == this)
return;
if (m)
m->disconnect(c);
c->skip_unwritten();
c->output_mux(this);
}
void
Mux_i::hide(Client *c)
{
Output_mux *m = c->output_mux();
if (m)
{
m->disconnect(c);
c->output_mux(0);
}
}
void Mux_i::prompt() const
{
char _b[25];
char *p = _b;
for (unsigned i = 0; i < sizeof(_b)-2 && _name[i]; ++i, ++p)
*p = _name[i];
p[0] = '>';
p[1] = ' ';
_self_client->cooked_write(_b, p - _b + 2);
if (_inp.p)
_self_client->cooked_write(_inp.b, _inp.p);
}
bool
Mux_i::inject_to_read_buffer(char c)
{
const l4_vcon_attr_t *a = _connected->attr();
if (a->i_flags & L4_VCON_INLCR && c == '\n')
c = '\r';
if (a->i_flags & L4_VCON_IGNCR && c == '\r')
return 0;
if (a->i_flags & L4_VCON_ICRNL && c == '\r')
c = '\n';
bool do_trigger = _connected->rbuf()->put(c);
if (_connected->attr()->l_flags & L4_VCON_ECHO)
write(&c, 1);
return do_trigger;
}
bool
Mux_i::handle_cmd_seq_global(const char k)
{
switch (k)
{
case 'h':
case 's':
{
Client *c = is_connected() ? _connected : 0;
for (auto const i : _ctl->clients)
{
if (k == 's')
{
if (!i->output_mux())
show(i);
}
else
{
if (i->output_mux() == this && &*i != c)
hide(i);
}
}
}
break;
default:
{
Controller::Client_iter c
= std::find_if(_ctl->clients.begin(),
Controller::Client_iter(_ctl->clients.end()),
Client::Equal_key(Client::Key(k)));
if (c != _ctl->clients.end())
{
if (*c != _connected)
{
disconnect(_connected, false);
printf("------------- Connecting to '%s' -------------\n",
(*c)->tag().c_str());
connect(*c);
return true;
}
}
}
};
return false;
}
void
Mux_i::clear_seq_print(bool erase)
{
for (unsigned a = 0; a < strlen(_seq_str); ++a)
_connected->write(erase ? "\b \b" : "\b");
flush(_connected);
}
void
Mux_i::handle_vcon_input(cxx::String const &buf)
{
if (buf.empty())
return;
bool do_trigger = false;
for (cxx::String::Index i = buf.start(); !buf.eof(i); ++i)
{
if (_inp.in_cmd_seq == 1)
{
_inp.in_cmd_seq = 0;
switch (buf[i])
{
case '.':
disconnect(_connected);
handle_prompt_input(buf.substr(i + 1));
return;
case 'q':
clear_seq_print(false);
do_trigger |= inject_to_read_buffer(27);
break;
case 'e':
clear_seq_print(false);
do_trigger |= inject_to_read_buffer('');
break;
case 'z':
clear_seq_print(false);
do_trigger |= inject_to_read_buffer('');
break;
case 'c':
clear_seq_print(false);
do_trigger |= inject_to_read_buffer(3);
break;
case 'l':
clear_seq_print(true);
do_trigger |= _connected->rbuf()->put_break();
_connected->write("[Break]");
flush(_connected);
break;
default:
clear_seq_print(true);
if (handle_cmd_seq_global(buf[i]))
return;
do_trigger |= inject_to_read_buffer(5);
break;
}
}
else if (buf[i] == 5) // ctrl-e
{
_connected->write(_seq_str);
flush(_connected);
_inp.in_cmd_seq++;
}
else
do_trigger |= inject_to_read_buffer(buf[i]);
if (do_trigger)
_connected->trigger();
}
}
void
Mux_i::handle_prompt_input(cxx::String const &buf)
{
if (buf.empty())
return;
for (cxx::String::Index i = buf.start(); !buf.eof(i); ++i)
{
if (_inp.in_cmd_seq)
{
handle_cmd_seq_global(buf[i]);
_inp.in_cmd_seq = 0;
}
else if (_inp.esc.in_seq)
{
if (_inp.esc.in_seq == 1)
{
if (buf[i] == '[')
++_inp.esc.in_seq;
else
_inp.esc.in_seq = 0;
}
else if (_inp.esc.in_seq == 2)
{
switch (buf[i])
{
case 'A': _inp.esc.last_key(Mux_input_buf::Esc::Up); break;
case 'B': _inp.esc.last_key(Mux_input_buf::Esc::Down); break;
case 'C': _inp.esc.last_key(Mux_input_buf::Esc::Right); break;
case 'D': _inp.esc.last_key(Mux_input_buf::Esc::Left); break;
case '5': _inp.esc.key = Mux_input_buf::Esc::Page_up;
++_inp.esc.in_seq; break;
case '6': _inp.esc.key = Mux_input_buf::Esc::Page_down;
++_inp.esc.in_seq; break;
default: if (0) printf("Ukn-%c-\n", buf[i]); break;
};
}
else if (_inp.esc.in_seq == 3)
{
switch (buf[i])
{
case '~': _inp.esc.mod = Mux_input_buf::Esc::Normal; break;
case '@': _inp.esc.mod = Mux_input_buf::Esc::CtrlShift; break;
case '^': _inp.esc.mod = Mux_input_buf::Esc::Control; break;
case '$': _inp.esc.mod = Mux_input_buf::Esc::Shift; break;
default: _inp.esc.key = Mux_input_buf::Esc::None; break;
};
_inp.esc.in_seq = 0;
}
else
_inp.esc.in_seq = 0;
if (_inp.esc.in_seq == 0)
{
if (0) switch (_inp.esc.key)
{
case Mux_input_buf::Esc::Left: printf("LEFT\n"); break;
case Mux_input_buf::Esc::Right: printf("RIGHT\n"); break;
case Mux_input_buf::Esc::Up: printf("UP\n"); break;
case Mux_input_buf::Esc::Down: printf("DOWN\n"); break;
case Mux_input_buf::Esc::Page_up: printf("PGUP\n"); break;
case Mux_input_buf::Esc::Page_down: printf("PGDOWN\n"); break;
case Mux_input_buf::Esc::None: break;
};
_inp.esc.key = Mux_input_buf::Esc::None;
}
}
else
{
switch (buf[i])
{
case 5: // ctrl-E
_inp.in_cmd_seq++;
break;
case 9: // TAB
{
int cnt = _ctl->complete(this, 0, &_inp);
if (cnt > 0)
{
write("\r", 1);
prompt();
}
}
break;
case 12: // ctrl-L
printf("\033[H\033[2J");
prompt();
break;
case 10:
break;
case 13:
w("\r\n");
_ctl->cmd(this, _inp.string());
_inp.clear();
if (!is_connected())
prompt();
break;
case 127: // BS
if (_inp.p)
{
w("\b \b");
--_inp.p;
}
break;
case 27:
_inp.esc.in_seq++;
break;
default:
_inp.add(buf[i]);
write(&buf[i], 1);
};
}
if (_inp.full())
break;
}
}
void
Mux_i::input(cxx::String const &buf)
{
if (is_connected())
handle_vcon_input(buf);
else
handle_prompt_input(buf);
}

View File

@@ -0,0 +1,160 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "mux.h"
#include "controller.h"
#include <l4/cxx/string>
#include <cstdarg>
#include <cstring>
#include <cstdio>
class Pbuf
{
public:
class Sink
{
public:
virtual void write(char const *buf, unsigned size) const = 0;
};
explicit Pbuf(Sink *sink) : _p(0), _sink(sink) {}
unsigned long size() const { return sizeof(_b); }
void flush();
void printf(char const *fmt, ...) __attribute__((format(printf, 2, 3)));
void outnstring(char const *str, unsigned long len);
private:
bool fits(unsigned l) const { return (_p + l) < sizeof(_b); }
void checknflush(int n);
char _b[1024];
unsigned long _p;
Sink *_sink;
};
class Mux_i : public Mux, private Pbuf::Sink
{
public:
explicit Mux_i(Controller *ctl, char const *name);
~Mux_i() { delete _self_client; }
void write_tag(Client *client);
void write(Client *tag, const char *msg, unsigned len_msg);
void flush(Client *tag);
int vsys_msg(const char *fmt, va_list args);
int vprintf(const char *fmt, va_list args);
void show(Client *c);
void hide(Client *c);
void connect(Client *client);
void disconnect(Client *client, bool show_prompt = true);
void input(cxx::String const &buf);
void add_frontend(Frontend *f);
void cat(Client *c, bool add_nl);
void tail(Client *tag, int numlines, bool add_nl);
bool is_connected() const { return _connected != _self_client; }
char const *name() const { return _name; }
private:
Mux_i(Mux_i const &) = delete;
Mux_i &operator = (Mux_i const &) = delete;
typedef cxx::H_list<Frontend> Fe_list;
typedef Fe_list::Iterator Fe_iter;
typedef Fe_list::Const_iterator Const_fe_iter;
struct Mux_input_buf : public Input_buf
{
char b[512];
unsigned p;
unsigned in_cmd_seq;
struct Esc
{
enum Key { None, Left, Right, Up, Down, Page_up, Page_down };
enum Mod { Normal, CtrlShift, Control, Shift };
Esc() : key(None), mod(Normal), in_seq(0) {}
void last_key(Key k) { key = k; in_seq = 0; }
Key key;
Mod mod;
unsigned in_seq;
};
Esc esc;
Mux_input_buf() : p(0), in_cmd_seq(0) {}
cxx::String string() const { return cxx::String(b, p); }
void clear() { p = 0; }
void add(char c) { if (p < sizeof(b)) b[p++] = c; }
bool full() const { return p == sizeof(b); }
int replace(cxx::String &del, cxx::String add)
{
if (p + add.len() - del.len() >= sizeof(b))
return -L4_ENOMEM;
if (del.len() == 0 && add.len() == 0)
return -L4_EINVAL;
if (del.start() == 0)
return -L4_EINVAL;
if (add.start() == 0 || add.len() == 0)
add.start(del.start());
memmove((char *)del.start() + add.len(), del.end(),
(char *)(b + p) - del.end());
memcpy((void *)del.start(), add.start(), add.len());
p += add.len() - del.len();
return 0;
}
};
void w(const char *s) const { write(s, strlen(s)); }
void prompt() const;
void handle_prompt_input(cxx::String const &buf);
void handle_vcon_input(cxx::String const &buf);
bool handle_cmd_seq_global(const char k);
void clear_seq_print(bool erase);
bool inject_to_read_buffer(char c);
void do_client_output(Client const *v, int taillines, bool add_nl);
// Sink::write
void write(char const *buf, unsigned size) const
{
for (Fe_iter i = const_cast<Fe_list&>(_fe).begin(); i != _fe.end(); ++i)
{
for (unsigned l = 0; l < size; )
{
int r = i->write(buf + l, size - l);
if (r < 0)
break;
l += r;
}
}
}
Fe_list _fe;
Client *_self_client;
Pbuf ob;
Client *_last_output_client;
Client *_connected;
Output_mux *_pre_connect_output;
unsigned _tag_len;
Mux_input_buf _inp;
Controller *_ctl;
char const *_name;
char const *_seq_str;
};

View File

@@ -0,0 +1,56 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <cstdarg>
class Client;
class Output_mux
{
public:
virtual void write(Client *tag, char const *buffer, unsigned size) = 0;
virtual void cat(Client *tag, bool add_nl) = 0;
virtual void tail(Client *tag, int numlines, bool add_nl) = 0;
virtual void flush(Client *tag) = 0;
virtual int vsys_msg(const char *fmt, va_list args) = 0;
int sys_msg(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
virtual int vprintf(const char *fmt, va_list args) = 0;
int printf(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
virtual char const *name() const = 0;
virtual void show(Client *c) = 0;
virtual void hide(Client *c) = 0;
virtual void connect(Client *c) = 0;
virtual void disconnect(Client *c, bool show_prompt = true) = 0;
virtual ~Output_mux() = 0;
};
inline Output_mux::~Output_mux() {}
inline int
Output_mux::sys_msg(const char *fmt, ...)
{
int l;
va_list a;
va_start(a, fmt);
l = vsys_msg(fmt, a);
va_end(a);
return l;
}
inline int
Output_mux::printf(const char *fmt, ...)
{
int l;
va_list a;
va_start(a, fmt);
l = vprintf(fmt, a);
va_end(a);
return l;
}

View File

@@ -0,0 +1,94 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "registry.h"
#include <cstdio>
#include <l4/re/error_helper>
namespace {
class Del_handler : public L4::Irqep_t<Del_handler>
{
private:
Registry *_r;
public:
explicit Del_handler(Registry *r) : _r(r) {}
void handle_irq()
{ _r->gc_step(); }
};
}
Registry::Registry(L4::Ipc_svr::Server_iface *sif)
: L4Re::Util::Object_registry(sif)
{
using L4Re::chkcap;
L4::Cap<L4::Irq> _del_irq = chkcap(register_irq_obj(new Del_handler(this)));
_server->register_del_irq(_del_irq);
};
L4::Cap<void>
Registry::register_obj(Server_object *o, char const *service)
{
using L4::Kobject;
using L4::cap_cast;
using L4::Cap;
Cap<Kobject> r
= cap_cast<Kobject>(L4Re::Util::Object_registry::register_obj(o, service));
if (!r)
return r;
_life.add(o);
r->dec_refcnt(1);
return r;
}
L4::Cap<void>
Registry::register_obj(Server_object *o)
{
using L4::Kobject;
using L4::cap_cast;
using L4::Cap;
Cap<Kobject> r = cap_cast<Kobject>(L4Re::Util::Object_registry::register_obj(o));
if (!r)
return r;
_life.add(o);
r->dec_refcnt(1);
return r;
}
void
Registry::gc_step()
{
if (0)
printf("GC: step this=%p _life = %p\n", this, *_life.begin());
for (Obj_iter n = _life.begin(); n != _life.end(); )
{
if (!n->obj_cap() || !n->obj_cap().validate().label())
{
if (0)
printf("GC: object=%p\n", *n);
unregister_obj(*n);
Server_object *o = *n;
n = _life.erase(n);
if (o->collected())
delete o;
}
else
++n;
}
}
void
Registry::gc_sweep()
{
}

View File

@@ -0,0 +1,35 @@
/*
* (c) 2012-2013 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "server.h"
#include <l4/re/util/object_registry>
class Registry : public L4Re::Util::Object_registry
{
private:
Registry(Registry const &);
void operator = (Registry const &);
typedef cxx::H_list<Server_object> Obj_list;
typedef Obj_list::Iterator Obj_iter;
Obj_list _life;
Obj_list _sweep;
public:
Registry(L4::Ipc_svr::Server_iface *sif);
void gc_sweep();
void gc_step();
// Tell the compiler that we are aware of the base function to prevent a
// compiler warning.
using L4Re::Util::Object_registry::register_obj;
L4::Cap<void> register_obj(Server_object *o, char const *service);
L4::Cap<void> register_obj(Server_object *o);
};

View File

@@ -0,0 +1,18 @@
/*
* (c) 2012-2013 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/hlist>
#include <l4/sys/cxx/ipc_epiface>
class Server_object
: public L4::Epiface,
public cxx::H_list_item
{
public:
virtual bool collected() = 0;
};

View File

@@ -0,0 +1,66 @@
/*
* (c) 2012-2013 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "vcon_client.h"
#include <l4/sys/typeinfo_svr>
unsigned Vcon_client::_dfl_obufsz = Vcon_client::Default_obuf_size;
void
Vcon_client::vcon_write(const char *buf, unsigned size) throw()
{ cooked_write(buf, size); }
unsigned
Vcon_client::vcon_read(char *buf, unsigned size) throw()
{
char const *d = 0;
const int offset = 0;
unsigned status = 0;
unsigned r = rbuf()->get(offset, &d);
if (r > size)
r = size;
if (rbuf()->is_next_break(offset))
{
status |= L4_VCON_READ_STAT_BREAK;
rbuf()->clear_next_break();
}
unsigned i = 0;
for (; i < r; ++i)
{
if (rbuf()->is_next_break(offset + i))
break;
buf[i] = d[i];
}
rbuf()->clear(i);
if ( i == r
&& (r < size || rbuf()->empty()))
status |= L4_VCON_READ_STAT_DONE;
if (!rbuf()->empty())
i = size + 1;
return i | status;
}
int
Vcon_client::vcon_set_attr(l4_vcon_attr_t const *a) throw()
{
_attr = *a;
return 0;
}
int
Vcon_client::vcon_get_attr(l4_vcon_attr_t *attr) throw()
{
*attr = _attr;
return 0;
}

View File

@@ -0,0 +1,58 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "client.h"
#include "controller.h"
#include "server.h"
#include <l4/re/util/icu_svr>
#include <l4/re/util/vcon_svr>
#include <l4/re/util/object_registry>
class Vcon_client
: public L4::Epiface_t<Vcon_client, L4::Vcon, Server_object>,
public L4Re::Util::Icu_cap_array_svr<Vcon_client>,
public L4Re::Util::Vcon_svr<Vcon_client>,
public Client
{
public:
typedef L4Re::Util::Icu_cap_array_svr<Vcon_client> Icu_svr;
typedef L4Re::Util::Vcon_svr<Vcon_client> My_vcon_svr;
Vcon_client(std::string const &name, int color, size_t bufsz, Key key,
bool line_buffering, unsigned line_buffering_ms,
L4Re::Util::Object_registry *, L4::Ipc_svr::Server_iface *sif,
Controller *ctl)
: Icu_svr(1, &_irq),
Client(name, color, 512, bufsz < 512 ? _dfl_obufsz : bufsz, key,
line_buffering, line_buffering_ms, sif, ctl)
{}
void vcon_write(const char *buffer, unsigned size) throw();
unsigned vcon_read(char *buffer, unsigned size) throw();
int vcon_set_attr(l4_vcon_attr_t const *a) throw();
int vcon_get_attr(l4_vcon_attr_t *attr) throw();
const l4_vcon_attr_t *attr() const { return &_attr; }
void trigger() const { _irq.trigger(); }
bool collected() { return Client::collected(); }
static void default_obuf_size(unsigned bufsz)
{
_dfl_obufsz = cxx::max(512U, cxx::min(16U << 20, bufsz));
}
private:
enum { Default_obuf_size = 40960 };
static unsigned _dfl_obufsz;
Icu_svr::Irq _irq;
};

View File

@@ -0,0 +1,25 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "vcon_fe.h"
#include <l4/re/error_helper>
Vcon_fe::Vcon_fe(L4::Cap<L4::Vcon> con, L4Re::Util::Object_registry *r)
: Vcon_fe_base(con, r)
{
L4Re::chksys(_vcon->bind(0, obj_cap()),
"binding to input IRQ");
l4_vcon_attr_t attr = { 0, 0, 0 };
_vcon->get_attr(&attr);
attr.l_flags &= ~(L4_VCON_ECHO | L4_VCON_ICANON);
attr.o_flags &= ~(L4_VCON_ONLCR | L4_VCON_OCRNL | L4_VCON_ONLRET);
attr.i_flags &= ~(L4_VCON_INLCR | L4_VCON_IGNCR | L4_VCON_ICRNL);
_vcon->set_attr(&attr);
handle_pending_input();
}

View File

@@ -0,0 +1,20 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "vcon_fe_base.h"
#include <l4/re/util/object_registry>
class Vcon_fe : public Vcon_fe_base
{
public:
Vcon_fe(L4::Cap<L4::Vcon> con, L4Re::Util::Object_registry *r);
int write(char const *buf, unsigned sz)
{ return do_write(buf, sz); }
};

View File

@@ -0,0 +1,39 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "vcon_fe_base.h"
#include <l4/re/error_helper>
Vcon_fe_base::Vcon_fe_base(L4::Cap<L4::Vcon> con,
L4Re::Util::Object_registry *r)
: _vcon(con)
{
r->register_irq_obj(this);
}
int
Vcon_fe_base::do_write(char const *buf, unsigned sz)
{
for (unsigned s = sz; s; )
{
int l = _vcon->write(buf, s);
if (l < 0)
break;
s -= l;
buf += l;
}
return sz;
}
void
Vcon_fe_base::handle_pending_input()
{
char data[8];
while (_vcon->read(data, sizeof(data)) > (int)sizeof(data))
;
}

View File

@@ -0,0 +1,43 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "frontend.h"
#include <l4/sys/vcon>
#include <l4/re/util/object_registry>
#include <l4/sys/cxx/ipc_epiface>
class Vcon_fe_base :
public Frontend,
public L4::Irqep_t<Vcon_fe_base>
{
public:
Vcon_fe_base(L4::Cap<L4::Vcon> con, L4Re::Util::Object_registry *r);
void handle_irq()
{
char buf[30];
const int sz = sizeof(buf);
int r;
do
{
r = _vcon->read(buf, sz);
if (_input && r > 0)
_input->input(cxx::String(buf, r > sz ? sz : r));
}
while (r > sz);
}
protected:
int do_write(char const *buf, unsigned sz);
bool check_input() { return _vcon->read(0, 0) > 0; }
void handle_pending_input();
L4::Cap<L4::Vcon> _vcon; // FIXME: could be an auto cap
};

View File

@@ -0,0 +1,93 @@
#include "virtio_client.h"
unsigned Virtio_cons::_dfl_obufsz = Virtio_cons::Default_obuf_size;
void
Virtio_cons::handle_tx()
{
using namespace L4virtio::Svr;
Virtqueue &q = _q[Tx];
Request_processor p;
Virtqueue::Head_desc h;
Buffer b;
while (auto r = q.next_avail())
{
h = p.start(mem_info(), r, &b);
for (;;)
{
cooked_write(b.pos, b.left);
if (!p.next(mem_info(), &b))
break;
}
q.finish(h, this);
}
}
void
Virtio_cons::handle_rx()
{
l4_uint32_t total = 0;
char const *d = 0;
unsigned rs = rbuf()->get(0, &d);
if (!rs)
return; // no input
using namespace L4virtio::Svr;
Virtqueue &q = _q[Rx];
Request_processor p;
Virtqueue::Head_desc h;
Buffer b;
Data_buffer src;
src.pos = const_cast<char *>(d);
src.left = rs;
for (;;)
{
auto r = q.next_avail();
h = p.start(mem_info(), r, &b);
total = 0;
for (;;)
{
total += src.copy_to(&b);
if (!src.left)
{
rbuf()->clear(rs);
rs = rbuf()->get(0, &d);
if (!rs)
{
q.finish(h, this, total);
return;
}
src.pos = const_cast<char *>(d);
src.left = rs;
continue;
}
else if (!p.next(mem_info(), &b))
{
q.finish(h, this, total);
break;
}
}
}
if (h)
q.finish(h, this, total);
}
void
Virtio_cons::kick()
{
if (L4_LIKELY(_q[Tx].ready()))
handle_tx();
if (L4_LIKELY(_q[Rx].ready()))
handle_rx();
}

View File

@@ -0,0 +1,152 @@
/*
* (c) 2012-2013 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "client.h"
#include "controller.h"
#include "server.h"
#include <l4/re/util/object_registry>
#include <l4/l4virtio/server/l4virtio>
#include <l4/l4virtio/l4virtio>
#include <l4/sys/cxx/ipc_epiface>
class Virtio_cons
: public L4virtio::Svr::Device,
public Client,
public L4::Epiface_t<Virtio_cons, L4virtio::Device, Server_object>
{
private:
enum { Rx, Tx };
struct Buffer : L4virtio::Svr::Data_buffer
{
Buffer() = default;
Buffer(L4virtio::Svr::Driver_mem_region const *r,
L4virtio::Svr::Virtqueue::Desc const &d,
L4virtio::Svr::Request_processor const *)
{
pos = static_cast<char *>(r->local(d.addr));
left = d.len;
}
};
struct Host_irq : public L4::Irqep_t<Host_irq>
{
explicit Host_irq(Virtio_cons *s) : s(s) {}
Virtio_cons *s;
void handle_irq()
{ s->kick(); }
};
Host_irq _host_irq;
L4virtio::Svr::Dev_config _dev_config;
L4virtio::Svr::Virtqueue _q[2];
L4Re::Util::Unique_cap<L4::Irq> kick_guest_irq;
enum { Default_obuf_size = 40960 };
static unsigned _dfl_obufsz;
enum { Max_desc = 0x100 };
public:
Virtio_cons(std::string const &name, int color, size_t bufsz, Key key,
bool line_buffering, unsigned line_buffering_ms,
L4Re::Util::Object_registry *r, L4::Ipc_svr::Server_iface *sif,
Controller *ctl)
: L4virtio::Svr::Device(&_dev_config),
Client(name, color, 512, bufsz < 512 ? _dfl_obufsz : bufsz, key,
line_buffering, line_buffering_ms, sif, ctl),
_host_irq(this),
_dev_config(0x44, L4VIRTIO_ID_CONSOLE, 0x20, 2)
{
reset_queue_config(0, Max_desc);
reset_queue_config(1, Max_desc);
r->register_irq_obj(&_host_irq);
init_mem_info(40);
_attr.l_flags = 0;
_attr.i_flags = 0;
_attr.o_flags = 0;
}
void register_single_driver_irq() override
{
kick_guest_irq = L4Re::Util::Unique_cap<L4::Irq>(
L4Re::chkcap(server_iface()->rcv_cap<L4::Irq>(0)));
L4Re::chksys(server_iface()->realloc_rcv_cap(0));
}
void trigger_driver_config_irq() override
{
_dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
kick_guest_irq->trigger();
}
L4::Cap<L4::Irq> device_notify_irq() const override
{ return L4::cap_cast<L4::Irq>(_host_irq.obj_cap()); }
void reset() override
{
for (L4virtio::Svr::Virtqueue &q: _q)
q.disable();
}
template<typename T, unsigned N >
static unsigned array_length(T (&)[N]) { return N; }
int reconfig_queue(unsigned index) override
{
if (index >= array_length(_q))
return -L4_ERANGE;
if (setup_queue(_q + index, index, Max_desc))
return 0;
return -L4_EINVAL;
}
bool check_queues() override
{
for (L4virtio::Svr::Virtqueue &q: _q)
if (!q.ready())
{
reset();
printf("failed to start queues\n");
return false;
}
return true;
}
void notify_queue(L4virtio::Svr::Virtqueue *queue)
{
//printf("%s\n", __func__);
if (queue->no_notify_guest())
return;
_dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
kick_guest_irq->trigger();
}
bool collected() override { return Client::collected(); }
void kick();
void handle_tx();
void handle_rx();
void trigger() const override { const_cast<Virtio_cons*>(this)->kick(); }
static void default_obuf_size(unsigned bufsz)
{
_dfl_obufsz = cxx::max(512U, cxx::min(16U << 20, bufsz));
}
Server_iface *server_iface() const override
{ return Server_object::server_iface(); }
};