fs_log: new log file server using native FS sessions

Fixes #1538
This commit is contained in:
Emery Hemingway 2015-05-23 23:33:24 +02:00 committed by Christian Helmuth
parent 7ce19216f4
commit ce1e6c16fb
6 changed files with 305 additions and 190 deletions

View File

@ -1,18 +0,0 @@
LOG server that writes log messages onto a file system.
Using this component, log messages of different processes can be redirected
to files on a file-system service. The assignment of processes to files can
be expressed in the configuration as follows:
! <start name="fs_log">
! <resource name="RAM" quantum="2M"/>
! <provides><service name="LOG"/></provides>
! <config>
! <policy label="noux" file="/noux.log" />
! <policy label="noux ->" file="/noux_process.log" />
! </config>
! </start>
In this example, all messages originating from the noux process are directed
to the file '/noux.log'. All messages originating from children of the noux
process end up in the file '/noux_process.log'.

View File

@ -1,171 +0,0 @@
/*
* \brief LOG service that writes to a file
* \author Alexander Tarasikov <tarasikov@ksyslabs.org>
* \author Norman Feske <norman.feske@genode-labs.com>
* \date 2013-05-07
*/
/*
* Copyright (C) 2013 Ksys Labs LLC
* Copyright (C) 2012-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/env.h>
#include <base/rpc_server.h>
#include <base/sleep.h>
#include <root/component.h>
#include <util/string.h>
#include <os/session_policy.h>
#include <cap_session/connection.h>
#include <log_session/log_session.h>
/* libc includes */
#include <fcntl.h>
#include <unistd.h>
class Log_component : public Genode::Rpc_object<Genode::Log_session>
{
public:
enum { LABEL_LEN = 64 };
private:
typedef Genode::size_t size_t;
char _label[LABEL_LEN];
size_t _label_len;
int _fd;
public:
/**
* Constructor
*/
Log_component(const char *label, char const *filename)
:
_fd(::open(filename, O_CREAT | O_WRONLY | O_APPEND))
{
using namespace Genode;
if (_fd < 0) {
PERR("unable to open \"%s\"", filename);
throw Root::Unavailable();
}
snprintf(_label, LABEL_LEN, "[%s] ", label);
_label_len = strlen(_label);
PINF("log client \"%s\" to file \"%s\")", label, filename);
}
/**
* Destructor
*/
~Log_component() { ::close(_fd); }
/*****************
** Log session **
*****************/
/**
* Write a log-message to the file.
*/
size_t write(String const &string_buf)
{
if (!(string_buf.is_valid_string())) {
PERR("corrupted string");
return 0;
}
char const *string = string_buf.string();
Genode::size_t const len = Genode::strlen(string);
::write(_fd, _label, _label_len);
::write(_fd, string, len);
return len;
}
};
class Log_root : public Genode::Root_component<Log_component>
{
private:
int _fd;
protected:
/**
* Root component interface
*/
Log_component *_create_session(const char *args)
{
using namespace Genode;
char label_buf[Log_component::LABEL_LEN];
Arg label_arg = Arg_string::find_arg(args, "label");
label_arg.string(label_buf, sizeof(label_buf), "");
/* obtain file name from configured policy */
enum { FILENAME_MAX_LEN = 256 };
char filename[FILENAME_MAX_LEN];
try {
Session_label label(args);
Session_policy policy(label);
policy.attribute("file").value(filename, sizeof(filename));
} catch (...) {
PERR("Invalid session request, no matching policy");
throw Root::Unavailable();
}
return new (md_alloc()) Log_component(label_buf, filename);
}
public:
/**
* Constructor
*
* \param session_ep entry point for managing cpu session objects
* \param md_alloc meta-data allocator to be used by root component
*/
Log_root(Genode::Rpc_entrypoint *session_ep, Genode::Allocator *md_alloc)
: Genode::Root_component<Log_component>(session_ep, md_alloc) { }
};
int main(int argc, char **argv)
{
using namespace Genode;
/*
* Initialize server entry point
*
* Use a large stack because we are calling libc functions from the
* context of the entrypoint.
*/
enum { STACK_SIZE = sizeof(addr_t)*16*1024 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "fs_log_ep");
static Log_root log_root(&ep, env()->heap());
/*
* Announce services
*/
env()->parent()->announce(ep.manage(&log_root));
/**
* Got to sleep forever
*/
sleep_forever();
return 0;
}

