202 lines
5.5 KiB
C++
202 lines
5.5 KiB
C++
/*
|
|
* \brief Server that writes log messages to a file system.
|
|
* \author Emery Hemingway
|
|
* \date 2015-05-13
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2015-2017 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <base/heap.h>
|
|
#include <file_system_session/connection.h>
|
|
#include <file_system/util.h>
|
|
#include <os/path.h>
|
|
#include <os/session_policy.h>
|
|
#include <base/heap.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <root/component.h>
|
|
#include <base/component.h>
|
|
#include <base/log.h>
|
|
|
|
/* Local includes */
|
|
#include "session.h"
|
|
|
|
namespace Fs_log {
|
|
|
|
using namespace Genode;
|
|
using namespace File_system;
|
|
|
|
class Root_component;
|
|
|
|
enum {
|
|
PACKET_SIZE = Log_session::String::MAX_SIZE,
|
|
QUEUE_SIZE = File_system::Session::TX_QUEUE_SIZE,
|
|
TX_BUF_SIZE = PACKET_SIZE * (QUEUE_SIZE+2)
|
|
};
|
|
|
|
typedef Genode::Path<File_system::MAX_PATH_LEN> Path;
|
|
|
|
}
|
|
|
|
|
|
class Fs_log::Root_component :
|
|
public Genode::Root_component<Fs_log::Session_component>
|
|
{
|
|
private:
|
|
|
|
Genode::Env &_env;
|
|
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
|
|
Genode::Heap _heap { _env.ram(), _env.rm() };
|
|
Allocator_avl _tx_alloc { &_heap };
|
|
File_system::Connection _fs
|
|
{ _env, _tx_alloc, "", "/", true, TX_BUF_SIZE };
|
|
|
|
void _update_config() { _config_rom.update(); }
|
|
|
|
Genode::Signal_handler<Root_component> _config_handler
|
|
{ _env.ep(), *this, &Root_component::_update_config };
|
|
|
|
protected:
|
|
|
|
Session_component *_create_session(const char *args) override
|
|
{
|
|
using namespace File_system;
|
|
|
|
size_t ram_quota =
|
|
Arg_string::find_arg(args, "ram_quota").aligned_size();
|
|
if (ram_quota < sizeof(Session_component))
|
|
throw Insufficient_ram_quota();
|
|
|
|
Path dir_path;
|
|
char file_name[MAX_NAME_LEN];
|
|
|
|
Session_label const session_label = label_from_args(args);
|
|
char const *label_str = session_label.string();
|
|
char const *label_prefix = "";
|
|
bool truncate = false;
|
|
|
|
try {
|
|
Session_policy policy(session_label, _config_rom.xml());
|
|
truncate = policy.attribute_value("truncate", truncate);
|
|
bool merge = policy.attribute_value("merge", false);
|
|
|
|
/* only a match on 'label_prefix' can be merged */
|
|
if (merge && policy.has_type("policy")
|
|
&& (!(policy.has_attribute("label")
|
|
|| policy.has_attribute("label_suffix"))))
|
|
{
|
|
/*
|
|
* split the label between what will be the log file
|
|
* and what will be prepended to messages in the file
|
|
*/
|
|
size_t offset = policy.attribute("label_prefix").value_size();
|
|
for (size_t i = offset; i < session_label.length()-4; ++i) {
|
|
if (strcmp(label_str+i, " -> ", 4))
|
|
continue;
|
|
|
|
label_prefix = label_str+i+4;
|
|
{
|
|
char tmp[128];
|
|
copy_cstring(tmp, label_str, min(sizeof(tmp), i+1));
|
|
dir_path = path_from_label<Path>(tmp);
|
|
}
|
|
break;
|
|
}
|
|
if (dir_path == "/")
|
|
dir_path = path_from_label<Path>(label_str);
|
|
|
|
} else if (!policy.has_type("default-policy")) {
|
|
dir_path = path_from_label<Path>(label_str);
|
|
}
|
|
|
|
}
|
|
catch (Session_policy::No_policy_defined) {
|
|
dir_path = path_from_label<Path>(label_str); }
|
|
|
|
if (dir_path == "/") {
|
|
copy_cstring(file_name, "log", sizeof(file_name));
|
|
label_prefix = label_str;
|
|
} else {
|
|
dir_path.append(".log");
|
|
copy_cstring(file_name, dir_path.last_element(), sizeof(file_name));
|
|
dir_path.strip_last_element();
|
|
dir_path.remove_trailing('/');
|
|
}
|
|
|
|
char const *errstr;
|
|
try {
|
|
|
|
Dir_handle dir_handle = ensure_dir(_fs, dir_path.base());
|
|
Handle_guard dir_guard(_fs, dir_handle);
|
|
|
|
Genode::Constructible<File_handle> handle;
|
|
try {
|
|
handle.construct(_fs.file(dir_handle, file_name,
|
|
File_system::WRITE_ONLY, false));
|
|
|
|
/* don't truncate at every new child session */
|
|
if (truncate && (strcmp(label_prefix, "") == 0))
|
|
_fs.truncate(*handle, 0);
|
|
}
|
|
catch (File_system::Lookup_failed) {
|
|
handle.construct(_fs.file(dir_handle, file_name,
|
|
File_system::WRITE_ONLY, true));
|
|
}
|
|
|
|
return new (md_alloc()) Session_component(_fs, *handle, label_prefix);
|
|
}
|
|
catch (Permission_denied) {
|
|
errstr = "permission denied"; }
|
|
catch (No_space) {
|
|
errstr = "file system out of space"; }
|
|
catch (Out_of_ram) {
|
|
errstr = "file system server out of RAM"; }
|
|
catch (Out_of_caps) {
|
|
errstr = "file system server out of caps"; }
|
|
catch (Invalid_name) {
|
|
errstr = "invalid path"; }
|
|
catch (Name_too_long) {
|
|
errstr = "name too long"; }
|
|
catch (...) {
|
|
errstr = "unhandled error"; }
|
|
|
|
Genode::error("cannot open log file ",
|
|
(char const *)dir_path.base(),
|
|
", ", errstr);
|
|
throw Service_denied();
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
Root_component(Genode::Env &env, Genode::Allocator &md_alloc)
|
|
:
|
|
Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
|
|
_env(env)
|
|
{
|
|
_config_rom.sigh(_config_handler);
|
|
|
|
/* fill the ack queue with packets so sessions never need to alloc */
|
|
File_system::Session::Tx::Source &source = *_fs.tx();
|
|
for (int i = 0; i < QUEUE_SIZE-1; ++i)
|
|
source.submit_packet(source.alloc_packet(PACKET_SIZE));
|
|
|
|
env.parent().announce(env.ep().manage(*this));
|
|
}
|
|
|
|
};
|
|
|
|
|
|
void Component::construct(Genode::Env &env)
|
|
{
|
|
static Genode::Sliced_heap sliced_heap { env.ram(), env.rm() };
|
|
static Fs_log::Root_component root { env, sliced_heap };
|
|
}
|