Packets/DefaultBundle: Document finalize() action
g0dil [Wed, 7 Nov 2007 10:09:14 +0000 (10:09 +0000)]
Scheduler: Elaborate more on boost::bind usage
Utils: Start senf::Daemon base class
Utils/Logger: Make ConsoleTarget a singleton
Utils/Logger: Introduce fallback routing

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@495 270642c3-0616-0410-b53a-bc976706d245

17 files changed:
Examples/RateStuffer/Mainpage.dox
Examples/RateStuffer/ratestuffer.cc
Packets/DefaultBundle/EthernetPacket.hh
Packets/DefaultBundle/IpV4Packet.hh
Packets/DefaultBundle/IpV6Extensions.hh
Packets/DefaultBundle/IpV6Packet.hh
Packets/DefaultBundle/UDPPacket.hh
Scheduler/Scheduler.hh
Utils/DaemonTools.cc
Utils/DaemonTools.hh
Utils/Logger/Config.hh
Utils/Logger/ConsoleTarget.cci [moved from Utils/Logger/ConsoleTarget.cc with 79% similarity]
Utils/Logger/ConsoleTarget.hh
Utils/Logger/Target.cc
Utils/Logger/Target.cci
Utils/Logger/Target.hh
Utils/Logger/Target.ih

index 95864eb..70391c5 100644 (file)
     port 44345 on localhost. The \a outputSocket uses the senf::ConnectedUDPv4SocketProtocol which
     is compatible with the senf::ppi::module::PassiveSocketWriter module.
 
-    \until udpWriter
+    \until udpSink
 
     Here we allocate the components:
 
     \li \a stuffer for the real work and
     \li \a udpWriter to send the packets to \a outputSocket
 
-    \until udpWriter
+    \until udpSink
 
     The \ref senf::ppi::connect() calls setup the necessary connections.
     
-    \until run
-
     The module setup is complete, \ref senf::ppi::run() is called to enter the event loop.
 
     \until }
index 631210e..3d42cdf 100644 (file)
@@ -131,11 +131,11 @@ int main(int argc, char * argv[])
     senf::ConnectedUDPv4ClientSocketHandle outputSocket(
         senf::INet4SocketAddress("localhost:44345"));
 
-    module::ActiveSocketSource<>  udpSource  ( inputSocket );
-    RateStuffer                   stuffer    ( 1000000000ul, 
-                                               senf::DataPacket::create(std::string("<idle>\n")),
-                                               2u, 1u );
-    module::PassiveSocketSink<> udpSink  ( outputSocket );
+    module::ActiveSocketSource<>  udpSource ( inputSocket );
+    RateStuffer                   stuffer   ( 1000000000ul, 
+                                              senf::DataPacket::create(std::string("<idle>\n")),
+                                              2u, 1u );
+    module::PassiveSocketSink<>   udpSink   ( outputSocket );
 
     ppi::connect( udpSource, stuffer   );
     ppi::connect( stuffer,   udpSink );
