diff --git a/repos/os/src/server/fs_log/README b/repos/os/src/server/fs_log/README
index abfa908de1..89aeac6427 100644
--- a/repos/os/src/server/fs_log/README
+++ b/repos/os/src/server/fs_log/README
@@ -2,11 +2,12 @@ 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.
+a log file at "init/nitpicker.log".
-The only configuration and policy available is the option to truncate
-files at the start of the LOG session, which is disabled by default.
+The option to truncate files at the start of each LOG session is available
+through session policy, as well the option to merge the logs of any
+session matching a given policy. When a merged policy label contains a
+trailing "->", the log filename takes the name of the next label element.
:Example configuration:
!
@@ -14,6 +15,7 @@ files at the start of the LOG session, which is disabled by default.
!
!
!
+!
!
!
!
diff --git a/repos/os/src/server/fs_log/log_file.h b/repos/os/src/server/fs_log/log_file.h
new file mode 100644
index 0000000000..44b06797ea
--- /dev/null
+++ b/repos/os/src/server/fs_log/log_file.h
@@ -0,0 +1,91 @@
+/*
+ * \brief File object shared between log sessions
+ * \author Emery Hemingway
+ * \date 2015-06-09
+ */
+
+/*
+ * 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__LOG_FILE_H_
+#define _FS_LOG__LOG_FILE_H_
+
+/* Genode includes */
+#include
+#include
+
+namespace Fs_log {
+
+ using namespace Genode;
+ using namespace File_system;
+
+ class Log_file;
+
+}
+
+class Fs_log::Log_file : public List::Element
+{
+ private:
+
+ char _dir_path[ MAX_PATH_LEN];
+ char _file_name[MAX_NAME_LEN];
+ File_system::Session &_fs;
+ File_handle _handle;
+ seek_off_t _offset;
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Log_file(File_system::Session &fs, File_handle handle,
+ char const *dir_path, char const *file_name,
+ seek_off_t offset)
+ :
+ _fs(fs), _handle(handle), _offset(offset)
+ {
+ strncpy(_dir_path, dir_path, sizeof(_dir_path));
+ strncpy(_file_name, file_name, sizeof(_file_name));
+ }
+
+ bool match(char const *dir, char const *filename) const
+ {
+ return
+ (strcmp(_dir_path, dir, MAX_PATH_LEN) == 0) &&
+ (strcmp(_file_name, filename, MAX_NAME_LEN) == 0);
+ }
+
+ /**
+ * Write a log message to the packet buffer.
+ */
+ size_t write(char const *msg, size_t msg_len)
+ {
+ File_system::Session::Tx::Source &source = *_fs.tx();
+
+ File_system::Packet_descriptor raw_packet;
+ if (!source.ready_to_submit())
+ raw_packet = source.get_acked_packet();
+ else
+ raw_packet = source.alloc_packet(Log_session::String::MAX_SIZE);
+
+ File_system::Packet_descriptor
+ packet(raw_packet,
+ 0, /* The result struct. */
+ _handle, File_system::Packet_descriptor::WRITE,
+ msg_len, _offset);
+
+ _offset += msg_len;
+
+ char *buf = source.packet_content(packet);
+ memcpy(buf, msg, msg_len);
+
+ source.submit_packet(packet);
+ return msg_len;
+ }
+};
+
+#endif
\ No newline at end of file
diff --git a/repos/os/src/server/fs_log/main.cc b/repos/os/src/server/fs_log/main.cc
index ac1f4e3919..79fa9c7f68 100644
--- a/repos/os/src/server/fs_log/main.cc
+++ b/repos/os/src/server/fs_log/main.cc
@@ -20,38 +20,41 @@
#include
/* Local includes */
+#include "log_file.h"
#include "session.h"
namespace Fs_log {
using namespace Genode;
+ using namespace File_system;
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
+ BLOCK_SIZE = Log_session::String::MAX_SIZE,
+ QUEUE_SIZE = File_system::Session::TX_QUEUE_SIZE,
+ TX_BUF_SIZE = BLOCK_SIZE * (QUEUE_SIZE*2 + 1)
};
}
-class Fs_log::Root_component : public Genode::Root_component
+class Fs_log::Root_component :
+ public Genode::Root_component
{
private:
- Allocator_avl _write_alloc;
- File_system::Connection _fs;
+ Allocator_avl _write_alloc;
+ File_system::Connection _fs;
+ List _log_files;
- File_system::File_handle open_file(File_system::Dir_handle &dir_handle,
- char const *name)
+ Log_file *lookup(char const *dir, char const *filename)
{
- 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);
- }
+ for (Log_file *file = _log_files.first(); file; file = file->next())
+ if (file->match(dir, filename))
+ return file;
+
+ return 0;
}
protected:
@@ -60,85 +63,149 @@ class Fs_log::Root_component : public Genode::Root_component ", path+i, 4) == 0) {
- path[i++] = '/';
- strncpy(path+i, path+i+3, sizeof(path)-i);
- start = i;
- i += 3;
- } else ++i;
+ /*
+ * If the policy has a trailing '->', move first element
+ * from the log prefix to the end of the log path.
+ */
+ size_t label_len = strlen(dir_path);
+ label_prefix = label_str+(label_len-1);
+
+ if ((strcmp((dir_path+label_len)-3, " ->", 4) == 0) ||
+ (strcmp((dir_path+label_len)-4, " -> ", 5) == 0)) {
+
+ for (size_t i = 0; *(label_str+i); ++i) {
+ if (strcmp(label_prefix+i, " -> ", 4))
+ continue;
+
+ strncpy(dir_path+label_len, label_prefix, i+1);
+ label_prefix += i+4;
+ break;
+ }
+
+ if (*label_prefix == ' ') ++label_prefix;
+ }
+
+ } else
+ strncpy(dir_path+1, label_str, MAX_PATH_LEN-1);
+
+ } catch (Session_policy::No_policy_defined) {
+ strncpy(dir_path+1, label_str, MAX_PATH_LEN-1);
}
- snprintf(name, sizeof(name), "%s.log", path+start);
- path[(start == 1) ? start : start-1] = '\0';
+ {
+ /* Parse out a directory and file name. */
+ size_t len = strlen(dir_path);
+ size_t start = 1;
+ for (size_t i = 1; i < len;) {
+ /* Replace any slashes in label elements. */
+ if (dir_path[i] == '/') dir_path[i] = '_';
+ if (strcmp(" -> ", dir_path+i, 4) == 0) {
+ dir_path[i++] = '/';
+ strncpy(dir_path+i, dir_path+i+3, MAX_PATH_LEN-i);
+ start = i;
+ i += 3;
+ } else ++i;
+ }
- /* Rewrite any slashes in the name. */
- for (char *p = name; *p; ++p)
- if (*p == '/') *p = '_';
+ /* Copy the remainder to the file name. */
+ snprintf(file_name, MAX_NAME_LEN, "%s.log", dir_path+start);
- File_handle file_handle;
- seek_off_t offset = 0;
- try {
- Dir_handle dir_handle = ensure_dir(_fs, path);
+ /* Terminate the directory path. */
+ dir_path[(start == 1) ? start : start-1] = '\0';
+
+ /* Rewrite any slashes in the name. */
+ for (char *p = file_name; *p; ++p)
+ if (*p == '/') *p = '_';
+ }
+
+ Log_file *file = lookup(dir_path, file_name);
+ if (!file) try {
+
+ Dir_handle dir_handle = ensure_dir(_fs, dir_path);
Handle_guard dir_guard(_fs, dir_handle);
+ File_handle handle;
+ seek_off_t offset = 0;
- file_handle = open_file(dir_handle, name);
+ try {
+ handle = _fs.file(dir_handle, file_name,
+ File_system::WRITE_ONLY, false);
- if (truncate)
- _fs.truncate(file_handle, 0);
- else
- offset = _fs.status(file_handle).size;
+ if (truncate)
+ _fs.truncate(handle, 0);
+ else
+ offset = _fs.status(handle).size;
+
+ } catch (File_system::Lookup_failed) {
+ handle = _fs.file(dir_handle, file_name,
+ File_system::WRITE_ONLY, true);
+ }
+
+ file = new (env()->heap())
+ Log_file(_fs, handle, dir_path, file_name, offset);
+
+ _log_files.insert(file);
} catch (Permission_denied) {
- PERR("%s/%s: permission denied", path, name);
- throw Root::Unavailable();
+ PERR("%s:%s: permission denied", dir_path, file_name);
} catch (Name_too_long) {
- PERR("%s/%s: name too long", path, name);
- throw Root::Unavailable();
+ PERR("%s:%s: name too long", dir_path, file_name);
} catch (No_space) {
- PERR("%s/%s: no space", path, name);
- throw Root::Unavailable();
+ PERR("%s:%s: no space", dir_path, file_name);
} catch (Out_of_node_handles) {
- PERR("%s/%s: out of node handles", path, name);
- throw Root::Unavailable();
+ PERR("%s:%s: out of node handles", dir_path, file_name);
} catch (Invalid_name) {
- PERR("%s/%s: invalid_name", path, name);
- throw Root::Unavailable();
+ PERR("%s:%s: invalid_name", dir_path, file_name);
} catch (Size_limit_reached) {
- PERR("%s/%s: size limit reached", path, name);
- throw Root::Unavailable();
+ PERR("%s:%s: size limit reached", dir_path, file_name);
} catch (...) {
- PERR("%s/%s: unknown error", path, name);
+ PERR("%s:%s: unknown error", dir_path, file_name);
+ throw;
+ }
+ if (!file) {
+ PERR("file was null");
throw Root::Unavailable();
}
- return new (md_alloc()) Session_component(_fs, file_handle, offset);
+ if (label_prefix && *label_prefix)
+ return new (md_alloc()) Labeled_session_component(label_prefix, *file);
+ return new (md_alloc()) Unlabeled_session_component(*file);
}
public:
diff --git a/repos/os/src/server/fs_log/session.h b/repos/os/src/server/fs_log/session.h
index ebe289446c..1132838dd7 100644
--- a/repos/os/src/server/fs_log/session.h
+++ b/repos/os/src/server/fs_log/session.h
@@ -2,6 +2,9 @@
* \brief Log session that writes messages to a file system.
* \author Emery Hemingway
* \date 2015-05-16
+ *
+ * Message writing is fire-and-forget to prevent
+ * logging from becoming I/O bound.
*/
/*
@@ -19,81 +22,89 @@
#include
#include
+/* Local includes */
+#include "log_file.h"
+
namespace Fs_log {
-
- using namespace Genode;
-
- class Session_component;
-
+ class Session_component;
+ class Unlabeled_session_component;
+ class Labeled_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
+class Fs_log::Session_component : public Rpc_object
+{
+ public:
+ virtual size_t write(String const &string) = 0;
+};
+
+class Fs_log::Unlabeled_session_component : public Session_component
{
private:
- File_system::Session &_fs;
- File_system::File_handle _file_handle;
- File_system::seek_off_t _offset;
+ Log_file &_log_file;
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) { }
+ Unlabeled_session_component(Log_file &log_file)
+ : _log_file(log_file) { }
/*****************
** Log session **
*****************/
- size_t write(Log_session::String const &string)
+ size_t write(Log_session::String const &msg)
{
- if (!(string.is_valid_string())) {
+ if (!msg.is_valid_string()) {
PERR("corrupted string");
return 0;
}
- File_system::Session::Tx::Source &source = *_fs.tx();
+ char const *msg_str = msg.string();
+ size_t msg_len = Genode::strlen(msg_str);
- char const *msg = string.string();
- size_t msg_len = Genode::strlen(msg);
- size_t write_len = msg_len;
+ return _log_file.write(msg_str, 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;
+class Fs_log::Labeled_session_component : public Session_component
+{
+ private:
- while (source.ack_avail())
- source.release_packet(source.get_acked_packet());
+ char _label[Log_session::String::MAX_SIZE];
+ size_t _label_len;
+ Log_file &_log_file;
- 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;
+ public:
- char *buf = source.packet_content(packet);
- memcpy(buf, msg, msg_len);
+ /**
+ * Constructor
+ */
+ Labeled_session_component(char const *label, Log_file &log_file)
+ : _log_file(log_file)
+ {
+ snprintf(_label, sizeof(_label), "[%s] ", label);
+ _label_len = strlen(_label);
+ }
- if (msg_len != write_len)
- buf[msg_len] = '\n';
+ /*****************
+ ** Log session **
+ *****************/
- source.submit_packet(packet);
- return msg_len;
+ size_t write(Log_session::String const &msg)
+ {
+ if (!msg.is_valid_string()) {
+ PERR("corrupted string");
+ return 0;
+ }
+
+ char const *msg_str = msg.string();
+ size_t msg_len = Genode::strlen(msg_str);
+
+ _log_file.write(_label, _label_len);
+ return _log_file.write(msg_str, msg_len);
}
};