From ce1e6c16fb785208d99fea735369e5722dfe3633 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 23 May 2015 23:33:24 +0200 Subject: [PATCH] fs_log: new log file server using native FS sessions Fixes #1538 --- repos/libports/src/server/fs_log/README | 18 -- repos/libports/src/server/fs_log/main.cc | 171 ---------------- repos/os/src/server/fs_log/README | 20 ++ repos/os/src/server/fs_log/main.cc | 184 ++++++++++++++++++ repos/os/src/server/fs_log/session.h | 100 ++++++++++ .../src/server/fs_log/target.mk | 2 +- 6 files changed, 305 insertions(+), 190 deletions(-) delete mode 100644 repos/libports/src/server/fs_log/README delete mode 100644 repos/libports/src/server/fs_log/main.cc create mode 100644 repos/os/src/server/fs_log/README create mode 100644 repos/os/src/server/fs_log/main.cc create mode 100644 repos/os/src/server/fs_log/session.h rename repos/{libports => os}/src/server/fs_log/target.mk (54%) diff --git a/repos/libports/src/server/fs_log/README b/repos/libports/src/server/fs_log/README deleted file mode 100644 index 315d96bbd..000000000 --- a/repos/libports/src/server/fs_log/README +++ /dev/null @@ -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: - -! -! -! -! -! -! -! -! - -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'. diff --git a/repos/libports/src/server/fs_log/main.cc b/repos/libports/src/server/fs_log/main.cc deleted file mode 100644 index 8f290ffb1..000000000 --- a/repos/libports/src/server/fs_log/main.cc +++ /dev/null @@ -1,171 +0,0 @@ -/* - * \brief LOG service that writes to a file - * \author Alexander Tarasikov - * \author Norman Feske - * \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 -#include -#include -#include -#include -#include -#include -#include - -/* libc includes */ -#include -#include - - -class Log_component : public Genode::Rpc_object -{ - 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 -{ - 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(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; -} diff --git a/repos/os/src/server/fs_log/README b/repos/os/src/server/fs_log/README new file mode 100644 index 000000000..abfa908de --- /dev/null +++ b/repos/os/src/server/fs_log/README @@ -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: +! +! +! +! +! +! +! +! + diff --git a/repos/os/src/server/fs_log/main.cc b/repos/os/src/server/fs_log/main.cc new file mode 100644 index 000000000..ac1f4e391 --- /dev/null +++ b/repos/os/src/server/fs_log/main.cc @@ -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 +#include +#include +#include +#include +#include + +/* 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 +{ + 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(&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); } + +} diff --git a/repos/os/src/server/fs_log/session.h b/repos/os/src/server/fs_log/session.h new file mode 100644 index 000000000..ebe289446 --- /dev/null +++ b/repos/os/src/server/fs_log/session.h @@ -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 +#include +#include + +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 +{ + 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 diff --git a/repos/libports/src/server/fs_log/target.mk b/repos/os/src/server/fs_log/target.mk similarity index 54% rename from repos/libports/src/server/fs_log/target.mk rename to repos/os/src/server/fs_log/target.mk index 40f3b0a18..372c6082e 100644 --- a/repos/libports/src/server/fs_log/target.mk +++ b/repos/os/src/server/fs_log/target.mk @@ -1,3 +1,3 @@ TARGET = fs_log SRC_CC = main.cc -LIBS = base libc +LIBS = base config server