.. _60-events:
Events
======
Four types of event are supported, at least for now. On Linux,
`DBus `__ is the mechanism that handles the
majority of the communication between the system and the applications, via a well described
subscription mechanism. On Windows, on the other side,
`WMI `__ is a WBEM
based interface that allows for subscription to system events in a streamlined way, thus
it has been implemented specifically for the Windows platform. WMI, of course, is not
available on Linux, while DBus is optionally available on Windows. Other environments may
expose interfaces that are not directly supported by **whenever**.
One very specific case, which is also particularly useful, is the *notification* of changes
in the filesystem for watched entities (files or directories), which is also implemented in
**whenever** as one of the possible events that can fire conditions.
The last kind of events supported by **whenever** relies on its *stdin* based
:ref:`command interface <70-intcli-input-commands>`. These events are directly raised by
issuing a ``trigger`` command followed by the event name: a wrapper, even possibly a platform
specific one, can therefore notify **whenever** that a specific event took place, or that the
user explicitly required to trigger it from the available user interface. This type of event is
the simplest one to define, as it has no criteria to be specified.
Note that if an event arises more that once within the tick interval, it is automatically
*debounced* and a single occurrence is counted.
All *event* definition sections must start with the TOML ``[[event]]`` header.
An optional entry, namely ``tags``, is accepted in item configuration: this entry is ignored by
**whenever** itself, however it is checked for correctness at startup and the configuration is
refused if not set to an array (of strings) or a table.
The associated conditions must exist, otherwise an error is raised and **whenever** aborts.
.. _60-events-fschange:
Filesystem changes
------------------
This type of event arises when there is a modification in the filesystems, regarding one of more
monitored files and/or directories. **whenever** allows to monitor a list of items for each defined
event of this type, and to associate an *event* based condition to the event itself. A sample
configuration follows:
.. code-block:: toml
[[event]]
name = "FilesystemChangeEventName"
type = "fschange"
condition = "AssignedConditionName"
# optional parameters (if omitted, defaults are used)
watch = [
"/path/to/resource",
"/another/path/to/file.txt",
]
recursive = false
poll_seconds = 2
The configuration entries are:
.. list-table::
:header-rows: 1
* - Entry
- Default
- Description
* - ``name``
- N/A
- the unique name of the event (mandatory)
* - ``type``
- N/A
- must be set to ``"fschange"`` (mandatory)
* - ``condition``
- N/A
- the name of the associated *event* based condition (mandatory)
* - ``watch``
- (empty)
- a list of items to be monitored: possibly expressed with their full path
* - ``recursive``
- *false*
- if *true*, listed directories will be monitored recursively
* - ``poll_seconds``
- 2
- generally not used, can be needed on systems where the notification service is unavailable
.. _60-events-dbus:
DBus signals (optional)
-----------------------
DBus provides signals that can be subscribed by applications, to receive information about various
aspects of the system status in an asynchronous way. **whenever** offers the possibility to
subscribe to these signals, so that when the *return parameters* match the provided constraints,
then the event occurs and the associated condition is fired.
.. note::
This type of item is only available when the ``dbus`` feature is enabled.
Subscription is performed by providing a *watch expression* in the same form that is used by the
`dbus-monitor ` utility. The criteria
that the *signal parameters* must meet in order for the event to arise, are specified using the
same format that is used for *return message parameter* checks in
:ref:`DBus method based conditions <50-conditions-dbus>`.
A sample configuration section follows:
.. code-block:: toml
[[event]]
name = "DbusMessageEventName"
type = "dbus" # mandatory value
bus = ":session" # either ":session" or ":system"
condition = "AssignedConditionName"
rule = """\
type='signal',\
sender='org.freedesktop.DBus',\
interface='org.freedesktop.DBus',\
member='NameOwnerChanged',\
arg0='org.freedesktop.zbus.MatchRuleStreamTest42'\
"""
# optional parameters (if omitted, defaults are used)
parameter_check_all = false
parameter_check = [
{ index = 0, operator = "eq", value = false },
{ index = [1, 5], operator = "neq", operator = "forbidden" },
{ index = [2, "mapidx", 5], operator = "match", value = "^[A-Z][a-zA-Z0-9_]*$" },
]
and the details of the configuration entries are described in the table below:
.. list-table::
:header-rows: 1
* - Entry
- Default
- Description
* - ``name``
- N/A
- the unique name of the event (mandatory)
* - ``type``
- N/A
- must be set to ``"dbus"`` (mandatory)
* - ``condition``
- N/A
- the name of the associated *event* based condition (mandatory)
* - ``bus``
- N/A
- the bus on which to listen for events: must be either ``":system"`` or ``":session"``,
including the starting colon (mandatory)
* - ``parameter_check_all``
- *false*
- if *true*, all the provided criteria will have to be satisfied for the event to be
fired, otherwise one is enough
* - ``parameter_check``
- (empty)
- a list of maps consisting of three fields each, each of which is a check to be performed
on return parameters
The considerations about indexes in return parameters are the same that have been seen for
:ref:`DBus message based conditions <50-conditions-dbus>`. It is worth to remind that any errors
that may arise during checks will cause the check itself to yield *false*.
If no parameter checks are provided, the event arises simply when the signal is caught.
.. _60-events-wmi:
WMI (optional, Windows only)
----------------------------
On Windows, **whenever** can subscribe to *WMI* events using event specific `WML queries`_.
This kind of query allows for an extremely precise determination of every aspect of the event
that has to be caught, including the possibility to specify any criteria regarding the payload
of an event in order to consider it verified. Thus **whenever** leaves to the *query* part of a
*WMI* event definition the task of filtering the specific event for which it enables a listener.
.. _WML queries: https://learn.microsoft.com/en-us/windows/win32/wmisdk/receiving-event-notifications
.. note::
This type of item is only available when the ``wmi`` feature is enabled.
As a result, the configuration of a *WMI* based event is much simpler than the one of *DBus signal*
based ones, by only having to specify a mandatory ``query`` entry, whose syntax and semantic is
similar to the one of the queries used in :ref:`WMI Query <50-conditions-dbus>` based conditions,
but has to be expressly built for events.
An example of *WMI* based event configuration follows:
.. code-block:: toml
[[event]]
name = "WMIEventName"
type = "wmi" # mandatory value
condition = "AssignedConditionName"
query = """
SELECT * FROM __InstanceModificationEvent
WHERE TargetInstance ISA "Win32_LogicalDisk"
AND TargetInstance.FreeSpace < 5000000000
"""
which will occur every time the remaining space of a logical disk goes roughly under 5GB. The
details of the configuration entries are described in the table below:
.. list-table::
:header-rows: 1
* - Entry
- Default
- Description
* - ``name``
- N/A
- the unique name of the event (mandatory)
* - ``type``
- N/A
- must be set to ``"dbus"`` (mandatory)
* - ``condition``
- N/A
- the name of the associated *event* based condition (mandatory)
* - ``query``
- N/A
- the *WQL* query used specify what criteria must be satisfied for the event to occur
As with DBus *match rules*, **whenever** does not do any parsing or check on the provided query:
an incorrect query will only cause the event registration to fail and log an error message, at
least in the *debug* log level.
Every event returned by the system matches the criteria specified in the *query*, and will cause
the assigned condition to fire.
.. warning::
Some antimalware tools might detect event subscriptions as suspicious.
.. _60-events-cli:
Command line
------------
As said above, this type of event has no other parameters than the name, the type identifier, and
the associated condition. All parameters are mandatory. The event is raised when a wrapper (or
the user) passes a ``trigger`` :ref:`command <70-intcli-input-commands>` to **whenever** through
the *stdin* stream of an active session.
A sample configuration section follows:
.. code-block:: toml
name = "ManuallyTriggeredEvent"
type = "cli" # mandatory value
condition = "AssignedConditionName"
and the details of the configuration entries are described in the table below:
.. list-table::
:header-rows: 1
* - Entry
- Default
- Description
* - ``name``
- N/A
- the unique name of the event (mandatory)
* - ``type``
- N/A
- must be set to ``"cli"`` (mandatory)
* - ``condition``
- N/A
- the name of the associated *event* based condition (mandatory)
No listening service is installed, so the impact on resource consumption and performance is almost
unnoticeable.