Tasks
Tasks are handled first in this document, because conditions must mandatorily specify the tasks to be executed upon verification. There are three types of task, each of which is described in detail in its specific subsection.
Tasks are defined via a dedicated table, which means that every task definition must start with the
TOML [[task]]
section header.
Task names are mandatory, and must be provided as alphanumeric strings (may include underscores),
beginning with a letter. The task type must be one of "command"
, "lua"
, or
"internal"
according to what has to be configured, and any other value is considered a
configuration error. There is another optional entry, namely tags
, that 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.
Command
Command based tasks actually execute commands at the OS level: they might have a positive as
well as a negative outcome, depending on user-provided criteria. As said above, these criteria
may not just depend on the exit code of the executed command, but also on checks performed on its
output taking either the standard output or the standard error channels into account. By default
no check is performed, but the user can choose, for instance, to consider a zero exit code as a
successful execution (quite common for OS commands). It is possible to consider another exit code
as successful, or the zero exit code as a failure (for example, if a file should not be found,
performing ls
on it would have the zero exit code as an undesirable outcome). Also, a
particular substring can be sought in the standard output or standard error streams both as
expected or as unexpected. The two streams can be matched against a provided regular expression
if just seeking a certain substring is not fine-grained enough. Both substrings and regular
expressions can be respectively sought or matched either case-sensitively or case-insensitively.
A sample configuration for a command based task is the following:
[[task]]
name = "CommandTaskName"
type = "command"
startup_path = "/some/startup/directory" # must exist
command = "executable_name"
command_arguments = [
"arg1",
"arg2",
]
# optional parameters (if omitted, defaults are used)
match_exact = false
match_regular_expression = false
success_stdout = "expected"
success_stderr = "expected_error"
success_status = 0
failure_stdout = "unexpected"
failure_stderr = "unexpected_error"
failure_status = 2
timeout_seconds = 60
case_sensitive = false
include_environment = false
set_environment_variables = false
environment_variables = { VARNAME1 = "value1", VARNAME2 = "value2" }
and the following table provides a detailed description of the entries:
Entry |
Default |
Description |
---|---|---|
|
N/A |
the unique name of the task (mandatory) |
|
N/A |
must be set to |
|
N/A |
the directory in which the command is started |
|
N/A |
path to the executable (mandatory; if the path is omitted, the executable should be found in the search PATH) |
|
N/A |
arguments to pass to the executable: can be an empty list, |
|
false |
if true, the entire output is matched instead of searching for a substring |
|
false |
if true, the match strings are considered regular expressions instead of substrings |
|
false |
if true, substring search or match and regular expressions match is performed case-sensitively |
|
(empty) |
if set, the number of seconds to wait before the command is terminated (with unsuccessful outcome) |
|
(empty) |
if set, when the execution ends with the provided exit code the task is considered successful |
|
(empty) |
if set, when the execution ends with the provided exit code the task is considered failed |
|
(empty) |
the substring or RE to be found or matched on stdout to consider the task successful |
|
(empty) |
the substring or RE to be found or matched on stderr to consider the task successful |
|
(empty) |
the substring or RE to be found or matched on stdout to consider the task failed |
|
(empty) |
the substring or RE to be found or matched on stderr to consider the task failed |
|
true |
if true, the command is executed in the same environment in which whenever was started |
|
true |
if true, whenever sets environment variables reporting the names of the task and the condition |
|
|
extra variables that might have to be set in the environment in which the provided command runs |
The priority used by whenever to determine success or failure in the task is the one in which
the related parameters appear in the above table: first exit codes are checked, then both stdout
and stderr are checked for substrings or regular expressions that identify success, and finally
the same check is performed on values that indicate a failure. Note that the command execution is
not considered successful with a zero exit code by default, nor a failure on a nonzero exit code:
both assumptions have to be explicitly configured by setting either success_status
or
failure_status
.
If a command is known to have the possibility to hang, a timeout can be configured by specifying the maximum number of seconds to wait for the process to exit: after this amount of time the process is terminated and fails.
If set_environment_variables
is true, whenever sets the following environment variables:
WHENEVER_TASK
to the unique name of the taskWHENEVER_CONDITION
to the unique name of the condition that triggered the task
for scripts or other executables that might be aware of whenever.
Tip
Many times the success or failure status can be disregarded, especially in situations where a condition causes a single task to be executed. When an execution flow must be respected, providing the correct parameters to identify success or failure becomes substantial, as well as in cases when the associated condition is set to retry the task (or a sequence where it is contained) until it succeeds.
Lua script
Tasks based on Lua scripts might be useful when an action has to be performed that requires a non-trivial sequence of operations, but for which it would be excessive to write a specific script to be run as a command. The script to be run is embedded directly in the configuration file – TOML helps in this sense, by allowing multiline strings by specification.
Lua based tasks can be considered more lightweight than command tasks, as the interpreter is embedded in whenever. Also, the embedded Lua interpreter is enriched with library functions that allow to write to the whenever log, at all logging levels (error, warn, info, debug, trace). The library functions are the following:
log.error
log.warn
log.info
log.debug
log.trace
and take a single string as their argument.
The configuration of Lua based tasks has the following form:
[[task]]
name = "LuaTaskName"
type = "lua"
script = '''
log.info("hello from Lua");
result = 10;
'''
# optional parameters (if omitted, defaults are used)
expect_all = false
expected_results = { result = 10 }
and the following table provides a detailed description of the entries:
Entry |
Default |
Description |
---|---|---|
|
N/A |
the unique name of the task (mandatory) |
|
N/A |
must be set to |
|
N/A |
the Lua code that has to be executed by the internal interpreter (mandatory) |
|
false |
if true, all the expected results have to be matched to consider the task successful, otherwise at least one |
|
|
a dictionary of variable names and their expected values to be checked after execution |
Note that triple single quotes have been used to embed the script: this allows to use escapes
and quotes in the script itself. Although the script should be embedded in the configuration file,
it is possible to execute external scripts via dofile("/path/to/script.lua")
or by using the
require
function. While a successful execution is always determined by matching the provided
criteria, an error in the script is always considered a failure.
From the embedded Lua interpreter there are two values set that can be accessed:
whenever_task
is the name of the task that executes the scriptwhenever_condition
is the name of the condition that triggered the task.
which might be useful if the scripts are aware of being run within whenever.
Internal input command
This type of task is useful in case the ability of whenever to unattendendly do something is needed to control the running instance of whenever itself: it is possible in fact to instruct the scheduler to execute one of the commands that have been implemented to be used by wrapper applications. This means that the scheduler:
can automatically reset one or more conditions
can pause itself or shut itself down: in both cases there is no automatic way back
can reload its configuration file
and so on upon verification of a condition.
Note
No security concern is raised here, as whenever is designed with the intention to run without administration rights; obviously every kind of automation tool, which performs unattended operations, might execute malicious actions under the hood, however the absence of particular privileges when running and the ability to read the configuration file without the need of specific tools, allow for complete control on what whenever does.
The configuration for such a type of task is simple, as it only requires to set the command
parameter in addition to the name and type. An example internal command based task is the
following:
[[task]]
name = "InternalTaskName"
type = "internal"
command = "reset_conditions Cond1 Cond2"
which resets the conditions named Cond1
and Cond2
, if they exist. A detailed description
of the parameter entries follows:
Entry |
Default |
Description |
---|---|---|
|
N/A |
the unique name of the task (mandatory) |
|
N/A |
must be set to |
|
N/A |
the internal command to be run, as a single string that includes its parameters |
As mentioned above, a comprehensivew list of possible internal commands can be found in the appropriate section.
This type of item is mostly intended as a way to automate part of the behavior of whenever during a session on behalf of a wrapper, that might expose part of the configuration implemented as a combination of internally managed conditions and specific tasks (even of this type) as single and simpler configuration element: an example could be the use of the org.freedesktop.UPower interface in DBus to catch a system resume event in order to reset all the conditions.
Warning
The provided command will not be checked upon configuration, it will fail instead causing a warning to be logged in case it is invalid or malformed.