yate/yatengine.h

1905 lines
58 KiB
C
Raw Permalink Normal View History

/**
* yatengine.h
* This file is part of the YATE Project http://YATE.null.ro
*
* Engine, plugins and messages related classes
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
2023-05-23 14:01:06 +00:00
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef __YATENGINE_H
#define __YATENGINE_H
#ifndef __cplusplus
#error C++ is required
#endif
#include <yateclass.h>
/**
* Holds all Telephony Engine related classes.
*/
namespace TelEngine {
/**
* A class for parsing and quickly accessing INI style configuration files
* @short Configuration file handling
*/
class YATE_API Configuration : public String
{
friend class Engine;
YNOCOPY(Configuration); // no automatic copies please
public:
/**
* Create an empty configuration
*/
Configuration();
/**
* Create a configuration from a file
* @param filename Name of file to initialize from
* @param warn True to warn if the configuration could not be loaded
*/
explicit Configuration(const char* filename, bool warn = true);
/**
* Assignment from string operator
*/
inline Configuration& operator=(const String& value)
{ String::operator=(value); return *this; }
/**
* Get the number of sections
* @return Count of sections
*/
inline unsigned int sections() const
{ return m_sections.length(); }
/**
* Get the number of non null sections
* @return Count of sections
*/
inline unsigned int count() const
{ return m_sections.count(); }
/**
* Retrieve an entire section
* @param index Index of the section
* @return The section's content or NULL if no such section
*/
NamedList* getSection(unsigned int index) const;
/**
* Retrieve an entire section
* @param sect Name of the section
* @return The section's content or NULL if no such section
*/
NamedList* getSection(const String& sect) const;
/**
* Locate a key/value pair in the section.
* @param sect Name of the section
* @param key Name of the key in section
* @return A pointer to the key/value pair or NULL.
*/
NamedString* getKey(const String& sect, const String& key) const;
/**
* Retrieve the value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @return The string contained in the key or the default
*/
const char* getValue(const String& sect, const String& key, const char* defvalue = 0) const;
/**
* Retrieve the numeric value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @param minvalue Minimum value allowed for the parameter
* @param maxvalue Maximum value allowed for the parameter
* @param clamp Control the out of bound values: true to adjust to the nearest
* bound, false to return the default value
* @return The number contained in the key or the default
*/
int getIntValue(const String& sect, const String& key, int defvalue = 0,
int minvalue = INT_MIN, int maxvalue = INT_MAX, bool clamp = true) const;
/**
* Retrieve the numeric value of a key in a section trying first a table lookup.
* @param sect Name of the section
* @param key Name of the key in section
* @param tokens A pointer to an array of tokens to try to lookup
* @param defvalue Default value to return if not found
* @return The number contained in the key or the default
*/
int getIntValue(const String& sect, const String& key, const TokenDict* tokens, int defvalue = 0) const;
/**
* Retrieve the 64-bit numeric value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @param minvalue Minimum value allowed for the parameter
* @param maxvalue Maximum value allowed for the parameter
* @param clamp Control the out of bound values: true to adjust to the nearest
* bound, false to return the default value
* @return The number contained in the key or the default
*/
int64_t getInt64Value(const String& sect, const String& key, int64_t defvalue = 0,
int64_t minvalue = LLONG_MIN, int64_t maxvalue = LLONG_MAX, bool clamp = true) const;
/**
* Retrieve the floating point value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @return The numeric value contained in the key or the default
*/
double getDoubleValue(const String& sect, const String& key, double defvalue = 0.0) const;
/**
* Retrieve the boolean value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @return The boolean value contained in the key or the default
*/
bool getBoolValue(const String& sect, const String& key, bool defvalue = false) const;
/**
* Deletes an entire section
* @param sect Name of section to delete, NULL to delete all
*/
void clearSection(const char* sect = 0);
/**
* Makes sure a section with a given name exists, creates if required
* @param sect Name of section to check or create
* @return The section's content or NULL if no such section
*/
NamedList* createSection(const String& sect);
/**
* Deletes a key/value pair
* @param sect Name of section
* @param key Name of the key to delete
*/
void clearKey(const String& sect, const String& key);
/**
* Add the value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key to add in the section
* @param value Value to set in the key
*/
void addValue(const String& sect, const char* key, const char* value = 0);
/**
* Set the value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key in section, will be created if missing
* @param value Value to set in the key
*/
void setValue(const String& sect, const char* key, const char* value = 0);
/**
* Set the numeric value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key in section, will be created if missing
* @param value Value to set in the key
*/
void setValue(const String& sect, const char* key, int value);
/**
* Set the boolean value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key in section, will be created if missing
* @param value Value to set in the key
*/
void setValue(const String& sect, const char* key, bool value);
/**
* Load the configuration from file
* @param warn True to also warn if the configuration could not be loaded
* @return True if successfull, false for failure
*/
bool load(bool warn = true);
/**
* Save the configuration to file
* @return True if successfull, false for failure
*/
bool save() const;
private:
/**
* Load the main configuration file
* @param warn True to also warn if the configuration could not be loaded
* @return True if successfull, false for failure
*/
inline bool loadMain(bool warn = true) {
m_main = true;
return load(warn);
}
ObjList *getSectHolder(const String& sect) const;
ObjList *makeSectHolder(const String& sect);
bool loadFile(const char* file, String sect, unsigned int depth, bool warn, void* priv);
ObjList m_sections;
bool m_main;
};
/**
* Class that implements atomic / locked access and operations to its shared variables
* @short Atomic access and operations to shared variables
*/
class YATE_API SharedVars : public Mutex, public RefObject
{
public:
/**
* Constructor
* @param name Optional name
*/
inline SharedVars(const char* name = 0)
: Mutex(false,"SharedVars"), m_vars(name)
{ }
/**
* Get the string value of a variable
* @param name Name of the variable
* @param rval String to return the value into
*/
void get(const String& name, String& rval);
/**
* Set the string value of a variable
* @param name Name of the variable to set
* @param val New value to assign to a variable
*/
void set(const String& name, const char* val);
/**
* Create and set a variable only if the variable is not already set
* @param name Name of the variable to set
* @param val New value to assign to a variable
* @return True if a new variable was created
*/
bool create(const String& name, const char* val = 0);
/**
* Clear a variable
* @param name Name of the variable to clear
*/
void clear(const String& name);
/**
* Clear all variables. Does nothing for Engine (global shared list)
*/
void clearAll();
/**
* Check if a variable exists
* @param name Name of the variable
* @return True if the variable exists
*/
bool exists(const String& name);
/**
* Atomically increment a variable as unsigned integer
* @param name Name of the variable
* @param wrap Value to wrap around at, zero disables
* @return Value of the variable before increment, zero if it was not defined or not numeric
*/
uint64_t inc(const String& name, uint64_t wrap = 0);
/**
* Atomically decrement a variable as unsigned integer
* @param name Name of the variable
* @param wrap Value to wrap around at, zero disables (stucks at zero)
* @return Value of the variable after decrement, zero if it was not defined or not numeric
*/
uint64_t dec(const String& name, uint64_t wrap = 0);
/**
* Atomically add a value to a variable as unsigned integer
* @param name Name of the variable
* @param val Value to add
* @param wrap Value to wrap around at, zero disables
* @return Value of the variable before addition, zero if it was not defined or not numeric
*/
uint64_t add(const String& name, uint64_t val, uint64_t wrap = 0);
/**
* Atomically substract a value from a variable as unsigned integer
* @param name Name of the variable
* @param val Value to substract
* @param wrap Value to wrap around at, zero disables
* @return Value of the variable after substraction, zero if it was not defined or not numeric
*/
uint64_t sub(const String& name, uint64_t val, uint64_t wrap = 0);
/**
* Atomically copy parameters to destination
* @param dest Destination list
* @param prefix Optional prefix to match in parameter names
* @param skipPrefix Skip over the prefix when building new parameter name
* @param replace Set to true to replace list parameter instead of adding a new one
*/
inline void copy(NamedList& dest, const String& prefix = String::empty(),
bool skipPrefix = true, bool replace = false) {
Lock lck(this);
if (prefix)
dest.copySubParams(m_vars,prefix,skipPrefix,replace);
else
dest.copyParams(m_vars);
}
/**
* Retrieve list name
* @return List name
*/
virtual const String& toString() const
{ return m_vars; }
/**
* Retrieve a named list of SharedVars. Create it if not found
* @param dest Destination to be filled with requested list
* @param name Name of the list
* @return True id destination is iset. The function will fail if name is empty
*/
static bool getList(RefPointer<SharedVars>& dest, const String& name);
private:
NamedList m_vars;
};
class MessageDispatcher;
class MessageRelay;
class Engine;
/**
* This class holds the messages that are moved around in the engine.
* @short A message container class
*/
class YATE_API Message : public NamedList
{
friend class MessageDispatcher;
public:
/**
* Creates a new message.
*
* @param name Name of the message - must not be NULL or empty
* @param retval Default return value
* @param broadcast Broadcast flag, true if handling the mesage must not stop it
*/
explicit Message(const char* name, const char* retval = 0, bool broadcast = false);
/**
* Copy constructor.
* Note that user data and notification are not copied
* @param original Message we are copying from
*/
Message(const Message& original);
/**
* Copy constructor that can alter the broadcast flag.
* Note that user data and notification are not copied
* @param original Message we are copying from
* @param broadcast Broadcast flag, true if handling the mesage must not stop it
*/
Message(const Message& original, bool broadcast);
/**
* Destruct the message and dereferences any user data
*/
~Message();
/**
* Get a pointer to a derived class given that class name
* @param name Name of the class we are asking for
* @return Pointer to the requested class or NULL if this object doesn't implement it
*/
virtual void* getObject(const String& name) const;
/**
* Retrieve a reference to the value returned by the message.
* @return A reference to the value the message will return
*/
inline String& retValue()
{ return m_return; }
/**
* Retrieve a const reference to the value returned by the message.
* @return A reference to the value the message will return
*/
inline const String& retValue() const
{ return m_return; }
/**
* Retrieve the object associated with the message
* @return Pointer to arbitrary user RefObject
*/
inline RefObject* userData() const
{ return m_data; }
/**
* Set obscure data associated with the message.
* The user data is reference counted to avoid stray pointers.
* Note that setting new user data will disable any notification.
* @param data Pointer to arbitrary user data
*/
void userData(RefObject* data);
/**
* Get a pointer to a derived class of user data given that class name
* @param name Name of the class we are asking for
* @return Pointer to the requested class or NULL if user object id NULL or doesn't implement it
*/
inline void* userObject(const String& name) const
{ return m_data ? m_data->getObject(name) : 0; }
/**
* Enable or disable notification of any @ref MessageNotifier that was set
* as user data. This method must be called after userData()
* @param notify True to have the message call the notifier
*/
inline void setNotify(bool notify = true)
{ m_notify = notify; }
/**
* Retrieve the broadcast flag
* @return True if the message is a broadcast (handling does not stop it)
*/
inline bool broadcast() const
{ return m_broadcast; }
/**
* Reset message. This method should be used when message is going to be re-dispatched.
* Reset message time, track param, return value.
* @param tm Time to set, defaults to current time
*/
void resetMsg(Time tm = Time::now());
/**
* Retrieve a reference to the creation time of the message.
* @return A reference to the @ref Time when the message was created
*/
inline Time& msgTime()
{ return m_time; }
/**
* Retrieve a const reference to the creation time of the message.
* @return A reference to the @ref Time when the message was created
*/
inline const Time& msgTime() const
{ return m_time; }
/**
* Retrieve a reference to the time when message was put in dispatcher.
* @return A reference to the @ref Time when the message was put in dispatcher queue.
* May be 0 if the message was not put in queue or time tracking is not enabled in dispatcher
*/
inline Time& msgTimeEnqueue()
{ return m_timeEnqueue; }
/**
* Retrieve a const reference to the time when message was put in dispatcher.
* @return A reference to the @ref Time when the message was put in dispatcher queue.
* May be 0 if the message was not put in queue or time tracking is not enabled in dispatcher
*/
inline const Time& msgTimeEnqueue() const
{ return m_timeEnqueue; }
/**
* Retrieve a reference to the time when message was dispatched.
* @return A reference to the @ref Time when the message was dispatched
* May be 0 if the message was not dispatched yet or time tracking is not enabled in dispatcher
*/
inline Time& msgTimeDispatch()
{ return m_timeDispatch; }
/**
* Retrieve a const reference to the time when message was dispatched.
* @return A reference to the @ref Time when the message was dispatched
* May be 0 if the message was not dispatched yet or time tracking is not enabled in dispatcher
*/
inline const Time& msgTimeDispatch() const
{ return m_timeDispatch; }
/**
* Name assignment operator
*/
inline Message& operator=(const char* value)
{ String::operator=(value); return *this; }
/**
* Encode the message into a string adequate for sending for processing
* to an external communication interface
* @param id Unique identifier to add to the string
*/
String encode(const char* id) const;
/**
* Encode the message into a string adequate for sending as answer
* to an external communication interface
* @param received True if message was processed locally
* @param id Unique identifier to add to the string
*/
String encode(bool received, const char* id) const;
/**
* Decode a string from an external communication interface for processing
* in the engine. The message is modified accordingly.
* @param str String to decode
* @param id A String object in which the identifier is stored
* @return -2 for success, -1 if the string was not a text form of a
* message, index of first erroneous character if failed
*/
int decode(const char* str, String& id);
/**
* Decode a string from an external communication interface that is an
* answer to a specific external processing request.
* @param str String to decode
* @param received Pointer to variable to store the dispatch return value
* @param id The identifier expected
* @return -2 for success, -1 if the string was not the expected answer,
* index of first erroneous character if failed
*/
int decode(const char* str, bool& received, const char* id);
protected:
/**
* Notify the message it has been dispatched.
* The default behaviour is to call the dispatched() method of the user
* data if it implements @ref MessageNotifier
* @param accepted True if one handler accepted the message
*/
virtual void dispatched(bool accepted);
private:
Message(); // no default constructor please
Message& operator=(const Message& value); // no assignment please
String m_return;
Time m_time;
Time m_timeEnqueue;
Time m_timeDispatch;
RefObject* m_data;
bool m_notify;
bool m_broadcast;
void commonEncode(String& str) const;
int commonDecode(const char* str, int offs);
};
/**
* The purpose of this class is to hold a message received method that is
* called for matching messages. It holds as well the matching criteria
* and priority among other handlers.
* @short A message handler
*/
class YATE_API MessageHandler : public String
{
friend class MessageDispatcher;
YNOCOPY(MessageHandler); // no automatic copies please
public:
/**
* Creates a new message handler.
* @param name Name of the handled message - may be NULL
* @param priority Priority of the handler, 0 = top
* @param trackName Name to be used in handler tracking
* @param addPriority True to append :priority to trackName
*/
explicit MessageHandler(const char* name, unsigned priority = 100,
const char* trackName = 0, bool addPriority = true);
/**
* Handler destructor.
*/
virtual ~MessageHandler();
/**
* Destroys the object, performs cleanup first
*/
virtual void destruct();
/**
* This method is called whenever the registered name matches the message.
* @param msg The received message
* @return True to stop processing, false to try other handlers
*/
virtual bool received(Message& msg) = 0;
/**
* Find out the priority of the handler
* @return Stored priority of the handler, 0 = top
*/
inline unsigned priority() const
{ return m_priority; }
/**
* Retrieve the tracking name of this handler
* @return Name that is to be used in tracking operation
*/
inline const String& trackName() const
{ return m_trackName; }
/**
* Retrieve the tracking name of this handler without added priority
* @return Name that is to be used in tracking operation
*/
inline const String& trackNameOnly() const
{ return m_trackNameOnly; }
/**
* Set a new tracking name for this handler.
* Works only if the handler was not yet inserted into a dispatcher
* @param name Name that is to be used in tracking operation
*/
inline void trackName(const char* name) {
if (m_dispatcher)
return;
m_trackName = name;
String tmp;
tmp << ':' << priority();
if (m_trackName.endsWith(tmp))
m_trackNameOnly = m_trackName.substr(0,m_trackName.length() - tmp.length());
else
m_trackNameOnly = m_trackName;
}
/**
* Retrive the objects counter associated to this handler
* @return Pointer to handler's objects counter or NULL
*/
inline NamedCounter* objectsCounter() const
{ return m_counter; }
/**
* Retrieve the filter (if installed) associated to this handler
* @return MatchingItemBase pointer, NULL if not set
*/
inline const MatchingItemBase* filter() const
{ return m_filter; }
/**
* Set a filters list associated to this handler
* @param filter Pointer to the filters list to install, will be owned and
* destroyed by the handler
*/
inline void setFilter(MatchingItemBase* filter) {
if (filter == m_filter)
return;
clearFilter();
m_filter = filter;
}
/**
* Set a filter for this handler
* @param filter Pointer to the filter to install, will be owned and
* destroyed by the handler. The filter may be a NamedPointer carrying a Regexp
*/
void setFilter(NamedString* filter);
/**
* Set a filter for this handler
* @param name Name of the parameter to filter
* @param value Value of the parameter to filter
*/
inline void setFilter(const char* name, const char* value)
{ setFilter(new MatchingItemString(name,value)); }
/**
* Remove and destroy any filter associated to this handler
*/
void clearFilter();
protected:
/**
* Remove the handler from its dispatcher, remove any installed filter.
* This method is called internally from destruct and the destructor
*/
void cleanup();
/**
* Internal use received function, do not call or override!
* @param msg The received message
* @return True if message was processed
*/
virtual bool receivedInternal(Message& msg);
/**
* Internal use handler unlock, do not call!
*/
void safeNowInternal();
private:
String m_trackName;
String m_trackNameOnly;
unsigned m_priority;
AtomicInt m_unsafe;
MessageDispatcher* m_dispatcher;
MatchingItemBase* m_filter;
NamedCounter* m_counter;
};
/**
* A multiple message receiver to be invoked by a message relay
* @short A multiple message receiver
*/
class YATE_API MessageReceiver : public GenObject
{
public:
/**
* This method is called from the message relay.
* @param msg The received message
* @param id The identifier with which the relay was created
* @return True to stop processing, false to try other handlers
*/
virtual bool received(Message& msg, int id) = 0;
};
/**
* A message handler that allows to relay several messages to a single receiver
* @short A message handler relay
*/
class YATE_API MessageRelay : public MessageHandler
{
YNOCOPY(MessageRelay); // no automatic copies please
public:
/**
* Creates a new message relay.
* @param name Name of the handled message - may be NULL
* @param receiver Receiver of th relayed messages
* @param id Numeric identifier to pass to receiver
* @param priority Priority of the handler, 0 = top
* @param trackName Name to be used in handler tracking
* @param addPriority True to append :priority to trackName
*/
inline MessageRelay(const char* name, MessageReceiver* receiver, int id,
int priority = 100, const char* trackName = 0, bool addPriority = true)
: MessageHandler(name,priority,trackName,addPriority),
m_receiver(receiver), m_id(id)
{ }
/**
* This method is not called from MessageHandler through polymorphism
* and should not be used or reimplemented.
* @param msg The received message
* @return True if the receiver exists and has handled the message
*/
virtual bool received(Message& msg)
{ return m_receiver && m_receiver->received(msg,m_id); }
/**
* Get the ID of this message relay
* @return Numeric identifier passed to receiver
*/
inline int id() const
{ return m_id; }
/**
* Internal use received function, do not call or override!
* @param msg The received message
* @return True if message was processed
*/
virtual bool receivedInternal(Message& msg);
private:
MessageReceiver* m_receiver;
int m_id;
};
/**
* An abstract class to implement hook methods called after any message has
* been dispatched. If an object implementing MessageNotifier is set as user
* data in a @ref Message then the dispatched() method will be called.
* @short Post-dispatching message hook
*/
class YATE_API MessageNotifier
{
public:
/**
* Destructor. Keeps compiler form complaining.
*/
virtual ~MessageNotifier();
/**
* This method is called after a message was dispatched.
* @param msg The already dispatched message message
* @param handled True if a handler claimed to have handled the message
*/
virtual void dispatched(const Message& msg, bool handled) = 0;
};
/**
* An abstract message notifier that can be inserted in a @ref MessageDispatcher
* to implement hook methods called after any message has been dispatched.
* No new methods are provided - we only need the multiple inheritance.
* @short Post-dispatching message hook that can be added to a list
*/
class YATE_API MessagePostHook : public RefObject, public MessageNotifier
{
};
/**
* The dispatcher class is a hub that holds a list of handlers to be called
* for the messages that pass trough the hub. It can also handle a queue of
* messages that are typically dispatched by a separate thread.
* @short A message dispatching hub
*/
class YATE_API MessageDispatcher : public GenObject
{
friend class Engine;
YNOCOPY(MessageDispatcher); // no automatic copies please
public:
/**
* Creates a new message dispatcher.
* @param trackParam Name of the parameter used in tracking handlers
*/
MessageDispatcher(const char* trackParam = 0);
/**
* Destroys the dispatcher and the installed handlers.
*/
~MessageDispatcher();
/**
* Retrieve the tracker parameter name
* @return Name of the parameter used to track message dispatching
*/
inline const String& trackParam() const
{ return m_trackParam; }
/**
* Installs a handler in the dispatcher.
* The handlers are installed in ascending order of their priorities.
* There is NO GUARANTEE on the order of handlers with equal priorities
* although for avoiding uncertainity such handlers are sorted by address.
* @param handler A pointer to the handler to install
* @return True on success, false on failure
*/
bool install(MessageHandler* handler);
/**
* Uninstalls a handler from the dispatcher.
* @param handler A pointer to the handler to uninstall
* @return True on success, false on failure
*/
bool uninstall(MessageHandler* handler);
/**
* Synchronously dispatch a message to the installed handlers.
* Handlers matching the message name and filter parameter are called in
* their installed order (based on priority) until one returns true.
* If the message has the broadcast flag set all matching handlers are
* called and the return value is true if any handler returned true.
* Note that in some cases when a handler is removed from the list
* other handlers with equal priority may be called twice.
* @param msg The message to dispatch
* @return True if one handler accepted it, false if all ignored
*/
bool dispatch(Message& msg);
/**
* Put a message in the waiting queue for asynchronous dispatching
* @param msg The message to enqueue, will be destroyed after dispatching
* @return True if successfully queued, false otherwise
*/
bool enqueue(Message* msg);
/**
* Dispatch all messages from the waiting queue
*/
void dequeue();
/**
* Dispatch one message from the waiting queue
* @return True if success, false if the queue is empty
*/
bool dequeueOne();
/**
* Set a limit to generate warning when a message took too long to dispatch
* @param usec Warning time limit in microseconds, zero to disable
*/
inline void warnTime(u_int64_t usec)
{ m_warnTime = usec; }
/**
* Enable or disable message events time (queued / dispatch)
* @param on True to enable, false to disable
*/
inline void traceTime(bool on = false)
{ m_traceTime = on; }
/**
* Enable or disable message handler duration
* @param on True to enable, false to disable
*/
inline void traceHandlerTime(bool on = false)
{ m_traceHandlerTime = on; }
/**
* Clear all the message handlers and post-dispatch hooks
*/
void clear();
/**
* Check if there is at least one message in the queue
* @return True if the queue holds at least one message
*/
inline bool hasMessages() const
{ return m_messages.get() || m_messages.next(); }
/**
* Check if there is at least one handler installed
* @return True if at least one handler is installed
*/
inline bool hasHandlers() const
{ return m_handlers.get() || m_handlers.next(); }
/**
* Check if there is at least one message hook installed
* @return True if at least one hook is installed
*/
inline bool hasHooks() const
{ return m_hooks.get() || m_hooks.next(); }
/**
* Get the number of messages waiting in the queue
* @return Count of messages in the queue
*/
unsigned int messageCount();
/**
* Get the number of handlers in this dispatcher
* @return Count of handlers
*/
unsigned int handlerCount();
/**
* Get the number of post-handling hooks in this dispatcher
* @return Count of hooks
*/
unsigned int postHookCount();
/**
* Get the total number of enqueued messages
* @return Count of enqueued messages
*/
u_int64_t enqueueCount() const
{ return m_enqueueCount; }
/**
* Get the total number of dequeued messages
* @return Count of dequeued messages
*/
u_int64_t dequeueCount() const
{ return m_dequeueCount; }
/**
* Get the total number of dispatched messages
* @return Count of dispatched messages
*/
u_int64_t dispatchCount() const
{ return m_dispatchCount; }
/**
* Get the queued messages high watermark
* @return Highest number of messages in queue
*/
u_int64_t queuedMax() const
{ return m_queuedMax; }
/**
* Get the average dequeued message age in milliseconds or microseconds
* @param usec True to return microseconds instead of milliseconds
* @return Average age of dequeued messages
*/
u_int64_t messageAge(bool usec = false) const
{ return usec ? m_msgAvgAge : ((m_msgAvgAge + 500) / 1000); }
/**
* Retrieve the handlers list lock object
* @return Handlers list lock object reference
*/
inline RWLock& handlersLock()
{ return m_handlersLock; }
/**
* Retrieve all statistics counters
* @param enqueued Returns count of enqueued messages
* @param dequeued Returns count of dequeued messages
* @param dispatched Returns count of all dispatched messages, including dequeued ones
* @param queueMax Returns queued high watermark
*/
void getStats(u_int64_t& enqueued, u_int64_t& dequeued, u_int64_t& dispatched, u_int64_t& queueMax);
/**
* Install or remove a hook to catch messages after being dispatched
* @param hook Pointer to a post-dispatching message hook
* @param remove Set to True to remove the hook instead of adding
*/
void setHook(MessagePostHook* hook, bool remove = false);
/**
* Fill handlers status info
* @param matchName True to match message name, false to match handler trackname
* @param match Value to match. May be a regular expression
* @param details Optional pointer to string to be filled with details
* @param total Optional pointer to data to be filled with total number of handlers
* @return The number of matched handlers
*/
unsigned int fillHandlersInfo(bool matchName, const String& match, String* details = 0,
unsigned int* total = 0);
protected:
/**
* Set the tracked parameter name
* @param paramName Name of the parameter used in tracking handlers
*/
inline void trackParam(const char* paramName)
{ m_trackParam = paramName; }
private:
ObjList m_handlers;
ObjList m_messages;
ObjList m_hooks;
RWLock m_handlersLock;
RWLock m_messagesLock;
RWLock m_hooksLock;
ObjList* m_msgAppend;
ObjList* m_hookAppend;
String m_trackParam;
unsigned int m_changes;
u_int64_t m_warnTime;
u_int64_t m_enqueueCount;
u_int64_t m_dequeueCount;
u_int64_t m_dispatchCount;
u_int64_t m_queuedMax;
u_int64_t m_msgAvgAge;
bool m_traceTime;
bool m_traceHandlerTime;
int m_hookCount;
bool m_hookHole;
};
/**
* Abstract class for message hook
* @short Abstract message hook
*/
class YATE_API MessageHook : public RefObject
{
public:
/**
* Try to enqueue a message to this hook's queue
* @param msg The message to enqueue
* @return True if the message was enqueued.
*/
virtual bool enqueue(Message* msg) = 0;
/**
* Clear this hook data
*/
virtual void clear() = 0;
/**
* Check if the given message can be inserted in this queue
* @param msg The message to check
* @return True if the message can be inserted in this queue
*/
virtual bool matchesFilter(const Message& msg) = 0;
};
/**
* MessageQueue class allows to create a private queue for a message who matches
* the specified filters.
* @short A message queue
*/
class YATE_API MessageQueue : public MessageHook, public Mutex
{
friend class Engine;
public:
/**
* Creates a new message queue.
* @param hookName Name of the message served by this queue
* @param numWorkers The number of workers who serve this queue
*/
MessageQueue(const char* hookName, int numWorkers = 0);
/**
* Destroys the message queue
*/
~MessageQueue();
/**
* Append a message in the queue
* @param msg The message to enqueue, will be destroyed after the processing is done
* @return True if successfully queued, false otherwise
*/
virtual bool enqueue(Message* msg);
/**
* Process a message from the waiting queue
* @return False if the message queue is empty
*/
bool dequeue();
/**
* Add a new filter to this queue
* @param name The filter name
* @param value The filter value
*/
void addFilter(const char* name, const char* value);
/**
* Remove a filter form this queue
* @param name The filter name
*/
void removeFilter(const String& name);
/**
* Clear private data
*/
virtual void clear();
/**
* Remove a thread from workers list
* @param thread The thread to remove
*/
void removeThread(Thread* thread);
/**
* Helper method to obtain the number of unprocessed messages in the queue
* @return The number of queued messages.
*/
inline unsigned int count() const
{ return m_count; }
/**
* Obtain the filter list for this queue
* @return The filter list
*/
inline const NamedList& getFilters() const
{ return m_filters; }
/**
* Check if the given message can be inserted in this queue
* @param msg The message to check
* @return True if the message can be inserted in this queue
*/
virtual bool matchesFilter(const Message& msg);
protected:
/**
* Callback method for message processing
* Default calls Engine::dispatch
* @param msg The message to process
*/
virtual void received(Message& msg);
private:
NamedList m_filters;
ObjList m_messages;
ObjList m_workers;
ObjList* m_append;
unsigned int m_count;
};
/**
* Initialization and information about plugins.
* Plugins are located in @em shared libraries that are loaded at runtime.
*
*<pre>
* // Create static Plugin object by using the provided macro
* INIT_PLUGIN(Plugin);
*</pre>
* @short Plugin support
*/
class YATE_API Plugin : public GenObject, public DebugEnabler
{
public:
/**
* Creates a new Plugin container.
* @param name the undecorated name of the library that contains the plugin
* @param earlyInit True to initialize the plugin early
*/
explicit Plugin(const char* name, bool earlyInit = false);
/**
* Destroys the plugin.
* The destructor must never be called directly - the Loader will do it
* when the shared object's reference count reaches zero.
*/
virtual ~Plugin();
/**
* Get a string representation of this object
* @return Name of the plugin
*/
virtual const String& toString() const
{ return m_name; }
/**
* Get a pointer to a derived class given that class name
* @param name Name of the class we are asking for
* @return Pointer to the requested class or NULL if this object doesn't implement it
*/
virtual void* getObject(const String& name) const;
/**
* Initialize the plugin after it was loaded and registered.
*/
virtual void initialize() = 0;
/**
* Check if the module is actively used.
* @return True if the plugin is in use, false if should be ok to restart
*/
virtual bool isBusy() const
{ return false; }
/**
* Retrieve the name of the plugin
* @return The plugin's name as String
*/
inline const String& name() const
{ return m_name; }
/**
* Retrive the objects counter associated to this plugin
* @return Pointer to plugin's objects counter or NULL
*/
inline NamedCounter* objectsCounter() const
{ return m_counter; }
/**
* Check if the module is to be initialized early
* @return True if the module should be initialized before regular ones
*/
bool earlyInit() const
{ return m_early; }
private:
Plugin(); // no default constructor please
String m_name;
NamedCounter* m_counter;
bool m_early;
};
#if 0 /* for documentation generator */
/**
* Macro to create static instance of the plugin
* @param pclass Class of the plugin to create
*/
void INIT_PLUGIN(class pclass);
/**
* Macro to create the unloading function
* @param unloadNow True if asked to unload immediately, false if just checking
* @return True if the plugin can be unloaded, false if not
*/
bool UNLOAD_PLUGIN(bool unloadNow);
#endif
#define INIT_PLUGIN(pclass) static pclass __plugin
#ifdef DISABLE_UNLOAD
#define UNLOAD_PLUGIN(arg) static bool _unused_unload(bool arg)
#else
#ifdef _WINDOWS
#define UNLOAD_PLUGIN(arg) extern "C" __declspec(dllexport) bool _unload(bool arg)
#else
#define UNLOAD_PLUGIN(arg) extern "C" bool _unload(bool arg)
#endif
#endif
/**
* Base class for engine running stage checkers.
* Descendants may check specific conditions and decide to stop the engine.
* There should be only one (static) instance of an engine checker
* @short Engine checker interface
*/
class YATE_API EngineCheck
{
public:
/**
* Do-nothing destructor of base class
*/
virtual ~EngineCheck()
{ }
/**
* Check running conditions. This method is called by the engine in the initialize process
* @param cmds Optional list of strings containing extra command line parameters
* (not parsed by the engine)
* @return False to stop the program
*/
virtual bool check(const ObjList* cmds) = 0;
/**
* Set the current engine checker
* @param ptr The new engine checker. May be 0 to reset it
*/
static void setChecker(EngineCheck* ptr = 0);
};
/**
* Prototype for engine main loop callback
*/
typedef int (*EngineLoop)();
/**
* This class holds global information about the engine.
* Note: this is a singleton class.
*
* @short Engine globals
*/
class YATE_API Engine
{
friend class EnginePrivate;
friend class EngineCommand;
YNOCOPY(Engine); // no automatic copies please
public:
/**
* Running modes - run the engine as Console, Client or Server.
*/
enum RunMode {
Stopped = 0,
Console = 1,
Server = 2,
Client = 3,
ClientProxy = 4,
};
enum CallAccept {
Accept = 0,
Partial = 1,
Congestion = 2,
Reject = 3,
};
/**
* Plugin load and initialization modes.
* Default is LoadLate that initailizes the plugin after others.
* LoadEarly will move the plugin to the front of the init order.
* LoadFail causes the plugin to be unloaded.
*/
enum PluginMode {
LoadFail = 0,
LoadLate,
LoadEarly
};
/**
* Main entry point to be called directly from a wrapper program
* @param argc Argument count
* @param argv Argument array
* @param env Environment variables
* @param mode Mode the engine must run as - Console, Client or Server
* @param loop Callback function to the main thread's loop
* @param fail Fail and return after parsing command line arguments
* @return Program exit code
*/
static int main(int argc, const char** argv, const char** env,
RunMode mode = Console, EngineLoop loop = 0, bool fail = false);
/**
* Display the help information on console
* @param client Display help for client running mode
* @param errout Display on stderr intead of stdout
*/
static void help(bool client, bool errout = false);
/**
* Initialize the engine
* @return Error code, 0 for success
*/
int engineInit();
/**
* Do engine cleanup
* @return Error code, 0 for success
*/
int engineCleanup();
/**
* Run the engine.
* @return Error code, 0 for success
*/
int run();
/**
* Get a pointer to the unique instance.
* @return A pointer to the singleton instance of the engine
*/
static Engine* self();
/**
* Get the running mode of the engine
* @return Engine's run mode as enumerated value
*/
static RunMode mode()
{ return s_mode; }
/**
* Get call accept status
* @return Engine's call accept status as enumerated value
*/
inline static CallAccept accept() {
return (s_congestion && (s_accept < Congestion)) ? Congestion : s_accept;
}
/**
* Set call accept status
* @param ca New call accept status as enumerated value
*/
inline static void setAccept(CallAccept ca) {
s_accept = ca;
}
/**
* Get call accept states
* @return states Pointer to the call accept states TokenDict
*/
inline static const TokenDict* getCallAcceptStates() {
return s_callAccept;
}
/**
* Alter the congestion state counter.
* @param reason Reason to enter congested state, NULL to leave congestion
*/
static void setCongestion(const char* reason = 0);
/**
* Get the congestion state counter
* @return Zero if not congested else the number of congested components
*/
static unsigned int getCongestion()
{ return s_congestion; }
/**
* Check if the engine is running as telephony client
* @return True if the engine is running in client mode
*/
inline static bool clientMode()
{ return (s_mode == Client) || (s_mode == ClientProxy); }
/**
* Register or unregister a plugin to the engine.
* @param plugin A pointer to the plugin to (un)register
* @param reg True to register (default), false to unregister
* @return True on success, false on failure
*/
static bool Register(const Plugin* plugin, bool reg = true);
/**
* Get the server node name, should be unique in a cluster
* @return Node identifier string, defaults to host name
*/
inline static const String& nodeName()
{ return s_node; }
/**
* Get the application's shared directory path
* @return The base path for shared files and directories
*/
inline static const String& sharedPath()
{ return s_shrpath; }
/**
* Get the filename for a specific configuration
* @param name Name of the configuration requested
* @param user True to build a user settings path
* @return A full path configuration file name
*/
static String configFile(const char* name, bool user = false);
/**
* Get the system or user configuration directory path
* @param user True to get the user settings path
* @return The directory path for system or user configuration files
*/
static const String& configPath(bool user = false);
/**
* Get the configuration file suffix
* @return The suffix for configuration files
*/
inline static const String& configSuffix()
{ return s_cfgsuffix; }
/**
* The module loading path
*/
inline static const String& modulePath()
{ return s_modpath; }
/**
* Add a relative extra module loading path. The list is empty by default
* but can be filled by a main program before calling @ref main()
* @param path Relative path to extra modules to be loaded
*/
static void extraPath(const String& path);
/**
* Set the per user application data path. This method must be called
* by a main program before calling @ref main() or @ref help()
* Path separators are not allowed. The default is taken from CFG_DIR.
* @param path Single relative path component to write user specific data
*/
static void userPath(const String& path);
/**
* Get the module filename suffix
* @return The suffix for module files
*/
inline static const String& moduleSuffix()
{ return s_modsuffix; }
/**
* Get the canonical path element separator for the operating system
* @return The operating system specific path element separator
*/
static const char* pathSeparator();
/**
* The global configuration of the engine.
* You must use this resource with caution.
* Note that sections [general], [modules], [preload] and [postload] are
* reserved by the engine. Also [telephony] is reserved by the drivers.
* @return A reference to the read-only engine configuration
*/
static const Configuration& config();
/**
* Get a - supposedly unique - instance ID
* @return Unique ID of the current running instance
*/
static unsigned int runId();
/**
* Get the engine parameters specific to this run.
* @return A reference to the list of run specific parameters
*/
inline static const NamedList& runParams()
{ return s_params; }
/**
* Reinitialize the plugins
*/
static void init();
/**
* Reinitialize one plugin
* @param name Name of the plugin to initialize, emplty, "*" or "all" to initialize all
* @return True if plugin(s) were reinitialized
*/
static bool init(const String& name);
/**
* Stop the engine and the entire program
* @param code Return code of the program
*/
static void halt(unsigned int code);
/**
* Stop and restart the engine and the entire program
* @param code Return code of the program
* @param gracefull Attempt to wait until no plugin is busy
* @return True if restart was initiated, false if exiting or no supervisor
*/
static bool restart(unsigned int code, bool gracefull = false);
/**
* Check if the engine was started
* @return True if the engine has gone through the start phase
*/
static bool started()
{ return s_started; }
/**
* Check if the engine is currently exiting
* @return True if exiting, false in normal operation
*/
static bool exiting()
{ return (s_haltcode != -1); }
/**
* Installs a handler in the dispatcher.
* @param handler A pointer to the handler to install
* @return True on success, false on failure
*/
static bool install(MessageHandler* handler);
/**
* Uninstalls a handler drom the dispatcher.
* @param handler A pointer to the handler to uninstall
* @return True on success, false on failure
*/
static bool uninstall(MessageHandler* handler);
/**
* Enqueue a message in the message queue for asynchronous dispatching
* @param msg The message to enqueue, will be destroyed after dispatching
* @param skipHooks True to append the message directly into the main queue
* @return True if enqueued, false on error (already queued)
*/
static bool enqueue(Message* msg, bool skipHooks = false);
/**
* Convenience function.
* Enqueue a new parameterless message in the message queue
* @param name Name of the parameterless message to put in queue
* @param broadcast Broadcast flag, true if handling the mesage must not stop it
* @return True if enqueued, false on error (already queued)
*/
inline static bool enqueue(const char* name, bool broadcast = false)
{ return name && *name && enqueue(new Message(name,0,broadcast)); }
/**
* Synchronously dispatch a message to the registered handlers
* @param msg Pointer to the message to dispatch
* @return True if one handler accepted it, false if all ignored
*/
static bool dispatch(Message* msg);
/**
* Synchronously dispatch a message to the registered handlers
* @param msg The message to dispatch
* @return True if one handler accepted it, false if all ignored
*/
static bool dispatch(Message& msg);
/**
* Convenience function.
* Dispatch a parameterless message to the registered handlers
* @param name The name of the message to create and dispatch
* @param broadcast Broadcast flag, true if handling the mesage must not stop it
* @return True if one handler accepted it, false if all ignored
*/
static bool dispatch(const char* name, bool broadcast = false);
/**
* Install or remove a hook to catch messages after being dispatched
* @param hook Pointer to a post-dispatching message hook
* @param remove Set to True to remove the hook instead of adding
*/
inline void setHook(MessagePostHook* hook, bool remove = false)
{ m_dispatcher.setHook(hook,remove); }
/**
* Retrieve the tracker parameter name
* @return Name of the parameter used to track message dispatching
*/
inline static const String& trackParam()
{ return s_self ? s_self->m_dispatcher.trackParam() : String::empty(); }
/**
* Appends a new message hook to the hooks list.
* @param hook The message hook to append.
* @return True if the message hook was successfully appended to the hooks list
*/
static bool installHook(MessageHook* hook);
/**
* Remove a message hook from the hooks list.
* @param hook The hook to remove.
*/
static void uninstallHook(MessageHook* hook);
/**
* Get a count of plugins that are actively in use
* @return Count of plugins in use
*/
int usedPlugins();
/**
* Get the number of messages waiting in the queue
* @return Count of messages in the queue
*/
inline unsigned int messageCount()
{ return m_dispatcher.messageCount(); }
/**
* Get the number of handlers in the dispatcher
* @return Count of handlers
*/
inline unsigned int handlerCount()
{ return m_dispatcher.handlerCount(); }
/**
* Get the number of post-handling hooks in the dispatcher
* @return Count of hooks
*/
inline unsigned int postHookCount()
{ return m_dispatcher.postHookCount(); }
/**
* Get the rate of dispatched messages per second
* @return Number of messages dispatched in the last second
*/
inline unsigned int messageRate() const
{ return m_messageRate; }
/**
* Get the maximum rate of dispatched messages per second
* @return Maximum number of messages dispatched per second
*/
inline unsigned int messageMaxRate() const
{ return m_maxMsgRate; }
/**
* Get the average dequeued message age in milliseconds or microseconds
* @param usec True to return microseconds instead of milliseconds
* @return Average age of dequeued messages
*/
unsigned int messageAge(bool usec = false) const
{ return (unsigned int)m_dispatcher.messageAge(usec); }
/**
* Retrieve dispatcher's statistics counters
* @param enqueued Returns count of enqueued messages
* @param dequeued Returns count of dequeued messages
* @param dispatched Returns count of all dispatched messages, including dequeued ones
* @param queueMax Returns queued high watermark
*/
inline void getStats(u_int64_t& enqueued, u_int64_t& dequeued, u_int64_t& dispatched, u_int64_t& queueMax)
{ m_dispatcher.getStats(enqueued,dequeued,dispatched,queueMax); }
/**
* Reset the high water mark of the stat counters
*/
inline void resetMax()
{ m_maxMsgRate = m_messageRate; m_dispatcher.m_queuedMax = m_dispatcher.messageCount(); }
/**
* Check if a plugin is currently loaded
* @param name Name of the plugin to check
* @return True if the named plugin is loaded
*/
inline bool pluginLoaded(const String& name) const
{ return !!m_libs[name]; }
/**
* Loads the plugins from an extra plugins directory or just an extra plugin
* @param relPath Path to the extra directory, relative to the main modules
* @return True if the plugin was loaded or the directory could at least be opened
*/
bool loadPluginDir(const String& relPath);
/**
* Set the load and init mode of the currently loading @ref Plugin
* @param mode Load and init mode, default LoadLate
*/
static void pluginMode(PluginMode mode);
/**
* Retrive the list of captured events of a specific type
* @param type Type of captured events, an empty name returns engine events
* @return List containing captured events of specified type, NULL if not found
*/
static const ObjList* events(const String& type);
/**
* Clear the list of captured events of a specific type
* @param type Type of captured events, an empty name clear engine events
*/
static void clearEvents(const String& type);
/**
* Access the engine's shared variables
* @return Reference to the static variables shared between modules
*/
static SharedVars& sharedVars();
/**
* Append command line arguments form current config.
* The following parameters are added: -Dads, -v, -q, Debugger timestamp.
* This method should be used when starting another libyate based application
* @param line Destination string
*/
static void buildCmdLine(String& line);
/**
* Initialize library from command line arguments.
* Enable debugger output.
* This method should be used in initialization stage of libyate based applications
* @param line Command line arguments string
* @param output Optional string to be filled with invalid argument errors
* or any output to be displyed later
*/
static void initLibrary(const String& line, String* output = 0);
/**
* Cleanup library. Set late abort, kill all threads.
* This method should be used in cleanup stage of libyate based applications
* @return Halt code
*/
static int cleanupLibrary();
/**
* Retrieve common Engine dispatcher
* @return MessageDispatcher pointer, NULL if not set
*/
static inline MessageDispatcher* dispatcher()
{ return s_self ? &(s_self->m_dispatcher) : 0; }
protected:
/**
* Destroys the engine and everything. You must not call it directly,
* @ref run() will do it for you.
*/
~Engine();
/**
* Loads one plugin from a shared object file
* @param file Name of the plugin file to load
* @param local Attempt to keep symbols local if supported by the system
* @param nounload Never unload the module from memory, finalize if possible
* @return True if success, false on failure
*/
bool loadPlugin(const char* file, bool local = false, bool nounload = false);
/**
* Loads the plugins from the plugins directory
*/
void loadPlugins();
/**
* Initialize all registered plugins
*/
void initPlugins();
private:
Engine();
void internalStatisticsStart();
void tryPluginFile(const String& name, const String& path, bool defload);
ObjList m_libs;
MessageDispatcher m_dispatcher;
uint64_t m_dispatchedLast;
unsigned int m_messageRate;
unsigned int m_maxMsgRate;
bool m_rateCongested;
bool m_queueCongested;
bool m_ageCongested;
static Engine* s_self;
static String s_node;
static String s_shrpath;
static String s_cfgsuffix;
static String s_modpath;
static String s_modsuffix;
static ObjList s_extramod;
static NamedList s_params;
static int s_haltcode;
static RunMode s_mode;
static bool s_started;
static unsigned int s_congestion;
static CallAccept s_accept;
static const TokenDict s_callAccept[];
};
}; // namespace TelEngine
#endif /* __YATENGINE_H */
/* vi: set ts=8 sw=4 sts=4 noet: */