The whenever Documentation
whenever is a lightweight task scheduler capable of executing tasks (in particular, OS commands and Lua scripts) according to specific conditions. Conditions are of various types: depending on time (both intervals or specific more-or-less defined instants), execution of OS commands or Lua scripts, changes in specific files and directories, session inactivity, DBus and WMI signals or property checks.[1] The scheduler intends to be as lightweight as possible in terms of used computational resources, and to run at a low priority level.
Configuration is provided to the scheduler via a TOML file, which must contain all definitions for conditions and associated tasks, as well as events that the scheduler should listen to.
Although a command line application, it is designed for desktops – therefore it should be executed via a controlling GUI wrapper.
Introduction
The purpose of whenever is to provide the user, possibly without administrative credentials, with the ability to define conditions that do not only depend on time, but also on particular states of the session, result of commands run in a shell, execution of Lua scripts, or other events that may occur while the system is being used. This scheduler is a terminal (or console, on Windows) application, however it is meant to run in the background without interaction with the user. The application is able to produce detailed logs, so that the user can review what the application is doing or has done.
The whenever scheduler might overlap, to some extent, with the standard cron scheduler on Unix, and with the Task Scheduler on Windows. However this tool tries to be more flexible – although possibly less precise than cron – and to function as an alternative to more complex solutions that could be implemented using the system-provided schedulers. The whenever approach is to perform certain tasks after a condition is met, in a relaxed fashion: this means that the tasks might not be performed exactly in the instant that marks the condition verification, but after such verification instead. Thus this tool is not intended as a replacement for the utilities provided by the operating system: it aims at representing an easy solution for those who need to automate some actions depending on other situations or events that may occur.
Also, whenever aims at being cross-platform: most features are available on all supported operating systems, with the exception of
DBus support, which is available on both Linux and Windows, but is of little or no use on the Windows platform, and is therefore not included in the prebuilt binaries, and
WMI support, which is only available on Windows.
whenever tries to be conservative in terms of resource cosumption (especially CPU and RAM), and, since it does not interact with the user normally, it should be able to run at low priority. Therefore, whenever does not implement a GUI by itself: on the contrary, it offers a simple stdin-based interface that is mostly aimed at interacting with an independent wrapper.
The actions to be performed are loaded every time at startup by means of a single configuration file that, as many modern tools do, uses the well known TOML format.[2]
A very lightweight cross-platform wrapper, namely whenever_tray, is available for both Linux and Windows. Developed in C++ and using the WxWidgets GUI library, it has been designed to implement the bare minimum of functionality and to just show an icon in the system tray area, from which it is possible to stop the scheduler, and to pause/resume the condition checks and therefore the execution of tasks that would derive from them. The minimalistic wrapper also hides the console window on Windows environments. Due to the use of stdin/stdout for communication, it is possible to build more elaborate wrappers in any language that supports the possibility to spawn a process and control its I/O, at the expense of a larger resource occupation but possibly without drawbacks in terms of performance, as the scheduler runs in a separate process anyway. The original Python based When application had an occupation in RAM of about 70MB on Ubuntu Linux using a not-too-populated configuration file, and could noticeably use the CPU.
This version, written in the Rust programming language, uses around 2MB of RAM on Windows[3] with a reasonable configuration file that uses tasks, conditions, and events supported on the platform. Although small in footprint, whenever is fully multithreaded, condition checks have no influence on each other and, when needed, may run concurrently. Consequential task execution also takes place with full parallelism – with the exception of those tasks that, per configuration, are set to run sequentially.
The former version of When itself has currently been converted to a frontend wrapper for whenever: see the specific repository and documentation for details.
Features
whenever can perform the following types of task:
Running OS executables, either binaries or scripts, checking their exit code or output (both on stdout and stderr) for expected or undesired results
Running Lua scripts, using an embedded interpreter, with the possibility of checking the contents of Lua variables for expected outcomes
Running internal input commands, to modify the internal scheduler status at the verification of specific conditions
as the consequence of the verification of a condition. The concepts of tasks and conditions are inherited from the Python based When scheduler.
The supported types of condition are the following:
Interval based: the periodic conditions are verified after a certain time interval has passed since whenever has started, and may be verified again after the same amount of time if the condition is set to be recurring
Time based: one or more instants in time can be provided for the condition to be verified
Idle user session: this type of condition is verified after the session has been idle for the specified amount of time
Command execution: an available executable (be it a script, a batch file on Windows, a binary) is run, its exit code or output is checked and, when an expected outcome is found, the condition is considered verified - or failed on an explicitly undesired outcome
Lua script execution: a Lua script is run using the embedded interpreter, and if the contents of one or more variables meet the specified expectations the condition is considered verified
DBus inspection (optional) <50-conditions-dbus>: a DBus method is called and the result is checked against some criteria provided in the configuration file
WMI query (optional, Windows only): a WMI query is executed and the result is checked against some criteria provided in the configuration file
Event based: are verified when a certain event occurs that fires the condition.
The events that can fire event based conditions are:
Filesystem changes, that is, changes in files and/or directories that are set to be monitored
DBus signals (optional), that may be filtered for an expected payload
WMI events (optional, Windows only), subscribed via specific WQL queries
Command line, that are manually triggered by writing to whenever standard input.
Note that DBus events and conditions are also supported on Windows, being one of the DBus target
platforms, and enabled by default. WMI events and conditions, on the contrary, are only supported
on Windows platforms. Both DBus and WMI support can be disabled on build, by respectively
removing dbus
and/or wmi
from the default features in the Cargo.toml file, or by building
the application with the --no-default-features
command line flag (in this case, other desired
features have to be specifically enabled using the --features
option). whenever can provide
the list of the available optional features by invoking whenever --options
from the command line.
All of the above listed items are fully configurable via a TOML configuration file, that must be specified as the only mandatory argument on the command line. The syntax of the configuration file is described in the following sections.
Every type of check is performed periodically, even the ones involving event based conditions[4]: the periodic time interval at which the conditions are checked is referred here as tick, and the tick interval can be specified in the configuration file – defaulting at 5 seconds. Note that, since performing all checks in the same instant at every tick could cause usage peaks in terms of computational resources, there is the option to attempt to randomly distribute some of the checks within the tick interval, by explicitly specifying this behavior in the configuration file.
The configuration can be also modified while the scheduler application is running, using a specific command, in a dynamic fashion that does not reset the items whose configuration is unchanged.
Configuration
The configuration file is strictly based on the current TOML specification: therefore it can be implemented by hand, or automatically written (for example, by a GUI based utility) using a library capable of writing well-formed TOML files. This section describes the exact format of this file, in all of its components.
The configuration has a global section for parameters that affect the scheduler behavior globally, and sections for the various configuration items described above, that is
In configuration files generated by tools such as the current version of When, a special
[[tags]]
section may appear in several places, which generally contains configuration specific
to the tool itself.
Note
If whenever has been compiled without support for one or more optional features, entries for items depending on that feature are considered configuration errors and cause whenever to exit.