mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-19 03:06:39 +00:00
parent
ee5d213c1f
commit
a9651d1728
9
gems/src/server/file_terminal/README
Normal file
9
gems/src/server/file_terminal/README
Normal file
@ -0,0 +1,9 @@
|
||||
File terminal is a service that provides Genode's Terminal_session interface
|
||||
for a given file via a File_system_session.
|
||||
|
||||
! <config>
|
||||
! <policy label="client1" filename="test.txt" />
|
||||
! <policy label="client2" filename="file.dat" io_buffer_size="4K"/>h
|
||||
! </config>
|
||||
|
||||
To keep things simple a client can only open one file at the moment.
|
291
gems/src/server/file_terminal/main.cc
Normal file
291
gems/src/server/file_terminal/main.cc
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* \brief Service providing the 'Terminal_session' interface for a file
|
||||
* \author Josef Soentgen
|
||||
* \date 2013-10-08
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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/heap.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/sleep.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
#include <os/session_policy.h>
|
||||
#include <root/component.h>
|
||||
#include <terminal_session/terminal_session.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
static bool const verbose = false;
|
||||
|
||||
#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__)
|
||||
|
||||
|
||||
class Open_file
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* File descriptor for open file
|
||||
*/
|
||||
int _fd;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public:
|
||||
|
||||
Open_file(const char *filename) : _fd(-1)
|
||||
{
|
||||
PDBGV("open '%s'", filename);
|
||||
_fd = ::open(filename, O_CREAT|O_RDWR);
|
||||
if (_fd == -1)
|
||||
::perror("open");
|
||||
}
|
||||
|
||||
~Open_file() { close(_fd); }
|
||||
|
||||
/**
|
||||
* Return file descriptor of the open file
|
||||
*/
|
||||
int fd() const { return _fd; }
|
||||
|
||||
/**
|
||||
* Register signal handler to be notified once we openend the file
|
||||
*/
|
||||
void connected_sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
_connected_sigh = sigh;
|
||||
|
||||
/* notify the client immediatly if we have already openend the file */
|
||||
if (file_opened() && _connected_sigh.valid())
|
||||
Genode::Signal_transmitter(_connected_sigh).submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the file was successfully openend
|
||||
*/
|
||||
bool file_opened() const { return _fd != -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 file */
|
||||
_read_buf_bytes_used = ::read(_fd, _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; }
|
||||
};
|
||||
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
class Session_component : public Genode::Rpc_object<Session, Session_component>,
|
||||
public Open_file
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Attached_ram_dataspace _io_buffer;
|
||||
|
||||
public:
|
||||
|
||||
Session_component(Genode::size_t io_buffer_size, const char *filename)
|
||||
:
|
||||
Open_file(filename),
|
||||
_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));
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
void _write(Genode::size_t num_bytes)
|
||||
{
|
||||
/* sanitize argument */
|
||||
num_bytes = Genode::min(num_bytes, _io_buffer.size());
|
||||
|
||||
/* write data to descriptor */
|
||||
if (::write(fd(), _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_file::read_avail_sigh(sigh);
|
||||
}
|
||||
|
||||
void connected_sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
Open_file::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)
|
||||
{
|
||||
Genode::size_t io_buffer_size = 4096;
|
||||
|
||||
try {
|
||||
Genode::Session_label label(args);
|
||||
Genode::Session_policy policy(label);
|
||||
|
||||
char filename[256];
|
||||
policy.attribute("filename").value(filename, sizeof(filename));
|
||||
|
||||
if (policy.has_attribute("io_buffer_size"))
|
||||
policy.attribute("io_buffer_size").value(&io_buffer_size);
|
||||
|
||||
return new (md_alloc())
|
||||
Session_component(io_buffer_size, filename);
|
||||
|
||||
} catch (Genode::Xml_node::Nonexistent_attribute) {
|
||||
PERR("Missing \"filename\" 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("--- file terminal started ---\n");
|
||||
|
||||
/**
|
||||
* The stack needs to be that large because certain functions
|
||||
* in the libc (e.g. mktime(3)) require a huge stack.
|
||||
*/
|
||||
enum { STACK_SIZE = 16*sizeof(addr_t)*1024 };
|
||||
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));
|
||||
|
||||
Genode::sleep_forever();
|
||||
|
||||
return 0;
|
||||
}
|
3
gems/src/server/file_terminal/target.mk
Normal file
3
gems/src/server/file_terminal/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = file_terminal
|
||||
SRC_CC = main.cc
|
||||
LIBS = libc libc_fs libc_log
|
Loading…
Reference in New Issue
Block a user