View File

@ -0,0 +1,20 @@
LOG server that writes log messages onto a file system.
Log files are creating in a directory tree formed from session labels.
As an example the session label "init -> nitpicker" would create
a log file at "init/nitpicker.log". The behavior of opening two LOG
sessions with the same label is undefined.
The only configuration and policy available is the option to truncate
files at the start of the LOG session, which is disabled by default.
:Example configuration:
! <start name="log_file">
! <resource name="RAM" quantum="1M"/>
! <provides><service name="LOG"/></provides>
! <config>
! <policy label="nic_drv" truncate="no"/>
! <policy label="" truncate="yes"/>
! </config>
! </start>

View File

@ -0,0 +1,184 @@
/*
* \brief Server that writes log messages to a file system.
* \author Emery Hemingway
* \date 2015-05-13
*/
/*
* Copyright (C) 2015 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 <file_system_session/connection.h>
#include <file_system/util.h>
#include <root/component.h>
#include <os/server.h>
#include <os/session_policy.h>
#include <base/printf.h>
/* Local includes */
#include "session.h"
namespace Fs_log {
using namespace Genode;
class Root_component;
struct Main;
enum {
BLOCK_SIZE = Log_session::String::MAX_SIZE,
QUEUE_SIZE = File_system::Session::TX_QUEUE_SIZE,
TX_BUF_SIZE = BLOCK_SIZE * QUEUE_SIZE
};
}
class Fs_log::Root_component : public Genode::Root_component<Fs_log::Session_component>
{
private:
Allocator_avl _write_alloc;
File_system::Connection _fs;
File_system::File_handle open_file(File_system::Dir_handle &dir_handle,
char const *name)
{
try {
return _fs.file(dir_handle, name, File_system::WRITE_ONLY, false);
} catch (File_system::Lookup_failed) {
return _fs.file(dir_handle, name, File_system::WRITE_ONLY, true);
}
}
protected:
Session_component *_create_session(const char *args)
{
using namespace File_system;
char path[MAX_PATH_LEN];
path[0] = '/';
char name[MAX_NAME_LEN];
Session_label session_label(args);
strncpy(path+1, session_label.string(), sizeof(path)-1);
bool truncate = false;
try {
Session_policy policy(session_label);
try {
truncate = policy.attribute("truncate").has_value("yes");
} catch (Xml_node::Nonexistent_attribute) { }
} catch (Session_policy::No_policy_defined) { }
size_t len = strlen(path);
size_t start = 1;
for (size_t i = 1; i < len;) {
/* Replace any slashes in label elements. */
if (path[i] == '/') path[i] = '_';
if (strcmp(" -> ", path+i, 4) == 0) {
path[i++] = '/';
strncpy(path+i, path+i+3, sizeof(path)-i);
start = i;
i += 3;
} else ++i;
}
snprintf(name, sizeof(name), "%s.log", path+start);
path[(start == 1) ? start : start-1] = '\0';
/* Rewrite any slashes in the name. */
for (char *p = name; *p; ++p)
if (*p == '/') *p = '_';
File_handle file_handle;
seek_off_t offset = 0;
try {
Dir_handle dir_handle = ensure_dir(_fs, path);
Handle_guard dir_guard(_fs, dir_handle);
file_handle = open_file(dir_handle, name);
if (truncate)
_fs.truncate(file_handle, 0);
else
offset = _fs.status(file_handle).size;
} catch (Permission_denied) {
PERR("%s/%s: permission denied", path, name);
throw Root::Unavailable();
} catch (Name_too_long) {
PERR("%s/%s: name too long", path, name);
throw Root::Unavailable();
} catch (No_space) {
PERR("%s/%s: no space", path, name);
throw Root::Unavailable();
} catch (Out_of_node_handles) {
PERR("%s/%s: out of node handles", path, name);
throw Root::Unavailable();
} catch (Invalid_name) {
PERR("%s/%s: invalid_name", path, name);
throw Root::Unavailable();
} catch (Size_limit_reached) {
PERR("%s/%s: size limit reached", path, name);
throw Root::Unavailable();
} catch (...) {
PERR("%s/%s: unknown error", path, name);
throw Root::Unavailable();
}
return new (md_alloc()) Session_component(_fs, file_handle, offset);
}
public:
/**
* Constructor
*/
Root_component(Server::Entrypoint &ep, Allocator &alloc)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &alloc),
_write_alloc(env()->heap()),
_fs(_write_alloc, TX_BUF_SIZE)
{ }
};
struct Fs_log::Main
{
Server::Entrypoint &ep;
Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root_component root { ep, sliced_heap };
Main(Server::Entrypoint &ep)
: ep(ep)
{ Genode::env()->parent()->announce(ep.manage(root)); }
};
/************
** Server **
************/
namespace Server {
char const* name() { return "fs_log_ep"; }
size_t stack_size() { return 3*512*sizeof(long); }
void construct(Entrypoint &ep) { static Fs_log::Main inst(ep); }
}

