Console: Documentation of the configuration support
[senf.git] / Console / Mainpage.dox
index 1d09056..806f96f 100644 (file)
 
     There are three parts to the Config/console library:
 
-    \li The console/config library is based on a \link node_tree tree of console/config
-        nodes. \endlink
-    \li Besides directories, the node contains command nodes. Commands are based on \link
-        console_commands variables or callbacks.\endlink
-    \li The console/config library is utilized by writing configuration files or interactive
-        commands in \link console_parser the console/config language.\endlink
-
+    The Config/Console library is built around several components
+
+    \li The \link node_tree Node tree\endlink represents all configuration options and commands
+        organized in a filesystem like structure.
+    \li \link console_commands Actions\endlink are added to the node tree in the form of command
+        nodes.
+    \li There exist several interfaces to \link console_access access\endlink entries in the node
+        tree: interactive console, reading configuration files etc.
+    
     The node tree works like a directory structure. Commands are entered into this directory
     structure and can be called passing arbitrary arguments. Configuration parameters are just
     commands which set their respective parameter, however the library allows commands to do much
@@ -51,6 +53,8 @@
     library. See above links for more:
 
     \code
+    #include <senf/Console.hh>
+
     // Define callback function.
     void mycommand(std::ostream & os, int foo, int bar)
     {
@@ -60,7 +64,7 @@
 
     namespace kw = senf::console::kw;
 
-    int main(int, char**)
+    int main(int argc, char** argv)
     {
         // Provide global documentation
         senf::console::root()
             .arg("foo")
             .arg(kw::name = "bar", kw::default_value = 0);
 
+       // Parse command line parameters
+       senf::console::parseOptions(argc,argv);
+
         // Start the interactive console server
         senf::console::Server::start(senf::INet4SocketAddress(senf::INet4Address::None, 23232u))
             .name("someServer");
+
+        // Run the scheduler
+       senf::Scheduler::instance().process();
     }
     \endcode
 
-    after this registration, the console can be accessed easily via telnet:
+    after this registration, we can call the command from the command-line using
+
+    <pre>
+    $ someServer --mycommand="1 2"
+    </pre>
+
+    the console can be accessed easily via telnet:
     
     <pre>
     $ telnet localhost 23232
     $
     </pre>
 
+    \see \ref console_testserver for a complete example application
+
+    \section intro_usage Access
+
+    There are several ways to access the node tree:
+    \li By parsing configuration files
+    \li By parsing command line parameters
+    \li By providing interactive console access
+
+    \see console_access
 
     \section intro_nodes The node tree
 
     command.
 
     \see \ref console_commands
+ */
+
+/** \defgroup console_access Accessing the Console/Config tree
+
+    The Console/Config library provides several ways to use the node tree to configure and control
+    an application.
+
+    \autotoc
+
+    \section console_access_config Configuration support
+
+    The configuration support of the Console/Config library revolves around the ConfigSource
+    concept. Each ConfigSource will somehow provide commands which will then be executed against the
+    node tree.
+
+    To simplify the usage, there will always be three interfaces to a specific config source:
+    \li A constructor to build a bare config source which is then added to a
+        senf::console::ConfigBundle (see \ref console_access_multiple)
+    \li A class parsing and executing a single config source. The visible interface of this class is
+        a combination of the constructor and the senf::console::ConfigBundle interfaces.
+    \li A helper function which will do the complete parsing of a single source with default
+        parameters.
+
+    When parsing these configuration sources, it is always possible to optionally change the root
+    node used during parsing and it is also possible to restrict parsing to a command subset. See
+    \ref console_access_partial.
+
+    \subsection console_access_file Configuration files
+
+    <table class="senf fixedwidth">
+    <tr><td><b>Constructor</b></td> <td>senf::console::FileConfig()</td></tr>
+    <tr><td><b>Class</b></td>       <td>senf::console::ConfigFile</td></tr>
+    <tr><td><b>Helper</b></td>      <td>senf::console::parseFile()</td></tr>
+    </table>
+
+    In it's simplest form, parsing a configuration file consists of calling
+    senf::console::parseFile() with the name of the respective config file as argument.
+
+    \code
+    senf::console::parseFile("some.conf");
+    \endcode
+
+    To get more flexible, instantiate a senf::console::ConfigFile instance at use that to parse the
+    file
     
+    \code
+    senf::console::ConfigFile cf ("some.conf");
+    cf.parse();
+    \endcode
 
-    \section intro_language The console/config language
+    If the application supports other configuration sources besides a single configuration file
+    (like command line options) or if it supports multiple configuration files (e.g. a system-wide
+    and a user specific configuration file) see \ref console_access_multiple and add one (or more)
+    senf::console::FileConfig() source to a senf::console::ConfigBundle.
 
-    To call the commands and set parameters, a very simple language is defined. The language is
-    almost declarative (e.g. it does not have any control-flow statements) but is processed
-    imperatively from top to bottom. This is very simple and flexible.
+    \subsubsection console_access_file_syntax Configuration file syntax
+
+    Configuration files are written in a simple configuration language. This language is almost
+    declarative (e.g. it does not have any control-flow statements) but is processed imperatively
+    from top to bottom. This is very simple and flexible.
 
     Commands are referenced by their path in the node tree. To simplify working with deeply nested
     directory structures, the current directory may be changed persistently or temporarily for some
     \endcode
 
     \see \ref console_parser
+
+    \subsection console_access_options Command line options
+
+    <table class="senf fixedwidth">
+    <tr><td><b>Constructor</b></td> <td>senf::console::OptionsConfig()</td></tr>
+    <tr><td><b>Class</b></td>       <td>senf::console::ProgramOptions</td></tr>
+    <tr><td><b>Helper</b></td>      <td>senf::console::parseOptions()</td></tr>
+    </table>
+
+    Command line options can either be parsed by calling the senf::console::parseOptions() helper
+
+    \code
+    senf::console::parseOptions(argc, argv)
+    \endcode
+
+    or more flexibly by instantiating a senf::console::ProgramOptions class
+
+    \code
+    std::vector<std::string> args;
+    senf::console::ProgramOptions opts (argc, argv);
+    opts
+        .nonOptions(args)
+        .alias('c', "--mycommand",true)
+        .alias('C', "--mycommand=2 3");
+    opts.parse();
+    \endcode
+
+    This registeres two short options and accumulates all non-option arguments in \c args.
+    
+    If the application supports other configuration sources besides the command line options (like
+    configuration files) see \ref console_access_multiple and add a senf::console::OptionsConfig()
+    source to a senf::console::ConfigBundle.
+
+    See \ref senf::console::ProgramOptions for the source specific additional parameters. These
+    apply to senf::console::ProgramOptions and to the senf::console::OptionsConfig() source.
+
+    \subsubsection console_access_options_syntax Options syntax
+
+    Command line options are primarily parsed as long-options. Long options start with '--'. Further
+    '-' characters serve as directory separators if required (that is, they are \e only interpreted
+    as directory separator is there is no entry in the current (sub-) directory matching more than a
+    single name component). This still allows using hyphens in node names.
+
+    Options can be abbreviated at each directory boundary: A command <tt>/foo/bar/do</tt> can be
+    called as <tt>--f-b-d</tt> as long as this name is unique.
+
+    Everything after the first '=' character is parsed into argument tokens using the normal
+    config/console parser. If the option has no '=' character, the list of argument tokens will be
+    empty.
+    
+    <table style="font-size:80%" class="senf">
+    <tr><th>Command</th><th>File syntax</th><th>Option syntax</th></tr>
+
+    <tr>
+      <td><tt>void doo()</tt></td>
+      <td><tt>/path/to/doo;</tt></td>
+      <td><tt>--path-to-doo</tt></td>
+    </tr>
+
+    <tr>
+      <td><tt>void doo(std::string const &)</tt></td>
+      <td><tt>/path/to/doo john.doe@everywhere.org;</tt></td>
+      <td><tt>--path-to-doo="john.doe@everywhere.org"</tt></td>
+    </tr>
+    
+    <tr>
+      <td><tt>void doo(std::string const &)</tt></td>
+      <td><tt>/path/to/doo "some test";</tt></td>
+      <td><tt>--path-to-doo='"some text"'</tt></td>
+    </tr>
+
+    <tr>
+      <td><tt>void doo(std::string const &, int)</tt></td>
+      <td><tt>/path/to/doo take 1;</tt></td>
+      <td><tt>--path-to-doo="take 1"</tt></td>
+    </tr>
+    </table>
+
+    The last column is additionally quoted using standard \c sh quoting: quotes in arguments need to
+    be additionally quoted for the shell.
+
+    Short options are registered as aliases for long options. They can be registered with or without
+    an implied parameter and can optionally take a parameter. so after
+
+    \code
+    opts
+        .alias('c', "--mycommand",true)
+        .alias('C', "--mycommand=2 3");
+    \endcode
+
+    we can call
+
+    <pre>
+    $ program -C -c "4 5"
+    $ program -Cc"4 5"
+    </pre>
+
+    which is the same as
+    
+    <pre>
+    $ program --mycommand="2 3" --mycommand="4 5"
+    </pre>
+
+    (Beware, that the second argument to \c alias() is \e not shell quoted). 
+
+    \subsection console_access_root Changing the root node
+
+    When used in it's default state, parsing will always interpret all commands relative to the
+    senf::console::root() node and will parse a file completely.
+
+    The first possibility to control this is to change the root node. This is done by 
+    \li passing that root node to the helper class or to the parse helper as an additional argument
+        (see the respective documentation).
+    \li passing it to the senf:;console::ConfigBundle constructor when parsing multiple sources.
+    
+    for example:
+
+    \code
+    senf::console::parseFile("/etc/myserver.conf", senf::console::root()['config']);
+    \endcode
+
+    This functionality is even more powerful by combining it with \c link nodes: This allows to
+    selectively choose commands from the node tree which are to be made accessible for
+    configuration. See \ref node_tree.
+
+    \subsection console_access_partial Partial / incremental configuration
+
+    Another feature provided by senf::console::ConfigBundle and all helper classes is partial
+    parsing.
+
+    \code
+    // Create a console/config aware object and place it into the node tree
+    FooObject foo;
+    senf::console::add("foo", foo.dir);
+
+    // Open configuration file
+    senf::console::ConfigFile cf ("/etc/myserver.conf");
+    
+    // Parse only commands in the configuration file which are in the foo.dir directory
+    cf.parse(foo.dir);
+
+    ...
+
+    // Anywhere later, parse the rest of the configuration file
+    cf.parse();
+    \endcode
+
+    This feature allows to parse parts of one or more configuration sources before the
+    console/config tree has been fully established. Partial parsing can be applied any number of
+    times to arbitrary nodes. Any command already parsed will be skipped automatically.
+
+    When combining partial parsing with \c chroot() and \c link's, it is important to realize, that
+    <em>partial parsing always applies to the \e real target and ignores links</em>. This is very
+    important: It allows a subsystem to parse it's configuration parameters irrespective of any
+    links pointing to nodes of that subsystem.
+
+    \subsection console_access_multiple Multiple sources
+
+    Most of the time, an application will utilize multiple configuration sources: A global
+    configuration file, maybe a user specific local configuration file, command line options ...
+
+    When parsing configuration commands, especially using partial / incremental parsing, all parse
+    commands should be applied to each configuration source in turn. This is the responsibility of
+    senf::console::ConfigBundle.
+
+    \code
+    senf::console::ScopedDirectory<> config;
+    senf::console::root().add("config", config);
+
+    // Let's enable all logger commands for configuration
+    config.link("logger", senf::console::root()["logger"]);
+
+    // Create bundle and add sources
+    std::vector<std::string> args;
+    senf::console::ConfigBundle conf (senf::console::root()["config"]);
+    conf.add( senf::console::FileConfig("/etc/myserver.conf") );
+    conf.add( senf::console::FileConfig(".myserver.conf") );
+    conf.add( senf::console::OptionsConfig(senf::Daemon::instance().argc(), 
+                                           senf::Daemon::instance().argv()) )
+        .nonOptions(args)
+        .alias('c', "--mycommand",true)
+        .alias('C', "--mycommand=2 3");
+
+    // Parse the logger subsystem commands in '/logger'
+    conf.parse(senf::console::root()['logger']);
+    
+    ...
+
+    // Parse all other configuration commands. All necessary commands and links in '/config' must by
+    // now have been created.  
+    conf.parse();
+    \endcode
+
+    This example parses three configuration sources: Two configuration files and additional
+    parameters specified on the command line. All the configuration commands are placed into the
+    <tt>/config</tt> directory (directly or via links). The configuration sources are parsed in the
+    order they are specified, so in this case, the command line options will override any options
+    specified in one of the configuration files.
+
+    \section console_access_console The interactive console
+
+    To make the console accessible, it must be initialized when the program is started:
+    \code
+    #include <senf/Console.hh>
+
+    int main(int argc, char * argv [])
+    {
+        // Configure console nodes, add commands ...
+
+        // Start console server
+        senf::console::start(senf::INet4SocketAddress(12345u))
+           .name("myserver");
+
+        // You need to enter the scheduler main-loop for the server to work
+        senf::Scheduler::instance().process();
+       
+        // Alternatively enter the main-loop via the PPI
+        // senf::ppi::run();
+    }
+    \endcode
+
+    This will start the server on IPv4 port 12345. The servers name (as displayed in the interactive
+    console prompt) is set to 'myserver'.
+
+    After launching the application, the server can be accessed at the given port:
+    \htmlonly
+    <pre>
+    bash$ telnet localhost 12345
+    Trying 127.0.0.1...
+    Connected to localhost.
+    Escape character is '^]'.
+
+    myserver:/$ exit
+    Connection closed by foreign host.
+    bash$
+    </pre>
+    \endhtmlonly
+
+    \subsection console_serverclient Server and Client objects
+
+    The senf::console::Server and senf::console::Client objects offer further API calls. To access
+    the server instance you need to store away the senf::console::Server reference returned when
+    starting the server so you can later refer to it:
+    \code
+    int main(int, char**)
+    {
+        senf::console::Server & server ( senf::console::start( ... ) );
+    
+        // Do something ...
+
+        server.stop()
+    }
+    \endcode
+
+    The client instance can be accessed via the \c std::ostream arg of any command callback
+    \code
+    void someCallback(std::ostream & os, ... )
+    {
+        senf::console::Client & client (senf::console::Client::get(os));
+    
+        // Use the client's log target
+        client.route<senf::log::Debug, senf::Log::IMPORTANT>();
+    }
+    \endcode
+
+    \see 
+        senf::console::Server for the Server API \n
+        <a href="classsenf_1_1console_1_1Client-members.html">senf::console::Client / List of all
+        members</a> for the Client API
+
+
+    \subsection console_shell Features of the interactive console shell
+
+    The interactive shell will use the GNU readline library for the first connected
+    instance. Further users will not have access to this functionality since GNU readline is
+    completely non-reentrant.
+
+    The shell supports auto-cd and auto-completion: If you enter the name of a directory at the
+    prompt, the console will change to that directory. With auto-completion, any unique beginning of
+    a path component will be completed automatically and transparently to th corresponding full
+    name.
+
  */
 
 /** \defgroup console_commands Supported command types
     \code
     void fun1(std::ostream & os, senf::console::ParseCommandInfo const & command)
     {
-        // We take exactly one argument
-        if (command.arguments().size() != 1) 
-            raise senf::console::SyntaxErrorException("invalid number of arguments");
-
-        senf::console::ParseCommandInfo::TokensRange & argTokens (
-            command.arguments()[0]);
-
-        // The argument must have exactly one token
-        if (argTokens.size() != 1)
-            raise senf::console::SyntaxErrorException("argument syntax error");
-
-        // Retrieve the token value
-        std::string arg (argTokens[0].value());
-
-        // In this example, we just write the argument to the output stream
-        os << arg << std::endl;
+        // Here we declare variables for the arguments
+        std::string value;
+
+        {
+            // We parse the arguments using the CheckedArgumentIteratorWrapper. This wrapper
+            // will throw a SyntaxErrorException if we access a nonexistent argument or if we
+            // do not parse all arguments.
+            senf::console::CheckedArgumentIteratorWrapper args (command.arguments());
+
+            senf::console::ParseCommandInfo::TokensRange argTokens ( *(args++) );
+            if (arg1Tokens.size() != 1)
+                raise senf::console::SyntaxErrorException("argument syntax error");
+            value = arg1Tokens[0];
+        }
+
+        os << value << std::endl;
     }
     \endcode
     
         
     \section console_memberfn Member functions
     
-    Member functions are supported like non-member functions. They must however be added through a
-    senf::console::ScopedDirectory instance to bind them to their instance.
+    Non-static member functions are supported like non-member functions (static member functions are
+    identical to non-members). They must however be added through a senf::console::ScopedDirectory
+    instance to bind them to their instance.
     \code
     class Test1
     {
     }
 
     void senf_console_parse_argument(senf::console::ParseCommandInfo::TokensRange const & tokens,
-                                     MyClass & out)
+                                     Coordinate & out)
     {
-        if (tokens.size() != 2)
-            throw SyntaxErrorException("parameter syntax error");
-        senf::console::ArgumentTraits<int>::parse(
-            senf::console::ParseCommandInfo::TokensRange( tokens.begin(), tokens.begin()+1 ),
-            out.x )
-        senf::console::ArgumentTraits<int>::parse(
-            senf::console::ParseCommandInfo::TokensRange( tokens.begin()+1, tokens.end() ),
-            out.y )
+        senf::console::CheckedArgumentIteratorWrapper arg (tokens);
+        senf::console::parse( *(arg++), out.x );
+        senf::console::parse( *(arg++), out.y );
     }
 
     void senf_console_format_value(Coordinate const & value, std::ostream & os)
     {
         os << '(' << value.x << ' ' << value.y << ')';
     }
-    \endcode
+    \endcode 
+
     The parser will accept an argument with two tokens which are each forwarded to the integer
-    parser. The formatter writes out the value as a parenthesized pair.
+    parser. The senf::console::CheckedArgumentIteratorWrapper ensures two things: That all input
+    tokens are parsed and no extra trailing tokens are left unparsed and it checks, that all
+    referenced tokens really exist.
+
+    The formatter writes out the value as a parenthesized pair.
 
     \code
     Coordinate fun5(Coordinate const & p) { return Coordinate(2*p.x, 2*p.y) }