index 78fd671..f7d8e6d 100644 (file)
@@ -100,6 +100,9 @@ namespace senf {
         \par Associated registries:
             \ref EtherTypes
 
+        \par Finalize action:
+            Set \a type from type of next packet if found in \ref EtherTypes
+
         \ingroup protocolbundle_default
      */
     struct EthernetPacketType
@@ -157,6 +160,9 @@ namespace senf {
         \par Associated registries:
             \ref EtherTypes
 
+        \par Finalize action:
+            Set \a type from type of next packet if found in \ref EtherTypes
+
         \ingroup protocolbundle_default
      */
     struct EthVLanPacketType
index bbc884c..00e2fdb 100644 (file)
@@ -124,6 +124,11 @@ namespace senf {
         \par Associated registries:
             \ref IpTypes
 
+        \par Finalize action:
+            Set \a length from payload size\n
+            Set \a protocol from type of next packet if found in \ref IpTypes\n
+            Calculate \a checksum
+
         \ingroup protocolbundle_default
      */
     struct IpV4PacketType
index bc4ba89..8d922b7 100644 (file)
@@ -66,6 +66,9 @@ namespace senf {
         \par Associated registries:
             \par IpTypes
         
+        \par Finalize action:
+            Set \a nextHeader from type of next packet if found in \ref IpTypes
+
         \ingroup protocolbundle_default
      */
     struct IpV6ExtensionType_Fragment
index cb073e8..da0b046 100644 (file)
@@ -92,6 +92,10 @@ namespace senf {
         \par Associated registries:
             \ref IpTypes
 
+        \par Finalize action:
+            Set \a length from payload size\n
+            Set \a nextHeader from type of next packet if found in \ref IpTypes
+
         \ingroup protocolbundle_default
      */
     struct IpV6PacketType
index 296597c..9e0177b 100644 (file)
@@ -67,6 +67,10 @@ namespace senf {
         \par Fields:
             \ref Parse_UDP
 
+        \par Finalize action:
+            Set \a length from payload size\n
+            Calculate \a checksum
+
         \ingroup protocolbundle_default
      */
     struct UDPPacketType
index 7a958e3..7696a64 100644 (file)
@@ -61,39 +61,57 @@ namespace senf {
         are defined on top of this functionality (e.g. ReadHelper or WriteHelper or the interval
         timers of the PPI).
 
-        \section sched_handlers Handlers
 
-        All handlers are managed as generic <a
+        \section sched_handlers Specifying handlers
+
+        All handlers are passed as generic <a
         href="http://www.boost.org/doc/html/function.html">Boost.Function</a> objects. This allows
         to pass any callable as a handler. Depending on the type of handler, some additional
-        arguments may be passed to the handler by the scheduler. If you want to pass additional
-        arguments to the handler, use <a
-        href="http://www.boost.org/libs/bind/bind.html">Boost.Bind</a>)).
+        arguments may be passed to the handler by the scheduler. 
+
+        If you need to pass additional information to your handler, use <a
+        href="http://www.boost.org/libs/bind/bind.html">Boost.Bind</a>:
+        \code
+        // Pass 'handle' as additional first argument to callback()
+        Scheduler::instance().add(handle, boost::bind(&callback, handle, _1))
+        // Call timeout() handler with argument 'n'
+        Scheduler::instance().timeout(boost::bind(&timeout, n))
+        \endcode
+
+        To use member-functions as callbacks, use either <a
+        href="http://www.boost.org/libs/bind/bind.html">Boost.Bind</a> or senf::membind()
+        \code
+        // e.g. in Foo::Foo() constructor:
+        Scheduler::instance().add(handle_, senf::membind(&Foo::callback, this))
+        \endcode
         
 
-        \section sched_fd File descriptors
+        \section sched_fd Registering file descriptors
         
         File descriptors are managed using add() or remove()
         \code
         Scheduler::instance().add(handle, &callback);
         Scheduler::instance().remove(handle);
-        \endcode
+        \endcode 
+
         The callback will be called with one additional argument. This argument is the event mask of
-        type EventId. This mask will tell, which of the registered events are
-        signaled. Additionally, EV_HUP or EV_ERR on hangup or error condition on the handle.
+        type EventId. This mask will tell, which of the registered events are signaled. The
+        additional flags EV_HUP or EV_ERR (on hangup or error condition) may be set additionally.
 
-        There is always only one handler registered for a file descriptor (registering multiple
-        callbacks for a single fd does not make sense).
+        Only a single handler may be registered for any combination of file descriptor and event
+        (registering multiple callbacks for a single fd and event does not make sense).
 
-        The scheduler will accept an almost arbitrary object as it's first argument. It will use
+        The scheduler will accept any object as \a handle argument as long as retrieve_filehandle()
+        may be called on that object
         \code
         int fd = retrieve_filehandle(handle);
-        \endcode
-        To fetch the file handle given some abstract handle type. The definition of
-        retrieve_filehandle() will be found using ADL.
+        \endcode 
+        to fetch the file handle given some abstract handle type. retrieve_filehandle() will be
+        found using ADL depending on the argument namespace. A default implementation is provided
+        for \c int arguments (file descriptors)
 
 
-        \section sched_timers Timers
+        \section sched_timers Registering timers
 
         The Scheduler has very simple timer support. There is only one type of timer: A single-shot
         deadline timer. More complex timers are built based on this. Timers are managed using
@@ -120,7 +138,7 @@ namespace senf {
         timeoutEarly. By default, this value is set to 0 but may be changed if needed.
 
 
-        \section sched_signals POSIX/UNIX signals
+        \section sched_signals Registering POSIX/UNIX signals
 
         The Scheduler also incorporates standard POSIX/UNIX signals. Signals registered with the
         scheduler will be handled \e synchronously within the event loop.
index 06db1a0..c8fe63e 100644 (file)
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
-
 #include "Exception.hh"
 
 //#include "DaemonTools.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
+///////////////////////////////////////////////////////////////////////////
+// senf::Daemon
+
+prefix_ senf::Daemon::~Daemon()
+{}
+
+prefix_ void senf::Daemon::daemonize(bool v)
+{
+    daemonize_ = v;
+}
+
+prefix_ bool senf::Daemon::daemon()
+{
+    return daemonize_;
+}
+
+prefix_ void senf::Daemon::consoleLog(std::string path, StdStream which)
+{
+    int fd (-1);
+    if (! path.empty()) {
+        int fd (::open(path.c_str(), O_WRONLY | O_APPEND));
+        if (fd < 0)
+            throwErrno("open()");
+    }
+    switch (which) {
+    case StdOut:
+        stdout_ = fd;
+        break;
+    case StdErr:
+        stderr_ = fd;
+        break;
+    case Both:
+        stdout_ = fd;
+        stderr_ = fd;
+        break;
+    }
+}
+
+prefix_ void senf::Daemon::pidFile(std::string f, bool unique)
+{
+    pidfile_ = f;
+    unique_ = unique;
+}
+
+prefix_ void senf::Daemon::detach()
+{}
+
+prefix_ int senf::Daemon::start(int argc, char const ** argv)
+{
+    argc_ = argc;
+    argv_ = argv;
+
+    configure();
+    if (daemonize_)
+        fork();
+    if (! pidfile_.empty())
+        pidfileCreate();
+    main();
+    return 0;
+}
+
+////////////////////////////////////////
+// protected members
+
+prefix_ senf::Daemon::Daemon()
+    : argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""), unique_(true),
+      detached_(false)
+{}
+
+////////////////////////////////////////
+// private members
+
+prefix_ void senf::Daemon::configure()
+{}
+
+prefix_ void senf::Daemon::main()
+{
+    init();
+    detach();
+    run();
+}
+
+prefix_ void senf::Daemon::init()
+{}
+
+prefix_ void senf::Daemon::run()
+{}
+
+prefix_ void senf::Daemon::pidfileCreate()
+{}
+
+///////////////////////////////////////////////////////////////////////////
+
 prefix_ void senf::daemonize()
 {
     int pid = fork();
index 15e4e7d..5fb5c2a 100644 (file)
 
 /** \defgroup process Process Management
 
-    This collection of utilities provides help in managing daemon processes.
+    Future features:
+
+    \li Daemon manager watching the real daemon. Auto-restart, when the daemon fails
+
+    \li Provide access to the config console indirectly via the daemon manager. This allows to
+        connect to the daemon manager console even if the app is not running
+    
+    \li For this to be efficient, the daemon manager must be it's own executable (e.g. senf-launch)
 
-    \idea Add communication between parent and child process to daemonize() and add things like
-        init_done(), failure() etc which allow the daemon process to tell the frontend of successful
-        startup or failure. This probably means moving all the methods into a DaemonTools class (as
-        statics or via a singleton). This would also allow for automatic pid file creation and
-        removal (remove in global destructor).
+    \li auto-detect whether called from senf-launch or not
 
-    \idea Add a DaemonProcess baseclass with init() and main() abstract members which wraps the
-        startup process. DaeminProcess::run() would fork, call init(), create a pid file and then
-        call main(). Exceptions during init()'s execution would be passed to the parent
-        process. This is based on the above API.
+    \li when daemon is running, the console is transparently forwarded to the daemon. The daemon
+        however can still access the daemon manager as a subgroup
+    
+    \li No idea, whether this is sensible: Make the daemon manager completely self-contained (not
+        dependent on any external OS support) by providing our own log-file rotation support.
+
+    This collection of utilities provides help in managing daemon processes.
 
     \idea A closeall()/closemost() function which is useful when starting child processes. We'll use
         getrlimit to now the biggest filehandle and close all of em. closemost() takes a number of
 
 // Custom includes
 #include <string>
+#include <boost/utility.hpp>
 
 //#include "DaemonTools.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
 
 namespace senf {
 
+    /** \brief Daemon process
+
+        This class provides the infrastructure to implement robust daemon processes. A daemon
+        process is implemented by deriving from senf::Daemon and implementing the necessary
+        (virtual) member functions.
+        \code
+        class MyDaemon : public senf::Daemon
+        {
+            void configure() {
+                // Set configuration parameters like daemonize(), pidFile() etc.
+            }
+
+            void init() {
+                // Initialize application. Setup all necessary objects. After init()
+                // has completed, is startup should not fail anymore
+            }
+
+            void run() {
+                // Main application code should be called here.
+            }
+        };
+        \endcode
+
+        The startup procedure is divided into three steps:
+        \li First, configure() is called. configure() should be as simple as possible. It just needs
+            to set the daemon parameters. No further setup should be done here.
+        \li init() is called after fork() but while still connected to the terminal. init() should
+            do all necessary application setup. Here, all configuration or user errors should be
+            detected and properly diagnosed.
+        \li After init() returns, the application will detach from the terminal. Now run() is called
+            to enter the application main loop.
+
+        Since there are times, where separating init() and run() into two separate functions is
+        difficult, instead of defining init() and run(), the member main() may be defined. This
+        member must call detach() as soon as initialization is completed to detach from the
+        foreground terminal.
+
+        
+        
+        \ingroup process
+      */
+    class Daemon : boost::noncopyable
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+        
+        /// Select standard stream to redirect
+        enum StdStream { 
+            StdOut /** Standard output stream */
+        ,   StdErr /** Standard error stream */
+        ,    Both  /** Both, standard output and error stream */
+        }; 
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///\{
+
+        virtual ~Daemon();
+
+        ///\}
+        ///\name Parameters
+        ///\{
+
+        void daemonize(bool);           ///< Configure whether to run in fore- or background
+        bool daemon();                  ///< \c true, if running as daemon
+
+        void consoleLog(std::string, StdStream which = Both); ///< Configure console log file
+                                        /**< May be called multiple times to set the log file
+                                             for stdout and stderr seperately. Any standard stream
+                                             not assigned to a log file will be redirected to
+                                             <tt>/dev/null</tt>. 
+
+                                             When running in the foreground, the log files will be
+                                             ignored. */
+
+        void pidFile(std::string, bool unique = true); ///< Configure pid file
+
+        ///\}
+        ///\name Auxiliary helpers
+        ///\{
+
+        void detach();                  ///< Detach into background now
+                                        /**< This is \e not the same as forking. The process will
+                                             already have forked into the background but until
+                                             detach() is called (either automatically after init()
+                                             returns or manually), the front end (foreground)
+                                             process will wait for the background process to ensure
+                                             successful startup. */
+
+        int argc();                     ///< Access command line parameter count
+        char const ** argv();           ///< Access command line parameters
+
+        void fail(int code=1);          ///< Terminate startup with failure
+
+        ///\}
+        
+        int start(int argc, char const ** argv); ///< Called from main() to launch daemon.
+                                        /**< Normally not called directly but from the
+                                             \ref SENF_DAEMON_MAIN macro. */
+
+    protected:
+        Daemon();
+
+#   ifdef DOXYGEN
+    protected:
+#   else
+    private:
+#   endif
+
+        virtual void configure();       ///< Called before forking to configure the daemon class
+        virtual void main();            ///< Called after forking to execute the main application
+                                        /**< The default implementation will call init(), detach()
+                                             and then run(). It is preferred to override init() and
+                                             run() if possible. */
+        virtual void init();            ///< Called to initialize the main application
+                                        /**< While init() is running, the application still is
+                                             connected to the controlling terminal. Error messages
+                                             will be shown to the user.
+
+                                             This member is only called, if the default main()
+                                             implementation is not overridden. */
+        virtual void run();             ///< Called to execute main application
+                                        /**< Called after detaching from the controlling
+                                             terminal.
+
+                                             This member is only called, if the default main()
+                                             implementation is not overridden. */
+    private:
+
+        void pidfileCreate();
+
+        int argc_;
+        char const ** argv_;
+
+        bool daemonize_;
+        int stdout_;
+        int stderr_;
+        std::string pidfile_;
+        bool unique_;
+
+        bool detached_;
+    };
+
+    /** \brief Provide \c main() function
+
+        This macro will provide a \c main() function to launch the daemon process defined in \a
+        klass. \a klass must be a class derived from senf::Daemon.
+
+        \ingroup process
+     */
+#   define SENF_DAEMON_MAIN(klass)                                                                \
+        int main(int argc, char const ** argv)                                                    \
+        {                                                                                         \
+            klass instance;                                                                       \
+            return instance.start(argc, argv);                                                    \
+        }
+
     /// \addtogroup process
     /// @{
 
index 634d723..4ffb5ac 100644 (file)
 
     The routing statements are processed by the targets in order, the first matching rule will
     decide a log messages fate for that target.
+    
+    There are two cases, where this setup may lead to inadvertently lost log messages:
+    \li When using a library which does internally use the Logger but not initializing the logger in
+        your application.
+    \li When log messages are created during initialization of static objects.
+    Since no route is set up in these cases, the messages will be dropped.
+    
+    To counter this problem, the logger is initially in <em>fallback routing</em> state. If any log
+    message arrives in this state, the message will be unconditionally logged to the console. The
+    first routing statement on any target will take the logger out of this state and normal routing
+    will take place.
 
     \see \ref senf::log::Target
 
similarity index 79%
rename from Utils/Logger/ConsoleTarget.cc
rename to Utils/Logger/ConsoleTarget.cci
index 411b2e4..ddd3601 100644 (file)
 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 /** \file
-    \brief ConsoleTarget non-inline non-template implementation */
+    \brief ConsoleTarget inline non-template implementation */
 
-#include "ConsoleTarget.hh"
 //#include "ConsoleTarget.ih"
 
 // Custom includes
-#include <iostream>
 
-//#include "ConsoleTarget.mpp"
-#define prefix_
-///////////////////////////////cc.p////////////////////////////////////////
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::ConsoleTarget
 
+prefix_ senf::log::ConsoleTarget & senf::log::ConsoleTarget::instance()
+{
+    static ConsoleTarget consoleTarget;
+    return consoleTarget;
+}
+
 prefix_ senf::log::ConsoleTarget::ConsoleTarget()
     : IOStreamTarget(std::cout)
 {}
 
-///////////////////////////////cc.e////////////////////////////////////////
+///////////////////////////////cci.e///////////////////////////////////////
 #undef prefix_
-//#include "ConsoleTarget.mpp"
 
 \f
 // Local Variables:
index 85cf8d7..122c632 100644 (file)
@@ -37,7 +37,13 @@ namespace log {
 
     /** \brief Write log messages to std::cout
 
-        IOStreamTarget writing to std::cout
+        IOStreamTarget writing to std::cout. This is a singleton target which always exists. Access
+        it via senf::log::ConsoleTarget::instance()
+        \code
+        senf::log::ConsoleTarget & console (senf::log::ConsoleTarget::instance());
+
+        console.route<senf::log::Debug>();
+        \endcode
 
         \ingroup targets
      */
@@ -47,17 +53,20 @@ namespace log {
         ///////////////////////////////////////////////////////////////////////////
         ///\name Structors and default members
         ///@{
-
-        ConsoleTarget();
+        
+        static ConsoleTarget & instance();
 
         ///@}
         ///////////////////////////////////////////////////////////////////////////
+
+    private:
+        ConsoleTarget();
     };
 
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
-//#include "ConsoleTarget.cci"
+#include "ConsoleTarget.cci"
 //#include "ConsoleTarget.ct"
 //#include "ConsoleTarget.cti"
 #endif
index 01864ec..2d4ded9 100644 (file)
@@ -28,6 +28,7 @@
 
 // Custom includes
 #include <algorithm>
+#include "ConsoleTarget.hh"
 
 //#include "Target.mpp"
 #define prefix_
@@ -135,6 +136,8 @@ prefix_ void senf::log::Target::route(detail::StreamBase const * stream,
     rib_.insert(i, RoutingEntry(stream, area, level, action));
     if (action == ACCEPT)
         updateRoutingCache(stream, area);
+    // This disables the initial fallback routing
+    detail::TargetRegistry::instance().routed();
 }
 
 prefix_ void senf::log::Target::unroute(detail::StreamBase const * stream,
@@ -199,6 +202,20 @@ prefix_ void senf::log::Target::write(boost::posix_time::ptime timestamp,
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// senf::log::detail::TargetRegistry
+
+prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
+                                                      AreaBase const & area, unsigned level,
+                                                      std::string msg)
+{
+    if (fallbackRouting_)
+        static_cast<Target &>(ConsoleTarget::instance()).v_write( 
+            (*timeSource_)(), stream.v_name(), area.v_name(), level, msg );
+    else
+        area.write( (*timeSource_)(), stream, level, msg );
+}
+
+///////////////////////////////////////////////////////////////////////////
 // senf::log::TimeSource
 
 prefix_ senf::log::TimeSource::~TimeSource()
index da1ee4e..5c77795 100644 (file)
@@ -94,23 +94,21 @@ prefix_ senf::log::Target::action_t senf::log::Target::RoutingEntry::action()
 ///////////////////////////////////////////////////////////////////////////
 // senf::log::detail::TargetRegistry
 
-prefix_ void senf::log::detail::TargetRegistry::write(StreamBase const & stream,
-                                                      AreaBase const & area, unsigned level,
-                                                      std::string msg)
+prefix_ void senf::log::detail::TargetRegistry::timeSource(std::auto_ptr<TimeSource> source)
 {
-    area.write((*timeSource_)(), stream, level, msg);
+    timeSource_.reset(source.release());
 }
 
-prefix_ void senf::log::detail::TargetRegistry::timeSource(std::auto_ptr<TimeSource> source)
+prefix_ void senf::log::detail::TargetRegistry::routed()
 {
-    timeSource_.reset(source.release());
+    fallbackRouting_ = false;
 }
 
 ////////////////////////////////////////
 // private members
 
 prefix_ senf::log::detail::TargetRegistry::TargetRegistry()
-    : timeSource_(new SystemTimeSource())
+    : timeSource_(new SystemTimeSource()), fallbackRouting_(true)
 {}
 
 prefix_ void senf::log::detail::TargetRegistry::registerTarget(Target * target)
index c5ac368..a3be572 100644 (file)
@@ -51,8 +51,8 @@
 
 namespace senf {
 namespace log {
-
-    class TargetRegistry;
+    
+    namespace detail { class TargetRegistry; }
 
     /** \brief Logging target base class
         
@@ -396,6 +396,7 @@ namespace log {
         RIB rib_;
         
         friend class detail::AreaBase;
+        friend class detail::TargetRegistry;
     };
 
     /** \brief Log message time source abstract base class
index 0506fc5..f6cdc85 100644 (file)
@@ -48,6 +48,8 @@ namespace detail {
 
         void timeSource(std::auto_ptr<TimeSource> source);
 
+        void routed();
+
     private:
         TargetRegistry();
         
@@ -57,6 +59,8 @@ namespace detail {
         typedef std::set<Target *> Targets;
         Targets targets_;
         boost::scoped_ptr<TimeSource> timeSource_;
+
+        bool fallbackRouting_;
         
         friend class senf::log::Target;
         friend class senf::singleton<TargetRegistry>;