mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-19 15:43:56 +00:00
Imported Genode release 11.11
This commit is contained in:
committed by
Christian Helmuth
parent
6bcc9aef0e
commit
da4e1feaa5
26
gems/src/server/http_block/README
Normal file
26
gems/src/server/http_block/README
Normal file
@ -0,0 +1,26 @@
|
||||
This directory contains a HTTP client that implements Genode's block session
|
||||
interface as a front-end. This way you can incorporate arbitrary files via.
|
||||
HTTP requests and export them as a block device within Genode.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Config file snippet:
|
||||
|
||||
!<start name="http_blkdrv">
|
||||
! <resource name="RAM" quantum="1M" />
|
||||
! <provides><service name="Block"/></provides> <!-- Mandatory -->
|
||||
! <config>
|
||||
!
|
||||
! <!-- File to export as a block device.
|
||||
! Syntax:'http:://<host>[:port]/<path to file>' -->
|
||||
! <uri>http://kc86.genode.labs:80/file.iso</uri>
|
||||
!
|
||||
! <!- The block size of the exported block device. This is optional, the
|
||||
! default is 512 bytes. -->
|
||||
! <block-size>2048</block-size>
|
||||
!
|
||||
! </config>
|
||||
!</start>
|
||||
|
305
gems/src/server/http_block/http.cc
Normal file
305
gems/src/server/http_block/http.cc
Normal file
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* \brief HTTP protocol parts
|
||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
||||
* \date 2010-08-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#include <base/child.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/sleep.h>
|
||||
#include <lwip/genode.h>
|
||||
|
||||
extern "C" {
|
||||
#include <lwip/netdb.h>
|
||||
}
|
||||
|
||||
#include "http.h"
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
/*
|
||||
* Debugging output
|
||||
*/
|
||||
static const int verbose = 0;
|
||||
|
||||
enum {
|
||||
/* HTTP status codes */
|
||||
HTTP_SUCC_OK = 200,
|
||||
HTTP_SUCC_PARTIAL = 206,
|
||||
|
||||
/* Size of our local buffer */
|
||||
HTTP_BUF = 2048,
|
||||
};
|
||||
|
||||
/* Tokenizer policy */
|
||||
struct Scanner_policy_file
|
||||
{
|
||||
static bool identifier_char(char c, unsigned /* i */)
|
||||
{
|
||||
return c != ':' && c != 0 && c != ' ' && c != '\n';
|
||||
}
|
||||
};
|
||||
|
||||
typedef ::Genode::Token<Scanner_policy_file> Http_token;
|
||||
|
||||
|
||||
void Http::cmd_head()
|
||||
{
|
||||
const char *http_templ = "%s %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"\r\n";
|
||||
|
||||
int length = snprintf(_http_buf, HTTP_BUF, http_templ, "HEAD", _path, _host);
|
||||
|
||||
if (lwip_write(_fd, _http_buf, length) != length) {
|
||||
PERR("Write error");
|
||||
throw Http::Socket_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Http::connect()
|
||||
{
|
||||
_fd = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (_fd < 0) {
|
||||
PERR("No socket avaiable");
|
||||
throw Http::Socket_error();
|
||||
}
|
||||
|
||||
if (lwip_connect(_fd, _info->ai_addr, sizeof(*(_info->ai_addr))) < 0) {
|
||||
PERR("Connect failed");
|
||||
throw Http::Socket_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Http::reconnect(){ lwip_close(_fd); connect(); }
|
||||
|
||||
|
||||
void Http::resolve_uri()
|
||||
{
|
||||
struct addrinfo *info;
|
||||
if (lwip_getaddrinfo(_host, _port, 0, &info)) {
|
||||
PERR("Error: Host %s not found", _host);
|
||||
throw Http::Uri_error();
|
||||
}
|
||||
|
||||
env()->heap()->alloc(sizeof(struct addrinfo), &_info);
|
||||
Genode::memcpy(_info, info, sizeof(struct addrinfo));
|
||||
}
|
||||
|
||||
|
||||
Genode::size_t Http::read_header()
|
||||
{
|
||||
bool header = true; size_t i = 0;
|
||||
|
||||
while (header) {
|
||||
if (!lwip_read(_fd, &_http_buf[i], 1))
|
||||
throw Http::Socket_closed();
|
||||
|
||||
/* DEBUG: Genode::printf("%c", _http_buf[i]); */
|
||||
|
||||
if (i >= 3 && _http_buf[i - 3] == '\r' && _http_buf[i - 2] == '\n'
|
||||
&& _http_buf[i - 1] == '\r' && _http_buf[i - 0] == '\n')
|
||||
header = false;
|
||||
|
||||
if (++i >= HTTP_BUF) {
|
||||
PERR("Buffer overflow");
|
||||
throw Http::Socket_error();
|
||||
}
|
||||
}
|
||||
|
||||
/* scan for status code */
|
||||
Http_token t(_http_buf, i);
|
||||
for (int count = 0;; t = t.next()) {
|
||||
|
||||
if (t.type() != Http_token::IDENT)
|
||||
continue;
|
||||
|
||||
if (count) {
|
||||
ascii_to(t.start(), &_http_ret);
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
void Http::get_capacity()
|
||||
{
|
||||
cmd_head();
|
||||
size_t len = read_header();
|
||||
char buf[32];
|
||||
Http_token t(_http_buf, len);
|
||||
|
||||
bool key = false;
|
||||
while (t) {
|
||||
|
||||
if (t.type() != Http_token::IDENT) {
|
||||
t = t.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key) {
|
||||
ascii_to(t.start(), &_size);
|
||||
|
||||
if (verbose)
|
||||
PDBG("File size: %zu bytes", _size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
t.string(buf, 32);
|
||||
|
||||
if (!Genode::strcmp(buf, "Content-Length", 32))
|
||||
key = true;
|
||||
|
||||
t = t.next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Http::do_read(void * buf, size_t size)
|
||||
{
|
||||
size_t buf_fill = 0;
|
||||
|
||||
while (buf_fill < size) {
|
||||
|
||||
int part;
|
||||
if ((part = lwip_read(_fd, (void *)((addr_t)buf + buf_fill),
|
||||
size - buf_fill)) <= 0) {
|
||||
PERR("Error: Reading data (%d)", errno);
|
||||
throw Http::Socket_error();
|
||||
}
|
||||
|
||||
buf_fill += part;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
PDBG("Read %zu/%zu", buf_fill, size);
|
||||
}
|
||||
|
||||
|
||||
Http::Http(char *uri, size_t length) : _port((char *)"80")
|
||||
{
|
||||
env()->heap()->alloc(HTTP_BUF, &_http_buf);
|
||||
|
||||
/* parse URI */
|
||||
parse_uri(uri, length);
|
||||
|
||||
/* search for host */
|
||||
resolve_uri();
|
||||
|
||||
/* connect to host */
|
||||
connect();
|
||||
|
||||
/* retrieve file info */
|
||||
get_capacity();
|
||||
}
|
||||
|
||||
|
||||
Http::~Http()
|
||||
{
|
||||
env()->heap()->free(_host, Genode::strlen(_host) + 1);
|
||||
env()->heap()->free(_path, Genode::strlen(_path) + 2);
|
||||
env()->heap()->free(_http_buf, HTTP_BUF);
|
||||
env()->heap()->free(_info, sizeof(struct addrinfo));
|
||||
}
|
||||
|
||||
|
||||
void Http::parse_uri(char *uri, size_t length)
|
||||
{
|
||||
/* strip possible http prefix */
|
||||
const char *http = "http://";
|
||||
size_t http_len = Genode::strlen(http);
|
||||
if (!strcmp(http, uri, http_len)) {
|
||||
uri += http_len;
|
||||
length -= http_len;
|
||||
}
|
||||
|
||||
/* set host and file path */
|
||||
size_t i;
|
||||
for (i = 0; i < length && uri[i] != '/'; i++) ;
|
||||
|
||||
env()->heap()->alloc(i + 1, &_host);
|
||||
Genode::strncpy(_host, uri, i + 1);
|
||||
|
||||
env()->heap()->alloc(length - i + 1, &_path);
|
||||
Genode::strncpy(_path, uri + i, length - i + 1);
|
||||
|
||||
/* look for port */
|
||||
size_t len = Genode::strlen(_host);
|
||||
for (i = 0; i < len && _host[i] != ':'; i++) ;
|
||||
if (i < len) {
|
||||
_port = &_host[i + 1];
|
||||
_host[i] = '\0';
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
PDBG("Port: %s", _port);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Http::cmd_get(size_t file_offset, size_t size, off_t offset)
|
||||
{
|
||||
if (verbose)
|
||||
PDBG("Read: offs %zu size: %zu I/O offs: %lx", file_offset, size, offset);
|
||||
|
||||
while (true) {
|
||||
|
||||
const char *http_templ = "GET %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Range: bytes=%lu-%lu\r\n"
|
||||
"\r\n";
|
||||
|
||||
int length = snprintf(_http_buf, HTTP_BUF, http_templ, _path, _host,
|
||||
file_offset, file_offset + size - 1);
|
||||
|
||||
if (lwip_write(_fd, _http_buf, length) < 0) {
|
||||
|
||||
if (errno == ESHUTDOWN)
|
||||
reconnect();
|
||||
|
||||
if (lwip_write(_fd, _http_buf, length) < 0)
|
||||
throw Http::Socket_error();
|
||||
}
|
||||
|
||||
try {
|
||||
read_header();
|
||||
} catch (Http::Socket_closed) {
|
||||
reconnect();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_http_ret != HTTP_SUCC_PARTIAL) {
|
||||
PERR("Error: Server returned %u", _http_ret);
|
||||
throw Http::Server_error();
|
||||
}
|
||||
|
||||
do_read((void *)(_base_addr + offset), size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void __attribute__((constructor)) init()
|
||||
{
|
||||
lwip_tcpip_init();
|
||||
|
||||
if (lwip_nic_init(0, 0, 0)) {
|
||||
PERR("DHCP failed");
|
||||
throw -1;
|
||||
}
|
||||
}
|
||||
|
122
gems/src/server/http_block/http.h
Normal file
122
gems/src/server/http_block/http.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* \brief HTTP back-end interface
|
||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
||||
* \date 2010-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _HTTP_H_
|
||||
#define _HTTP_H_
|
||||
|
||||
#include <base/stdint.h>
|
||||
|
||||
struct addrinfo;
|
||||
|
||||
class Http
|
||||
{
|
||||
typedef Genode::size_t size_t;
|
||||
typedef Genode::addr_t addr_t;
|
||||
typedef Genode::off_t off_t;
|
||||
|
||||
private:
|
||||
|
||||
size_t _size; /* number of bytes in file */
|
||||
char *_host; /* host name */
|
||||
char *_port; /* host port */
|
||||
char *_path; /* absolute file path on host */
|
||||
char *_http_buf; /* internal data buffer */
|
||||
unsigned _http_ret; /* HTTP status code */
|
||||
struct addrinfo *_info; /* Resolved address info for host */
|
||||
int _fd; /* Socket file handle */
|
||||
addr_t _base_addr; /* Address of I/O dataspace */
|
||||
|
||||
/*
|
||||
* Send 'HEAD' command
|
||||
*/
|
||||
void cmd_head();
|
||||
|
||||
/*
|
||||
* Connect to host
|
||||
*/
|
||||
void connect();
|
||||
|
||||
/*
|
||||
* Re-connect to host
|
||||
*/
|
||||
void reconnect();
|
||||
|
||||
/*
|
||||
* Set URI of remote file
|
||||
*/
|
||||
void parse_uri(char *uri, size_t length);
|
||||
|
||||
/*
|
||||
* Resolve host
|
||||
*/
|
||||
void resolve_uri();
|
||||
|
||||
/*
|
||||
* Read HTTP header and parse server-status code
|
||||
*/
|
||||
size_t read_header();
|
||||
|
||||
/*
|
||||
* Determine remote-file size
|
||||
*/
|
||||
void get_capacity();
|
||||
|
||||
/*
|
||||
* Read 'size' bytes into buffer
|
||||
*/
|
||||
void do_read(void * buf, size_t size);
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* Constructor (default block size is 512 Bytes, default host port is 80
|
||||
*/
|
||||
Http(char *uri, size_t length);
|
||||
|
||||
/*
|
||||
* Destructor
|
||||
*/
|
||||
~Http();
|
||||
|
||||
/**
|
||||
* Read remote file size
|
||||
*
|
||||
* \return Remote file size in bytes
|
||||
*/
|
||||
size_t file_size() { return _size; }
|
||||
|
||||
/**
|
||||
* Set base address of I/O dataspace
|
||||
*
|
||||
* \param base_addr Base of dataspace
|
||||
*/
|
||||
void base_addr(addr_t base_addr) { _base_addr = base_addr; }
|
||||
|
||||
/**
|
||||
* Send 'GET' command
|
||||
*
|
||||
* \param file_offset Read from offset of remote file
|
||||
* \param size Number of byts to transfer
|
||||
* \param offset Offset in I/O dataspace
|
||||
*/
|
||||
void cmd_get(size_t file_offset, size_t size, off_t offset);
|
||||
|
||||
/* Exceptions */
|
||||
class Exception : public ::Genode::Exception { };
|
||||
class Uri_error : public Exception { };
|
||||
class Socket_error : public Exception { };
|
||||
class Socket_closed : public Exception { };
|
||||
class Server_error : public Exception { };
|
||||
};
|
||||
|
||||
#endif /* _HTTP_H_ */
|
289
gems/src/server/http_block/main.cc
Normal file
289
gems/src/server/http_block/main.cc
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* \brief Block interface for HTTP block driver
|
||||
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
|
||||
* \date 2010-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/thread.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/semaphore.h>
|
||||
#include <block_session/rpc_object.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <root/component.h>
|
||||
#include <os/config.h>
|
||||
|
||||
/* local includes */
|
||||
#include "http.h"
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
namespace Block {
|
||||
|
||||
class Http_interface
|
||||
{
|
||||
private:
|
||||
|
||||
size_t _block_size;
|
||||
Http *_http;
|
||||
|
||||
public:
|
||||
|
||||
Http_interface() : _block_size(512), _http(0) {}
|
||||
|
||||
static Http_interface* obj()
|
||||
{
|
||||
static Http_interface _obj;
|
||||
return &_obj;
|
||||
}
|
||||
|
||||
void block_size(size_t block_size)
|
||||
{
|
||||
_block_size = block_size;
|
||||
}
|
||||
|
||||
size_t block_size() { return _block_size; }
|
||||
|
||||
void base_addr(addr_t base_addr)
|
||||
{
|
||||
_http->base_addr(base_addr);
|
||||
}
|
||||
|
||||
void read(size_t block_nr, size_t block_count, off_t offset)
|
||||
{
|
||||
_http->cmd_get(block_nr * _block_size, block_count * _block_size, offset);
|
||||
}
|
||||
|
||||
size_t block_count()
|
||||
{
|
||||
return _http->file_size() / _block_size;
|
||||
}
|
||||
|
||||
void uri(char *uri, size_t length)
|
||||
{
|
||||
_http = new(env()->heap()) Http(uri, length);
|
||||
}
|
||||
|
||||
Http * http_blk() { return _http; }
|
||||
};
|
||||
|
||||
class Session_component : public Session_rpc_object
|
||||
{
|
||||
private:
|
||||
|
||||
class Tx_thread : public Genode::Thread<8192>
|
||||
{
|
||||
private:
|
||||
|
||||
Session_component *_session;
|
||||
|
||||
public:
|
||||
|
||||
Tx_thread(Session_component *session)
|
||||
: Genode::Thread<8192>("worker"), _session(session) { }
|
||||
|
||||
void entry()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Session_component::Tx::Sink *tx_sink = _session->tx_sink();
|
||||
Block::Packet_descriptor packet;
|
||||
|
||||
_session->tx_ready();
|
||||
|
||||
/* handle requests */
|
||||
while (true) {
|
||||
|
||||
/* blocking-get packet from client */
|
||||
packet = tx_sink->get_packet();
|
||||
if (!packet.valid()) {
|
||||
PWRN("received invalid packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
packet.succeeded(false);
|
||||
|
||||
switch (packet.operation()) {
|
||||
|
||||
case Block::Packet_descriptor::READ:
|
||||
|
||||
try {
|
||||
Http_interface::obj()->read(packet.block_number(),
|
||||
packet.block_count(),
|
||||
packet.offset());
|
||||
packet.succeeded(true);
|
||||
}
|
||||
catch (Http::Socket_error) { PERR("socket error"); }
|
||||
catch (Http::Server_error) { PERR("server error"); }
|
||||
|
||||
break;
|
||||
|
||||
case Block::Packet_descriptor::WRITE:
|
||||
break;
|
||||
|
||||
default:
|
||||
PWRN("received invalid packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* acknowledge packet to the client */
|
||||
if (!tx_sink->ready_to_ack())
|
||||
PDBG("need to wait until ready-for-ack");
|
||||
tx_sink->acknowledge_packet(packet);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Genode::Dataspace_capability _tx_ds; /* buffer for tx channel */
|
||||
Genode::Semaphore _startup_sema; /* thread startup sync */
|
||||
Tx_thread _tx_thread;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param tx_ds dataspace used for tx channel
|
||||
*/
|
||||
Session_component(Genode::Dataspace_capability tx_ds,
|
||||
Genode::Rpc_entrypoint &ep)
|
||||
: Session_rpc_object(tx_ds, ep), _tx_ds(tx_ds),
|
||||
_startup_sema(0), _tx_thread(this)
|
||||
{
|
||||
/*
|
||||
* Map packet stream
|
||||
*/
|
||||
addr_t base = env()->rm_session()->attach(tx_ds);
|
||||
|
||||
Http_interface::obj()->base_addr(base);
|
||||
|
||||
_tx_thread.start();
|
||||
_startup_sema.down();
|
||||
}
|
||||
|
||||
void info(Genode::size_t *blk_count, Genode::size_t *blk_size,
|
||||
Operations *ops)
|
||||
{
|
||||
*blk_count = Http_interface::obj()->block_count();
|
||||
*blk_size = Http_interface::obj()->block_size();
|
||||
ops->set_operation(Packet_descriptor::READ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal indicating that transmit thread is ready
|
||||
*/
|
||||
void tx_ready() { _startup_sema.up(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Allow one client only
|
||||
*/
|
||||
|
||||
/*
|
||||
* Shortcut for single-client root component
|
||||
*/
|
||||
typedef Genode::Root_component<Session_component> Root_component;
|
||||
|
||||
/**
|
||||
* Root component, handling new session requests
|
||||
*/
|
||||
class Root : public Root_component
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Always returns the singleton block-session component
|
||||
*/
|
||||
Session_component *_create_session(const char *args)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Genode::size_t ram_quota =
|
||||
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
||||
Genode::size_t tx_buf_size =
|
||||
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||
|
||||
/* delete ram quota by the memory needed for the session */
|
||||
Genode::size_t session_size = max((Genode::size_t)4096,
|
||||
sizeof(Session_component)
|
||||
+ sizeof(Allocator_avl));
|
||||
if (ram_quota < session_size)
|
||||
throw Root::Quota_exceeded();
|
||||
|
||||
/*
|
||||
* Check if donated ram quota suffices for both
|
||||
* communication buffers. Also check both sizes separately
|
||||
* to handle a possible overflow of the sum of both sizes.
|
||||
*/
|
||||
if (tx_buf_size > ram_quota - session_size) {
|
||||
PERR("insufficient 'ram_quota', got %zd, need %zd",
|
||||
ram_quota, tx_buf_size + session_size);
|
||||
throw Root::Quota_exceeded();
|
||||
}
|
||||
|
||||
return new (md_alloc())
|
||||
Session_component(env()->ram_session()->alloc(tx_buf_size), *ep());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Root(Genode::Rpc_entrypoint *session_ep,
|
||||
Genode::Allocator *md_alloc)
|
||||
: Root_component(session_ep, md_alloc) { }
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void process_config()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Xml_node config_node = config()->xml_node();
|
||||
|
||||
bool uri = false;
|
||||
for (unsigned i = 0; i < config_node.num_sub_nodes(); ++i) {
|
||||
|
||||
Xml_node file_node = config_node.sub_node(i);
|
||||
|
||||
if (file_node.has_type("uri")) {
|
||||
Block::Http_interface::obj()->uri(file_node.content_addr(), file_node.content_size());
|
||||
uri = true;
|
||||
}
|
||||
|
||||
if (file_node.has_type("block-size")) {
|
||||
size_t blk_size;
|
||||
file_node.value(&blk_size);
|
||||
Block::Http_interface::obj()->block_size(blk_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!uri)
|
||||
throw Http::Uri_error();
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
enum { STACK_SIZE = 4*1024 };
|
||||
static Cap_connection cap;
|
||||
static Rpc_entrypoint ep(&cap, STACK_SIZE, "http_block_ep");
|
||||
|
||||
process_config();
|
||||
|
||||
static Block::Root block_root(&ep, env()->heap());
|
||||
env()->parent()->announce(ep.manage(&block_root));
|
||||
sleep_forever();
|
||||
|
||||
return 0;
|
||||
}
|
5
gems/src/server/http_block/target.mk
Normal file
5
gems/src/server/http_block/target.mk
Normal file
@ -0,0 +1,5 @@
|
||||
TARGET = http_block
|
||||
SRC_CC = main.cc http.cc
|
||||
|
||||
LIBS = signal server cxx env lwip libc
|
||||
|
12
gems/src/server/tcp_terminal/README
Normal file
12
gems/src/server/tcp_terminal/README
Normal file
@ -0,0 +1,12 @@
|
||||
TCP terminal is a service that provides Genode's terminal-session interface
|
||||
via individual TCP connections. It supports multiple clients. The TCP port
|
||||
to be used for each client is defined in as session policy in the config node
|
||||
of the TCP server:
|
||||
|
||||
! <config>
|
||||
! <policy label="client" port="8181"/>
|
||||
! <policy label="another_client" port="8282"/>
|
||||
! </config>
|
||||
|
||||
For an example of how to use the TCP terminal, please refer to the run script
|
||||
at 'gems/run/tcp_terminal.run'.
|
514
gems/src/server/tcp_terminal/main.cc
Normal file
514
gems/src/server/tcp_terminal/main.cc
Normal file
@ -0,0 +1,514 @@
|
||||
/*
|
||||
* \brief Service providing the 'Terminal_session' interface via TCP
|
||||
* \author Norman Feske
|
||||
* \date 2011-09-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list.h>
|
||||
#include <util/misc_math.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/heap.h>
|
||||
#include <root/component.h>
|
||||
#include <terminal_session/terminal_session.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
/* socket API */
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
static bool const verbose = true;
|
||||
|
||||
|
||||
class Open_socket : public Genode::List<Open_socket>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Socket descriptor for listening to a new TCP connection
|
||||
*/
|
||||
int _listen_sd;
|
||||
|
||||
/**
|
||||
* Socket descriptor for open TCP connection
|
||||
*/
|
||||
int _sd;
|
||||
|
||||
/**
|
||||
* Signal handler to be informed about the established connection
|
||||
*/
|
||||
Genode::Signal_context_capability _connected_sigh;
|
||||
|
||||
/**
|
||||
* Signal handler to be informed about data available to read
|
||||
*/
|
||||
Genode::Signal_context_capability _read_avail_sigh;
|
||||
|
||||
/**
|
||||
* Buffer for incoming data
|
||||
*
|
||||
* This buffer is used for synchronizing the reception of data in the
|
||||
* main thread ('watch_sockets_for_incoming_data') and the entrypoint
|
||||
* thread ('read'). The main thread fills the buffer if its not already
|
||||
* occupied and the entrypoint thread consumes the buffer. When the
|
||||
* buffer becomes occupied, the corresponding socket gets removed from
|
||||
* the 'rfds' set of 'select()' until a read request from the terminal
|
||||
* client comes in.
|
||||
*/
|
||||
enum { READ_BUF_SIZE = 4096 };
|
||||
char _read_buf[READ_BUF_SIZE];
|
||||
Genode::size_t _read_buf_bytes_used;
|
||||
|
||||
/**
|
||||
* Establish remote connection
|
||||
*
|
||||
* \return socket descriptor used for the remote TCP connection
|
||||
*/
|
||||
static int _remote_listen(int tcp_port)
|
||||
{
|
||||
int listen_sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (listen_sd == -1) {
|
||||
PERR("socket creation failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr_in sockaddr;
|
||||
sockaddr.sin_family = PF_INET;
|
||||
sockaddr.sin_port = htons (tcp_port);
|
||||
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(listen_sd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
|
||||
PERR("bind to port %d failed", tcp_port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(listen_sd, 1)) {
|
||||
PERR("listen failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Genode::printf("listening on port %d...\n", tcp_port);
|
||||
return listen_sd;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Open_socket(int tcp_port);
|
||||
|
||||
~Open_socket();
|
||||
|
||||
/**
|
||||
* Return socket descriptor for listening to new connections
|
||||
*/
|
||||
int listen_sd() const { return _listen_sd; }
|
||||
|
||||
/**
|
||||
* Return true if all steps of '_remote_listen' succeeded
|
||||
*/
|
||||
bool listen_sd_valid() const { return _listen_sd != -1; }
|
||||
|
||||
/**
|
||||
* Return socket descriptor of the connection
|
||||
*/
|
||||
int sd() const { return _sd; }
|
||||
|
||||
/**
|
||||
* Register signal handler to be notified once we accepted the TCP
|
||||
* connection
|
||||
*/
|
||||
void connected_sigh(Genode::Signal_context_capability sigh) { _connected_sigh = sigh; }
|
||||
|
||||
/**
|
||||
* Register signal handler to be notified when data is available for
|
||||
* reading
|
||||
*/
|
||||
void read_avail_sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
_read_avail_sigh = sigh;
|
||||
|
||||
/* if read data is available right now, deliver signal immediately */
|
||||
if (!read_buffer_empty() && _read_avail_sigh.valid())
|
||||
Genode::Signal_transmitter(_read_avail_sigh).submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept new connection, defining the connection's socket descriptor
|
||||
*
|
||||
* This function is called by the 'select()' thread when a new
|
||||
* connection is pending.
|
||||
*/
|
||||
void accept_remote_connection()
|
||||
{
|
||||
struct sockaddr addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
_sd = accept(_listen_sd, &addr, &len);
|
||||
|
||||
if (_sd > 0)
|
||||
Genode::printf("connection established\n");
|
||||
|
||||
/*
|
||||
* Inform client about the finished initialization of the terminal
|
||||
* session
|
||||
*/
|
||||
if (_connected_sigh.valid())
|
||||
Genode::Signal_transmitter(_connected_sigh).submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if TCP connection is established
|
||||
*
|
||||
* If the return value is false, we are still in listening more
|
||||
* and have not yet called 'accept()'.
|
||||
*/
|
||||
bool connection_established() const { return _sd != -1; }
|
||||
|
||||
/**
|
||||
* Fetch data from socket into internal read buffer
|
||||
*/
|
||||
void fill_read_buffer_and_notify_client()
|
||||
{
|
||||
if (_read_buf_bytes_used) {
|
||||
PWRN("read buffer already in use");
|
||||
return;
|
||||
}
|
||||
|
||||
/* read from socket */
|
||||
_read_buf_bytes_used = ::read(_sd, _read_buf, sizeof(_read_buf));
|
||||
|
||||
/* notify client about bytes available for reading */
|
||||
if (_read_avail_sigh.valid())
|
||||
Genode::Signal_transmitter(_read_avail_sigh).submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush internal read buffer into destination buffer
|
||||
*/
|
||||
Genode::size_t flush_read_buffer(char *dst, Genode::size_t dst_len)
|
||||
{
|
||||
Genode::size_t num_bytes = Genode::min(dst_len, _read_buf_bytes_used);
|
||||
Genode::memcpy(dst, _read_buf, num_bytes);
|
||||
_read_buf_bytes_used = 0;
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the internal read buffer is ready to receive data
|
||||
*/
|
||||
bool read_buffer_empty() const { return _read_buf_bytes_used == 0; }
|
||||
};
|
||||
|
||||
|
||||
class Open_socket_pool
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Protection for '_list'
|
||||
*/
|
||||
Genode::Lock _lock;
|
||||
|
||||
/**
|
||||
* List of open sockets
|
||||
*/
|
||||
Genode::List<Open_socket> _list;
|
||||
|
||||
/**
|
||||
* Number of currently open sockets
|
||||
*/
|
||||
int _count;
|
||||
|
||||
/**
|
||||
* Pipe used to synchronize 'select()' loop with the entrypoint thread
|
||||
*/
|
||||
int sync_pipe_fds[2];
|
||||
|
||||
/**
|
||||
* Intercept the blocking state of the current 'select()' call
|
||||
*/
|
||||
void _wakeup_select()
|
||||
{
|
||||
char c = 0;
|
||||
::write(sync_pipe_fds[1], &c, sizeof(c));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Open_socket_pool() : _count(0)
|
||||
{
|
||||
pipe(sync_pipe_fds);
|
||||
}
|
||||
|
||||
void insert(Open_socket *s)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
_list.insert(s);
|
||||
_count++;
|
||||
_wakeup_select();
|
||||
}
|
||||
|
||||
void remove(Open_socket *s)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
_list.remove(s);
|
||||
_count--;
|
||||
_wakeup_select();
|
||||
}
|
||||
|
||||
void update_sockets_to_watch()
|
||||
{
|
||||
_wakeup_select();
|
||||
}
|
||||
|
||||
void watch_sockets_for_incoming_data()
|
||||
{
|
||||
/* prepare arguments for 'select()' */
|
||||
fd_set rfds;
|
||||
int nfds = 0;
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
/* collect file descriptors of all open sessions */
|
||||
FD_ZERO(&rfds);
|
||||
for (Open_socket *s = _list.first(); s; s = s->next()) {
|
||||
|
||||
/*
|
||||
* If one of the steps of creating the listen socket
|
||||
* failed, skip the session.
|
||||
*/
|
||||
if (!s->listen_sd_valid())
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the connection is not already established, tell
|
||||
* 'select' to notify us about a new connection.
|
||||
*/
|
||||
if (!s->connection_established()) {
|
||||
nfds = Genode::max(nfds, s->listen_sd());
|
||||
FD_SET(s->listen_sd(), &rfds);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* The connection is established. We watch the connection's
|
||||
* file descriptor for reading, but only if our buffer can
|
||||
* take new data. Otherwise, we let the incoming data queue
|
||||
* up in the TCP/IP stack.
|
||||
*/
|
||||
nfds = Genode::max(nfds, s->sd());
|
||||
if (s->read_buffer_empty())
|
||||
FD_SET(s->sd(), &rfds);
|
||||
}
|
||||
|
||||
/* add sync pipe to set of file descriptors to watch */
|
||||
FD_SET(sync_pipe_fds[0], &rfds);
|
||||
nfds = Genode::max(nfds, sync_pipe_fds[0]);
|
||||
}
|
||||
|
||||
/* block for incoming data or sync-pipe events */
|
||||
select(nfds + 1, &rfds, NULL, NULL, NULL);
|
||||
|
||||
/* check if we were woken up via the sync pipe */
|
||||
if (FD_ISSET(sync_pipe_fds[0], &rfds)) {
|
||||
char c = 0;
|
||||
::read(sync_pipe_fds[0], &c, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* read pending data from sockets */
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
for (Open_socket *s = _list.first(); s; s = s->next()) {
|
||||
|
||||
/* look for new connection */
|
||||
if (!s->connection_established()) {
|
||||
if (FD_ISSET(s->listen_sd(), &rfds))
|
||||
s->accept_remote_connection();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* connection is established, look for incoming data */
|
||||
if (FD_ISSET(s->sd(), &rfds))
|
||||
s->fill_read_buffer_and_notify_client();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Open_socket_pool *open_socket_pool()
|
||||
{
|
||||
static Open_socket_pool inst;
|
||||
return &inst;
|
||||
}
|
||||
|
||||
|
||||
Open_socket::Open_socket(int tcp_port)
|
||||
: _listen_sd(_remote_listen(tcp_port)), _sd(-1)
|
||||
{
|
||||
open_socket_pool()->insert(this);
|
||||
}
|
||||
|
||||
|
||||
Open_socket::~Open_socket()
|
||||
{
|
||||
close(_sd);
|
||||
open_socket_pool()->remove(this);
|
||||
}
|
||||
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
class Session_component : public Genode::Rpc_object<Session, Session_component>,
|
||||
public Open_socket
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Attached_ram_dataspace _io_buffer;
|
||||
|
||||
public:
|
||||
|
||||
Session_component(Genode::size_t io_buffer_size, int tcp_port)
|
||||
:
|
||||
Open_socket(tcp_port),
|
||||
_io_buffer(Genode::env()->ram_session(), io_buffer_size)
|
||||
{ }
|
||||
|
||||
/********************************
|
||||
** Terminal session interface **
|
||||
********************************/
|
||||
|
||||
Size size() { return Size(0, 0); }
|
||||
|
||||
bool avail()
|
||||
{
|
||||
return !read_buffer_empty();
|
||||
}
|
||||
|
||||
Genode::size_t _read(Genode::size_t dst_len)
|
||||
{
|
||||
Genode::size_t num_bytes =
|
||||
flush_read_buffer(_io_buffer.local_addr<char>(),
|
||||
Genode::min(_io_buffer.size(), dst_len));
|
||||
|
||||
/*
|
||||
* If read buffer was in use, look if more data is buffered in
|
||||
* the TCP/IP stack.
|
||||
*/
|
||||
if (num_bytes)
|
||||
open_socket_pool()->update_sockets_to_watch();
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
void _write(Genode::size_t num_bytes)
|
||||
{
|
||||
/* sanitize argument */
|
||||
num_bytes = Genode::min(num_bytes, _io_buffer.size());
|
||||
|
||||
/* write data to socket, assuming that it won't block */
|
||||
if (::write(sd(), _io_buffer.local_addr<char>(), num_bytes) < 0)
|
||||
PERR("write error, dropping data");
|
||||
}
|
||||
|
||||
Genode::Dataspace_capability _dataspace()
|
||||
{
|
||||
return _io_buffer.cap();
|
||||
}
|
||||
|
||||
void read_avail_sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
Open_socket::read_avail_sigh(sigh);
|
||||
}
|
||||
|
||||
void connected_sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
Open_socket::connected_sigh(sigh);
|
||||
}
|
||||
|
||||
Genode::size_t read(void *buf, Genode::size_t) { return 0; }
|
||||
Genode::size_t write(void const *buf, Genode::size_t) { return 0; }
|
||||
};
|
||||
|
||||
|
||||
class Root_component : public Genode::Root_component<Session_component>
|
||||
{
|
||||
protected:
|
||||
|
||||
Session_component *_create_session(const char *args)
|
||||
{
|
||||
/*
|
||||
* XXX read I/O buffer size from args
|
||||
*/
|
||||
Genode::size_t io_buffer_size = 4096;
|
||||
|
||||
try {
|
||||
Genode::Session_policy policy(args);
|
||||
|
||||
unsigned tcp_port = 0;
|
||||
policy.attribute("port").value(&tcp_port);
|
||||
return new (md_alloc())
|
||||
Session_component(io_buffer_size, tcp_port);
|
||||
|
||||
} catch (Genode::Xml_node::Nonexistent_attribute) {
|
||||
PERR("Missing \"port\" attribute in policy definition");
|
||||
throw Genode::Root::Unavailable();
|
||||
} catch (Genode::Session_policy::No_policy_defined) {
|
||||
PERR("Invalid session request, no matching policy");
|
||||
throw Genode::Root::Unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Root_component(Genode::Rpc_entrypoint *ep,
|
||||
Genode::Allocator *md_alloc)
|
||||
:
|
||||
Genode::Root_component<Session_component>(ep, md_alloc)
|
||||
{ }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Genode::printf("--- TCP terminal started ---\n");
|
||||
|
||||
/* initialize entry point that serves the root interface */
|
||||
enum { STACK_SIZE = 4*4096 };
|
||||
static Cap_connection cap;
|
||||
static Rpc_entrypoint ep(&cap, STACK_SIZE, "terminal_ep");
|
||||
|
||||
static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
|
||||
|
||||
/* create root interface for service */
|
||||
static Terminal::Root_component root(&ep, &sliced_heap);
|
||||
|
||||
/* announce service at our parent */
|
||||
env()->parent()->announce(ep.manage(&root));
|
||||
|
||||
for (;;)
|
||||
open_socket_pool()->watch_sockets_for_incoming_data();
|
||||
|
||||
return 0;
|
||||
}
|
3
gems/src/server/tcp_terminal/target.mk
Normal file
3
gems/src/server/tcp_terminal/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = tcp_terminal
|
||||
SRC_CC = main.cc
|
||||
LIBS = env cxx server libc libc_lwip_nic_dhcp libc_log libc_lock_pipe
|
1367
gems/src/server/terminal/main.cc
Normal file
1367
gems/src/server/terminal/main.cc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
gems/src/server/terminal/mono.tff
Normal file
BIN
gems/src/server/terminal/mono.tff
Normal file
Binary file not shown.
4
gems/src/server/terminal/target.mk
Normal file
4
gems/src/server/terminal/target.mk
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = terminal
|
||||
LIBS = cxx env server signal
|
||||
SRC_CC = main.cc
|
||||
SRC_BIN = mono.tff
|
Reference in New Issue
Block a user