From: g0dil Date: Wed, 7 Nov 2007 10:09:14 +0000 (+0000) Subject: Packets/DefaultBundle: Document finalize() action X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=c7512677a51c8ba551ab23611d6e99bdc7a7fdfa;p=senf.git Packets/DefaultBundle: Document finalize() action 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 --- diff --git a/Examples/RateStuffer/Mainpage.dox b/Examples/RateStuffer/Mainpage.dox index 95864eb..70391c5 100644 --- a/Examples/RateStuffer/Mainpage.dox +++ b/Examples/RateStuffer/Mainpage.dox @@ -174,7 +174,7 @@ 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: @@ -182,12 +182,10 @@ \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 } diff --git a/Examples/RateStuffer/ratestuffer.cc b/Examples/RateStuffer/ratestuffer.cc index 631210e..3d42cdf 100644 --- a/Examples/RateStuffer/ratestuffer.cc +++ b/Examples/RateStuffer/ratestuffer.cc @@ -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("\n")), - 2u, 1u ); - module::PassiveSocketSink<> udpSink ( outputSocket ); + module::ActiveSocketSource<> udpSource ( inputSocket ); + RateStuffer stuffer ( 1000000000ul, + senf::DataPacket::create(std::string("\n")), + 2u, 1u ); + module::PassiveSocketSink<> udpSink ( outputSocket ); ppi::connect( udpSource, stuffer ); ppi::connect( stuffer, udpSink ); diff --git a/Packets/DefaultBundle/EthernetPacket.hh b/Packets/DefaultBundle/EthernetPacket.hh index 78fd671..f7d8e6d 100644 --- a/Packets/DefaultBundle/EthernetPacket.hh +++ b/Packets/DefaultBundle/EthernetPacket.hh @@ -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 diff --git a/Packets/DefaultBundle/IpV4Packet.hh b/Packets/DefaultBundle/IpV4Packet.hh index bbc884c..00e2fdb 100644 --- a/Packets/DefaultBundle/IpV4Packet.hh +++ b/Packets/DefaultBundle/IpV4Packet.hh @@ -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 diff --git a/Packets/DefaultBundle/IpV6Extensions.hh b/Packets/DefaultBundle/IpV6Extensions.hh index bc4ba89..8d922b7 100644 --- a/Packets/DefaultBundle/IpV6Extensions.hh +++ b/Packets/DefaultBundle/IpV6Extensions.hh @@ -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 diff --git a/Packets/DefaultBundle/IpV6Packet.hh b/Packets/DefaultBundle/IpV6Packet.hh index cb073e8..da0b046 100644 --- a/Packets/DefaultBundle/IpV6Packet.hh +++ b/Packets/DefaultBundle/IpV6Packet.hh @@ -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 diff --git a/Packets/DefaultBundle/UDPPacket.hh b/Packets/DefaultBundle/UDPPacket.hh index 296597c..9e0177b 100644 --- a/Packets/DefaultBundle/UDPPacket.hh +++ b/Packets/DefaultBundle/UDPPacket.hh @@ -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 diff --git a/Scheduler/Scheduler.hh b/Scheduler/Scheduler.hh index 7a958e3..7696a64 100644 --- a/Scheduler/Scheduler.hh +++ b/Scheduler/Scheduler.hh @@ -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 Boost.Function 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 Boost.Bind)). + arguments may be passed to the handler by the scheduler. + + If you need to pass additional information to your handler, use Boost.Bind: + \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 Boost.Bind 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. diff --git a/Utils/DaemonTools.cc b/Utils/DaemonTools.cc index 06db1a0..c8fe63e 100644 --- a/Utils/DaemonTools.cc +++ b/Utils/DaemonTools.cc @@ -29,13 +29,105 @@ #include #include #include - #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(); diff --git a/Utils/DaemonTools.hh b/Utils/DaemonTools.hh index 15e4e7d..5fb5c2a 100644 --- a/Utils/DaemonTools.hh +++ b/Utils/DaemonTools.hh @@ -22,18 +22,24 @@ /** \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 @@ -47,12 +53,171 @@ // Custom includes #include +#include //#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 + /dev/null. + + 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 /// @{ diff --git a/Utils/Logger/Config.hh b/Utils/Logger/Config.hh index 634d723..4ffb5ac 100644 --- a/Utils/Logger/Config.hh +++ b/Utils/Logger/Config.hh @@ -86,6 +86,17 @@ 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 fallback routing 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 diff --git a/Utils/Logger/ConsoleTarget.cc b/Utils/Logger/ConsoleTarget.cci similarity index 79% rename from Utils/Logger/ConsoleTarget.cc rename to Utils/Logger/ConsoleTarget.cci index 411b2e4..ddd3601 100644 --- a/Utils/Logger/ConsoleTarget.cc +++ b/Utils/Logger/ConsoleTarget.cci @@ -21,28 +21,30 @@ // 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 -//#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" // Local Variables: diff --git a/Utils/Logger/ConsoleTarget.hh b/Utils/Logger/ConsoleTarget.hh index 85cf8d7..122c632 100644 --- a/Utils/Logger/ConsoleTarget.hh +++ b/Utils/Logger/ConsoleTarget.hh @@ -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(); + \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 diff --git a/Utils/Logger/Target.cc b/Utils/Logger/Target.cc index 01864ec..2d4ded9 100644 --- a/Utils/Logger/Target.cc +++ b/Utils/Logger/Target.cc @@ -28,6 +28,7 @@ // Custom includes #include +#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(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() diff --git a/Utils/Logger/Target.cci b/Utils/Logger/Target.cci index da1ee4e..5c77795 100644 --- a/Utils/Logger/Target.cci +++ b/Utils/Logger/Target.cci @@ -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 source) { - area.write((*timeSource_)(), stream, level, msg); + timeSource_.reset(source.release()); } -prefix_ void senf::log::detail::TargetRegistry::timeSource(std::auto_ptr 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) diff --git a/Utils/Logger/Target.hh b/Utils/Logger/Target.hh index c5ac368..a3be572 100644 --- a/Utils/Logger/Target.hh +++ b/Utils/Logger/Target.hh @@ -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 diff --git a/Utils/Logger/Target.ih b/Utils/Logger/Target.ih index 0506fc5..f6cdc85 100644 --- a/Utils/Logger/Target.ih +++ b/Utils/Logger/Target.ih @@ -48,6 +48,8 @@ namespace detail { void timeSource(std::auto_ptr source); + void routed(); + private: TargetRegistry(); @@ -57,6 +59,8 @@ namespace detail { typedef std::set Targets; Targets targets_; boost::scoped_ptr timeSource_; + + bool fallbackRouting_; friend class senf::log::Target; friend class senf::singleton;