diff --git a/repos/os/include/util/print_lines.h b/repos/os/include/util/print_lines.h new file mode 100644 index 000000000..e5dc1b5ba --- /dev/null +++ b/repos/os/include/util/print_lines.h @@ -0,0 +1,96 @@ +/* + * \brief Utility for safely writing multi-line text + * \author Norman Feske + * \date 2014-01-11 + */ + +/* + * Copyright (C) 2014 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 _INCLUDE__OS__PRINT_LINES_H_ +#define _INCLUDE__OS__PRINT_LINES_H_ + +#include + +namespace Genode { + + template + static inline void print_lines(char const *, size_t, FUNC const &); +} + + +/** + * Print multi-line string + * + * \param MAX_LINE_LEN maximum line length, used to dimension a line buffer + * on the stack + * \param string character buffer, not necessarily null-terminated + * \param len number of characters to print + * \param func functor called for each line with 'char const *' as + * argument + * + * In situations where a string is supplied by an untrusted client, we cannot + * simply print the client-provided content as a single string becausewe cannot + * expect the client to null-terminate the string properly. The 'Log_multiline' + * class outputs the content line by line while keeping track of the content + * size. + * + * The output stops when reaching the end of the buffer or when a null + * character is encountered. + */ +template +void Genode::print_lines(char const *string, size_t len, FUNC const &func) +{ + /* skip leading line breaks */ + for (; *string == '\n'; string++); + + /* number of space and tab characters used for indentation */ + size_t const num_indent_chars = + ({ + size_t n = 0; + for (; string[n] == ' ' || string[n] == '\t'; n++); + n; + }); + + char const * const first_line = string; + + while (*string && len) { + + /* + * Skip indentation if the pattern is the same as for the first line. + */ + if (Genode::strcmp(first_line, string, num_indent_chars) == 0) + string += num_indent_chars; + + size_t const line_len = + ({ + size_t i = 0; + for (; string[i] && i < len && string[i] != '\n'; i++); + + /* the newline character belongs to the line */ + if (string[i] == '\n') + i++; + + i; + }); + + if (!line_len) + break; + + /* + * Copy line from (untrusted) caller to local line buffer + */ + char line_buf[MAX_LINE_LEN]; + Genode::strncpy(line_buf, string, Genode::min(line_len + 1, sizeof(line_buf))); + func(line_buf); + + string += line_len; + len -= line_len; + } +} + +#endif /* _INCLUDE__OS__PRINT_LINES_H_ */ diff --git a/repos/os/src/server/report_rom/report_service.h b/repos/os/src/server/report_rom/report_service.h index 1b9989fe9..0c28230c7 100644 --- a/repos/os/src/server/report_rom/report_service.h +++ b/repos/os/src/server/report_rom/report_service.h @@ -18,6 +18,7 @@ #include #include #include +#include /* local includes */ #include @@ -41,18 +42,6 @@ struct Report::Session_component : Genode::Rpc_object, Rom::Writer bool &_verbose; - size_t const _str_line_end(char const * const str, size_t const len) - { - size_t i = 0; - for (; str[i] && i < len && str[i] != '\n'; i++); - - /* the newline character belongs to the line */ - if (str[i] == '\n') - i++; - - return i; - } - Rom::Module &_create_module(Rom::Module::Name const &name) { try { @@ -62,6 +51,12 @@ struct Report::Session_component : Genode::Rpc_object, Rom::Writer } } + static void _log_lines(char const *string, size_t len) + { + Genode::print_lines<200>(string, len, + [&] (char const *line) { PLOG(" %s", line); }); + } + public: Session_component(Rom::Module::Name const &name, size_t buffer_size, @@ -91,41 +86,8 @@ struct Report::Session_component : Genode::Rpc_object, Rom::Writer length = Genode::min(length, _ds.size()); if (_verbose) { - PLOG("report '%s'", _module.name().string()); - - /* - * We cannot simply print the content of the report dataspace - * as one string because we cannot expect the client to null- - * terminate the content properly. Therefore, we output the - * report line by line while keeping track of the dataspace - * size. - */ - - /* pointer and length of remaining string */ - char const *str = _ds.local_addr(); - size_t len = length; - - while (*str && len) { - - size_t const line_len = _str_line_end(str, len); - - if (!line_len) - break; - - /* - * Copy line from (untrusted) report dataspace to local - * line buffer, - */ - char line_buf[200]; - Genode::strncpy(line_buf, str, Genode::min(line_len, sizeof(line_buf))); - PLOG(" %s", line_buf); - - str += line_len; - len -= line_len; - } - - PLOG("\n"); + _log_lines(_ds.local_addr(), length); } _module.write_content(_ds.local_addr(), length);