NAME
scw - scheduled command wrapper
SYNOPSIS
scw [ --config FILE ] [--set SETTING=VALUE]... ACTION [OPTION]...
scw run [ --force ] ITEM [-- COMMAND]
scw enable|disable ITEM
scw status ITEM
scw list [--enabled|--disabled]
scw update
scw
-h|--help
scw -V|--version
DESCRIPTION
Wrap a scheduled command in a framework which adds concurrency locking, prerequisites, dependency checks, conflict avoidance, randomised startup delays, flexible logging, and monitoring metrics.
Each distinct scheduled command is referred to as an “item”, and can have its own configuration for each of these features:
- Concurrency locking
-
Prevents an item from being run more than once at the same time, for example if it's scheduled to run every 2 minutes and occasionally takes longer than that to complete.
- Prerequisites
-
Prevents an item from running if some prior condition is not met, for example checking whether this is the active node of a failover cluster, or whether some crucial underpinning service is running.
- Dependency checks
-
Ensures that an item will run only if some other item has succeeded before it - with the possibility of waiting a short while for the dependency to finish rather than giving up straight away. Useful when linked items need to be scheduled separately at particular times but the later one can only run if the earlier one has succeeded. For example, a data load batch may have to run at some time in the early morning, and a subsequent data processing batch, which for business reasons has to run after a particular time of day, can only run if the early morning data load succeeded.
- Conflict avoidance
-
Prevents an item from running if some other item is still running - with the possibility of waiting a short while for the conflicting item to finish rather than giving up straight away.
- Randomised startup delays
-
Avoids resource overconsumption when the same item is scheduled to run on multiple systems.
- Flexible logging
-
Item output can be sent to any combination of files, syslog, email, or HTTP, with or without timestamps. The standard output and standard error of items can be combined or separated, and a special status stream is also made available so that significant events (such as starting each step of a multi-step process) can be recorded separately.
- Metrics
-
A collection of informational files is maintained for each item. Any monitoring agent can read these files and raise alerts based on their contents. An item list file (a JSON array of item descriptions) is automatically generated so that a system such as Zabbix can find and monitor all items without needing an operator to make adjustments when items are added or removed.
All of these features are optional.
In its simplest form, scw can be invoked from existing scheduler entries by using the “run” action, so for example this crontab(5) entry would change as follows:
# Original entry 0 * * * * /some/command --option ARGUMENT # Replacement 0 * * * * scw run mycommand -- /some/command --option ARGUMENT
In this example, the scheduled command will be known to scw as the “mycommand” item, and all of the logs and metrics produced will use that name.
To make use of the full range of features, place scheduled commands or definition files into the item definition directory, and call “scw update” to generate the system crontab and the item list file.
ACTIONS
- run ITEM
-
Start running the item named ITEM, applying any prerequisite checks, dependency and conflict checks, startup delays, and concurrency locks, after checking whether the item has been disabled (unless the “--force” option was passed, in which case it will be run as if it was enabled). If there is no command defined for ITEM, all arguments after “--” will be passed to “sh -c”. If standard input, output, and error are all connected to a terminal, any configured randomised startup delay will be skipped, and output will be written to the terminal as well as to the usual logging destinations.
- enable ITEM
-
Enable the item named ITEM so that it runs as scheduled.
- disable ITEM
-
Disable the item named ITEM so that it will not run as scheduled, unless run with the “--force” option.
- status ITEM
-
Show the current status of the item named ITEM. This only operates on items defined in the item definition directory.
- list
-
List all defined items, or items that are defined and enabled (with the “--enabled” option), or items that are defined and disabled (with “--disabled”).
- update
-
Update the system crontab and the item list file from the items defined in the item definition directories (see the FILES section).
OPTIONS
With no action:
- -h, --help
-
Print a usage message on standard output and exit successfully.
- -V, --version
-
Print version information on standard output and exit successfully.
With any action:
- -c, --config FILE
-
Read configuration from FILE instead of the system-wide default location.
- -s, --set SETTING=VALUE
-
Set the item or configuration setting SETTING to VALUE (see the CONFIGURATION section).
With the “run” action:
- -f, --force
-
Run the item regardless of whether it has been marked as disabled.
With the “list” action:
- -e, --enabled
-
Only list items which are currently enabled.
- -d, --disabled
-
Only list items which are currently disabled.
ITEM DEFINITIONS
The settings for an item are defined in a file named ITEM.cf under the user's item definition directory, by default /etc/scw/items/USER/ (see the FILES section).
When an item is defined in this way, its Command setting should be provided, to state what to run.
Alternatively, shell or Perl scripts can be placed directly into the directory, named ITEM.sh or ITEM.pl, so long as their first comment block at the top of the file contains the item settings prefixed with “scw”, like this:
#!/bin/sh # # Perform action ABC. # # scw Description = Do ABC # scw Schedule = 0 2 * * Mon # scw MaxRunTime = 30 minutes # scw SuccessInterval = 1 day 30 minutes #
This mechanism allows other packages to drop their own scheduled commands directly into this framework, and so long as they run “scw update” after installation, the items will be scheduled and configured correctly.
If an item has a script as well as a “.cf” file, the “.cf” settings are applied first, and the Command is implied.
Defining items in this way allows software developers to include scheduling information directly in their build artefacts, so that packaging and deployment teams don't need to worry about a developer-generated crontab running something with the wrong credentials (assuming that the package has been configured to run the software under its own user account).
CONFIGURATION
Item definitions, and configuration files, share the same syntax: SETTING=VALUE pairs, one per line. Blank lines are ignored, as are comments (denoted by “#”). Leading and trailing whitespace is ignored.
Unless otherwise stated, each setting takes only one value, so setting it again overrides whatever it was set to earlier. To remove a setting, set it to an empty value.
Placeholders
The following placeholders can be used in values:
- {ITEM}
The name of this item.
- {USER}
The username of the account currently running scw, normalised to lower case.
Configuration settings
The following settings are available in configuration files:
- ItemsDir
-
The directory in which to find item definitions and item scripts. The default value is usually /etc/scw/items/{USER}.
- MetricsDir
-
The directory in which to place metrics files for an item. The directory must already exist and be writable by the current user, or if it doesn't exist, its parent must be writable by the current user so that scw can create it. The default value is usually /var/spool/scw/{USER}/{ITEM}.
- CheckLockFile
-
The lock file to use when performing dependency or conflict checks. The default value is usually /var/spool/scw/{USER}/.lock.
- UserConfigFile
-
The per-user configuration file. When scw runs, it first loads the global configuration file, and then this one. The default value is usually /etc/scw/settings/{USER}.cf. This setting can only be changed in the global configuration file.
- ItemListFile
-
The file to which “scw update” will write a JSON array describing all items, which can be used by a monitoring system to discover what to monitor. The default value is usually /var/spool/scw/items.json. This setting can only be changed in the global configuration file.
- CrontabFile
-
The file to which “scw update” will write a crontab. The default value is usually /etc/cron.d/scw. This setting can only be changed in the global configuration file.
- UpdateLockFile
-
The lock file to use to ensure that only one instance of “scw update” is running at a time. The default value is usually /var/spool/scw/.update-lock. This setting can only be changed in the global configuration file.
Configuration files may also contain any of the other settings listed below for items, to set defaults which items can override.
Item settings
The following settings are available for items.
- Description
-
A short one-line description of what this item does. This is recorded in the item list file by “scw update”.
- Command
-
The command to run. This is passed to “sh -c”.
- Schedule
-
When to run this item. This takes time and date fields in the same format as crontab(5). An item can have up to 16 Schedule values. This is used by “scw update” when generating the system crontab.
- RandomDelay
-
Each time this item starts, there will be a random delay of up to this many seconds before continuing with checks and running the command. The time period can be specified in seconds, or with multiple numbers suffixed with “w” (weeks), “d” (days), “h” (hours), “m” (minutes), or “s” (seconds). Either the whole word can be given, or just the first letter. Spaces are optional. For example, “1d5h7m6s”, “1 day 5 hours 7 minutes 6 seconds”, and “104826” are all equivalent.
- MaxRunTime
-
The maximum number of seconds to allow the command to run, after which it will be forcibly terminated. If this is not set, there is no limit. The same time period formatting rules as RandomDelay apply here.
- Prerequisite
-
A command to run before attempting to run any item's Command. If the Prerequisite command exits with a non-zero status, the item is treated as if it was not scheduled to run at all, and so its command is not run. This can be used to, for instance, check that this server is the active node of a failover cluster, so that scheduled commands only run on the active node. The output of the Prerequisite command is always discarded. Note: The Prerequisite command may be run multiple times for an item if any delays are involved, since it is invoked prior to any delay, and then again after any delay just before the item's Command is to be run.
- SuccessInterval
-
The number of seconds permitted between successful command runs before an alert should be raised. This is not used directly by scw, but is made available as a metrics file for your monitoring system to read - see the Metrics subsection under FILES. The same time period formatting rules as RandomDelay apply here.
- ConcurrencyWait
-
If the item is already running, it will not start a second instance. Instead of abandoning the attempt immediately, it will wait up to this many seconds for the previous run to complete first. If the previous run finishes by then, the new run will proceed as normal. If ConcurrencyWait is not set, there will be no waiting. The same time period formatting rules as RandomDelay apply here.
- SilentConcurrency
-
If the item was already running, and did not finish before the ConcurrencyWait timeout expired, then a second instance won't be started. By default, when this happens, the overrun metrics file is created and no other metrics are affected. If the SilentConcurrency setting is “no”, “off”, “false”, or “0”, then this situation will instead be treated as if the command had run and failed.
- DependsOn
-
The name of another item which must have successfully run since the previous run of this item. If the dependency is not met, this item will not run. An item can have up to 16 DependsOn values.
- DependencyWait
-
If all dependencies are not met, keep waiting for them to be met for up to this many seconds. If, after waiting, the dependencies have been met, start the item as normal. The same time period formatting rules as RandomDelay apply here.
- SilentDependency
-
By default, if an item's dependencies are not met, the item is treated as if it ran its command and it failed. If the SilentDependency setting is “yes”, “on”, “true”, or “1”, then this situation will instead be treated as if the item was not scheduled to be run at all, and no metrics will be updated.
- ConflictsWith
-
The name of another item which must not be running at the same time as this item. If it is, this item will not run. An item can have up to 16 ConflictsWith values.
- ConflictWait
-
If a conflicting item is running, keep waiting for it to finish for up to this many seconds. If, after waiting, the conflicts have been resolved, start the item as normal. The same time period formatting rules as RandomDelay apply here.
- SilentConflict
-
By default, if an item can't start because of a conflict, it is treated as if it ran its command and it failed. If the SilentConflict setting is “yes”, “on”, “true”, or “1”, then this situation will instead be treated as if the item was not scheduled to be run at all, and no metrics will be updated.
- StatusMode
-
A command can provide additional information about its progress through a status stream (see the STATUS REPORTING section). If StatusMode is set to “fd”, then status information is read from the command's file descriptor 3. If it is set to “stdout” or “stderr”, status information is derived from the command's standard output or standard error respectively, using the StatusTag.
- StatusTag
-
When StatusMode is not “fd”, any command output lines in the appropriate stream which start with the StatusTag will have that tag removed and the remainder will be used as the status information. See the STATUS REPORTING section.
- TimestampUTC
-
By default, timestamps are expressed in the system's local time zone. If the TimestampUTC setting is “yes”, “on”, “true”, or “1”, then timestamps are expressed in UTC. Note that this only affects timestamps in the output - the Schedule always refers to the system's local time zone, as cron(8) does.
- OutputMap
-
Map the command's output to a destination. See below for more details. An item can have up to 16 OutputMap values.
Output mapping
The OutputMap settings take values of the form “STREAM FORMAT DESTINATION”, where STREAM selects one or more command output streams, FORMAT selects how it should be formatted, and DESTINATION selects where to send it to.
For example, an item might have this output map configuration, or it could even be in the global configuration file as the default for this server:
# Write stdout, stderr, and status, with timestamps, to a file OutputMap = OES stamped /var/log/scw/{USER}/{ITEM}.log # Email stdout and stderr, without timestamps, to root@localhost OutputMap = OE raw root@localhost # On failure, email stderr, without timestamps, to admin@company.com OutputMap = !E raw admin@company.com # Write status messages to syslog as facility "user", level "notice". OutputMap = S raw user.notice # Send status messages as JSON data via HTTPS POST OutputMap = S json https://status.company.com/receiver
The STREAM is any combination of the following:
- O
Standard output.
- E
Standard error.
- S
Status messages.
- !
Spool until the command completes, and then only send to the destination if the exit status was non-zero, indicating failure.
The FORMAT is one of the following:
- raw
Lines of text exactly as output by the command.
- stamped
Lines prefixed with a timestamp and, if multiple streams were selected, an indicator of which stream they came from.
- json
A JSON object containing integers named epoch and pid, and strings named hostname, user, item, stream, and message; the stream will be one of “stdout”, “stderr”, or “status”.
- form
An HTTP form post (key=value pairs) containing the same fields as the json format.
The DESTINATION is a filename, a list of email addresses separated by semicolons, a syslog priority in the form facility.level, or a URL.
STATUS REPORTING
When an item's command runs through several steps, it can pass details of which step it's up to, and whether the previous step failed, to scw using the status reporting mechanism.
The first word of the status report should be one of “notice”, “ok”, “warning”, or “error”.
When running an item's command, scw inserts a special “begin” status report at the start, and an “end” status report at the end.
Depending on the StatusMode and StatusTag settings, status reporting might look like this:
#!/bin/sh # # scw StatusMode = fd printf "%s %s" "notice" "Starting step 1" >&3 # do some work here if $succeeded; then printf "%s %s" "ok" "Step 1 complete" >&3 else printf "%s %s" "error" "Step 1 failed" >&3 exit 1 fi printf "%s %s" "notice" "Starting step 2" >&3 # do some more work here # ...
Adding status information like this makes analysis easier, for example discovering how the time taken for each step of a multi-step command fluctuates, or clearly highlighting where a command failed.
Using a StatusMode of “stdout” or “stderr” means prefixing the status message with the value of the StatusTag, like this:
#!/bin/sh # # scw StatusMode = stdout # scw StatusTag = STATUS: printf "STATUS: %s %s" "notice" "Starting step 1" # ... and so on.
Writing status information this way may be easier than with the “fd” method in some circumstances. It has the side effect that the status messages will also be recorded in standard output or standard error logs.
EXIT STATUS
The following exit status values apply to all actions:
- 0
-
Success: the action completed without error.
- 4
-
An unknown option, action, or setting was passed on the command line, or too many or too few arguments were provided for the chosen action. No action was taken.
- 5
-
The configuration file could not be read, or contains unrecoverable errors. No action was taken.
- 6
-
Some other error occurred that was not covered by any of the above. Action may have been partially completed.
Exit status values for “run”
The “run” action can exit with one of the following:
- 0
-
Success: the action completed without error.
- 1
-
Item failed: the item's command was run, and it exited non-zero.
- 2
-
Item timed out: the item's command was run, but it reached its configured maximum run time, and was forcibly terminated.
- 8
-
Item has no command: the specified item has no entry in the item definition directory and there was no command in the remaining command line arguments to scw, so no command has been run.
- 9
-
Item not enabled: the item is currently disabled, and the “--force” option was not provided, so the command has not been run.
- 10
-
Item prerequisites not met: the prerequisite check has failed, so the command has not been run.
- 11
-
Item dependencies not met: the items on which this item depends have not all run, so the command has not been run.
- 12
-
Item conflict: one of the items which this item conflicts with is currently running, and all options for startup delays have been exhausted, so the command has not been run.
- 13
-
Item already running: the item is already running, and all of its configured options for startup delays have been exhausted, so the command has not been run.
Note that any exit status lower than 3 indicates that the item's command was definitely started; only an exit status of 0 indicates that it succeeded.
Exit status values for “status”
The “status” action exits with the sum of the following values:
- 16
-
Added if the item does not exist (meaning that it has no entry in the item definition directory).
- 32
-
Added if the item is disabled.
- 64
-
Added if the item is currently running.
For example, an exit status of 0 indicates that the item exists, is enabled, and is not currently running. An exit status of 96 indicates an item which is disabled, but currently running.
FILES
File locations may be adjusted by the installation process, so for example paths listed here under /etc may be under /usr/local/etc on your system. Locations may also be overridden by configuration settings.
- /etc/scw/default.cf
-
Global default settings.
- /etc/scw/settings/USER.cf
-
Settings to apply when running as user USER.
- /etc/scw/items/USER/*.cf
-
Item definitions for user USER.
- /etc/scw/items/USER/*.sh
- /etc/scw/items/USER/*.pl
-
Item scripts for user USER, with their definitions embedded in a comment block at the top of the script (see the ITEM DEFINITIONS section).
- /var/log/scw/USER/*.log
-
The default location for log files generated by items owned by USER.
- /var/spool/scw/USER/ITEM/
-
Metrics files for the item ITEM owned by the user USER. See below for more details.
- /var/spool/scw/USER/.lock
-
An empty file used for locking while checking for dependencies and conflicts.
- /var/spool/scw/items.json
-
A JSON array describing all items, suitable for using as a Zabbix low-level discovery file. Updated by “scw update”.
- /etc/cron.d/scw
-
The default crontab written by “scw update”.
- /var/spool/scw/.update-lock
-
An empty file used for locking while running “scw update”.
Metrics
The metrics directory for an item can contain these files:
- disabled
-
An empty file whose presence indicates that the item is disabled, and whose last-modification time indicates when it was disabled.
- success-interval
-
The number of seconds permitted between successful command runs before an alert should be raised, followed by a newline. Monitoring systems should be instructed to raise an alert if the succeeded file's last-modification time is more than success-interval seconds ago and the prerequisites-met file exists.
- prerequisites-met
-
An empty file which is created if the item's prerequisites are met, and deleted if they are not. Its last-modification time indicates when the prerequisites were last successfully checked.
- started
-
An empty file whose last-modification time indicates when the item last started. It is not updated until the item's command actually starts running (so, after any startup delays).
- ended
-
An empty file whose last-modification time indicates when the item last ended after running the item's command, regardless of whether the command succeeded. Its last-modification time is not updated unless the command actually ran.
- succeeded
-
An empty file whose last-modification time indicates when the item's command last ran and ended with a zero exit status. It is not deleted on failure.
- failed
-
An empty file which is created when the item's command runs and ends with a non-zero exit status. Its last-modification time indicates when the command first failed - it is not updated when subsequent runs fail. This file is deleted as soon as the command runs and exits with a zero exit status.
- overran
-
An empty file which is created when the item could not run because it was already running and all startup delay options were exhausted. Its last-modification time indicates when this first happened. It is deleted the next time the item is able to start.
- run-time
-
The number of seconds the item's command most recently took to run, followed by a newline. It is updated each time the item's command finishes a run, not counting any startup delays, and regardless of the command's exit status.
- .lock
-
An empty file which the item will lock while running.
- pid
-
While the item is running, this file contains the item's process ID, followed by a newline. The file is deleted on exit.
- last-status
-
The status message most recently reported by the item (see the STATUS REPORTING section).
NOTES
Time periods such as MaxRunTime can be written in seconds, or as any combination of weeks, days, hours, minutes, and seconds, each number suffixed with the unit. Spaces are allowed between each component of the time period, but no other words or punctuation. These are all equivalent:
MaxRunTime = 2 weeks 3 days 10 hours 5 minutes 2 seconds MaxRunTime = 2w 3d 10h 5m 2s MaxRunTime = 2w3d10h5m2s MaxRunTime = 17 days 605 minutes 2 seconds MaxRunTime = 1505102 seconds MaxRunTime = 1505102
The name of an item can only contain letters, numbers, underscores, and hyphens.
Item settings files and scripts in ItemsDir, the per-user configuration file in UserConfigFile, and the global configuration file, must be normal files, and must not be symbolic links.
Each setting which takes multiple values - Schedule, DependsOn, ConflictsWith, OutputMap - is limited to 16 values in total after applying rules from all relevant sources. For example, if the global configuration defines 3 OutputMap values, an item can only add 13 more unless it first clears the list by assigning an empty value to OutputMap.
EXAMPLES
The global configuration file /etc/scw/default.cf could look like this:
# Configuration settings ItemsDir = /etc/scw/items/{USER} MetricsDir = /var/spool/scw/{USER}/{ITEM} CheckLockFile = /var/spool/scw/{USER}/.lock UserConfigFile = /etc/scw/settings/{USER}.cf ItemListFile = /var/spool/scw/items.json CrontabFile = /etc/cron.d/scw UpdateLockFile = /var/spool/scw/.update-lock # Item defaults SilentConcurrency = yes SilentDependency = no SilentConflict = no StatusMode = fd TimestampUTC = no # Write stdout, stderr, and status, with timestamps, to a file OutputMap = OES stamped /var/log/scw/{USER}/{ITEM}.log # On failure, email stderr, without timestamps, to root OutputMap = !E raw root
An associated configuration file /etc/logrotate.d/scw for logrotate(8) would look like this:
/var/log/scw/*/*.log { weekly compress missingok notifempty nocreate }
A local application “app” which runs under the user account appuser, with its own scheduled commands, could have a per-user configuration file /etc/scw/settings/appuser.cf like this:
# Replace the output map for the application's scheduled commands OutputMap = OutputMap = OES stamped /srv/app/logs-full/{ITEM}.log OutputMap = S stamped /srv/app/logs-status/{ITEM}.log OutputMap = S raw user.notice OutputMap = S json https://monitoring.company.com/scw-receiver # Keep the metrics near the logs MetricsDir = /srv/app/metrics/{ITEM} # Only run scheduled commands if the application is active Prerequisite = systemctl --quiet is-active app.service # The application keeps its scheduled commands alongside the rest of its # deployed software ItemsDir = /opt/app/scheduled
The deployment package for “app” could then include scheduled command definitions for the application, such as this example which would be named /opt/app/scheduled/backup.sh:
#!/bin/sh # scw Description = Back up the application's files # scw Schedule = 0 22 * * * # 10pm every day # scw SuccessInterval = 129600 # alert after a day and a half printf "%s %s" "notice" "starting backup" >&3 /opt/app/sbin/trigger-app-backup exitStatus=$? if test $exitStatus -eq 0; then printf "%s %s" "ok" "backup successful" >&3 else printf "%s %s" "error" "backup failed - exit status $exitStatus" >&3 fi exit $exitStatus
The post-installation script for the “app” package would include a call to “scw update”. This would define the item “backup” for the user “appuser”, and would regenerate the crontab file /etc/cron.d/scw. The script /opt/app/scheduled/backup.sh would then automatically run at 10pm daily, and write its logs as directed by the OutputMap settings in /etc/scw/settings/appuser.cf. If the monitoring system was directed to discover items by reading the global item list file /var/spool/scw/items.json, it would start monitoring this scheduled command automatically.
REPORTING BUGS
Please report any bugs to scw@ivarch.com.
Alternatively, use the issue tracker linked from the scw home page.
SEE ALSO
crontab(5), cron(8)
COPYRIGHT
Copyright © 2024 Andrew Wood.
License GPLv3+: GNU GPL version 3 or later.
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.