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 }
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 );
\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
\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
\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
\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
\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
\par Fields:
\ref Parse_UDP
+ \par Finalize action:
+ Set \a length from payload size\n
+ Calculate \a checksum
+
\ingroup protocolbundle_default
*/
struct UDPPacketType
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
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.
#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();
/** \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
/// @{
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
// 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:
/** \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
*/
///////////////////////////////////////////////////////////////////////////
///\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
// Custom includes
#include <algorithm>
+#include "ConsoleTarget.hh"
//#include "Target.mpp"
#define prefix_
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,
}
///////////////////////////////////////////////////////////////////////////
+// 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()
///////////////////////////////////////////////////////////////////////////
// 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)
namespace senf {
namespace log {
-
- class TargetRegistry;
+
+ namespace detail { class TargetRegistry; }
/** \brief Logging target base class
RIB rib_;
friend class detail::AreaBase;
+ friend class detail::TargetRegistry;
};
/** \brief Log message time source abstract base class
void timeSource(std::auto_ptr<TimeSource> source);
+ void routed();
+
private:
TargetRegistry();
typedef std::set<Target *> Targets;
Targets targets_;
boost::scoped_ptr<TimeSource> timeSource_;
+
+ bool fallbackRouting_;
friend class senf::log::Target;
friend class senf::singleton<TargetRegistry>;