View File

@ -0,0 +1,100 @@
/*
* \brief Log session that writes messages to a file system.
* \author Emery Hemingway
* \date 2015-05-16
*/
/*
* Copyright (C) 2015 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 _FS_LOG__SESSION_H_
#define _FS_LOG__SESSION_H_
/* Genode includes */
#include <log_session/log_session.h>
#include <file_system_session/file_system_session.h>
#include <base/rpc_server.h>
namespace Fs_log {
using namespace Genode;
class Session_component;
}
/**
* A log session that writes messages to a file system node.
*
* Message writing is fire-and-forget to prevent
* logging from becoming I/O bound.
*/
class Fs_log::Session_component : public Rpc_object<Log_session, Session_component>
{
private:
File_system::Session &_fs;
File_system::File_handle _file_handle;
File_system::seek_off_t _offset;
public:
/**
* Constructor
*/
Session_component(File_system::Session &fs,
File_system::File_handle fh,
File_system::seek_off_t offset)
: _fs(fs), _file_handle(fh), _offset(offset) { }
/*****************
** Log session **
*****************/
size_t write(Log_session::String const &string)
{
if (!(string.is_valid_string())) {
PERR("corrupted string");
return 0;
}
File_system::Session::Tx::Source &source = *_fs.tx();
char const *msg = string.string();
size_t msg_len = Genode::strlen(msg);
size_t write_len = msg_len;
/*
* If the message did not fill the incoming buffer
* make space to add a newline.
*/
if ((msg_len < Log_session::String::MAX_SIZE) &&
(msg[msg_len-1] != '\n'))
++write_len;
while (source.ack_avail())
source.release_packet(source.get_acked_packet());
File_system::Packet_descriptor
packet(source.alloc_packet(Log_session::String::MAX_SIZE),
0, /* The result struct. */
_file_handle, File_system::Packet_descriptor::WRITE,
write_len, _offset);
_offset += write_len;
char *buf = source.packet_content(packet);
memcpy(buf, msg, msg_len);
if (msg_len != write_len)
buf[msg_len] = '\n';
source.submit_packet(packet);
return msg_len;
}
};
#endif

View File

@ -1,3 +1,3 @@
TARGET = fs_log
SRC_CC = main.cc
LIBS = base libc
LIBS = base config server