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

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(); }
};