I've wanted something with a bit more flexibility than standard Cron for a while, and I finally came up with a simple solution that I like and felt might be of interest to this community.
The core of my scheduler is a Bash script, and the script searches for files in a "tasks" subfolder that has one folder for each hostname the scheduler is run on. Here's what the layout of what I currently have looks like: cron$ tree . |-- cron |-- logs | `-- [...] `-- tasks `-- sinister |-- backup.sh |-- email-digest.sh `-- woot-watch.sh Each of the scripts has two functions defined: "condition," which exits successfully if the script should run and "run," which contains the execution logic. The core script sources each of the tasks for the current host then checks to see if the "condition" function exits successfully before invoking the "run" function and logging the output. I currently run the core scheduler on top of top Vixie / ISC cron so I don't have to worry privileged system access: ~$ /usr/bin/crontab -l MAILTO="" * * * * * bash ~/cron/cron I've attached the prototype I'm currently using to this email as well as a sample task script. I know Bash isn't considered suckless, but there's nothing preventing any of this from being done in POSIX shell. The script also recognizes "-l" and "-e" flags for listing and editing scripts, respectively, and I currently have the core script symlinked to ~/bin/crontab. Eric
#!/usr/bin/env bash set -o pipefail export YEAR=$(printf '%(%Y)T' -1) export MONTH=$(printf '%(%m)T' -1) export DAY=$(printf '%(%d)T' -1) export HOUR=$(printf '%(%H)T' -1) export MINUTE=$(printf '%(%M)T' -1) export DAYOFWEEK=$(printf '%(%w)T' -1) export MAILTO=$USER export SENDMAIL=/usr/sbin/sendmail run_tasks() { local basename local default_subject local logfile local send_email local task send_email=0 for task in "tasks/$HOSTNAME/"*; do if ! [[ -e "$task" ]]; then break else basename=$(basename "$task") basename=${basename%.sh} fi if source "$task" && condition; then echo "[!] $basename" ( logfile="logs/$basename.$(printf '%(%Y-%m-%dT%H:%M%z)T' -1)" if ! run &> "$logfile"; then send_email=${send_email:-1} default_subject="$basename (failed)" else default_subject="$basename" fi [[ "$send_email" -ne 0 ]] && { echo "Date: $(date -R)" echo "From: Cron Script <$USER@$HOSTNAME>" echo "To: <$MAILTO>" echo "Subject: ${SUBJECT:-$default_subject}" echo cat "$logfile" } | "$SENDMAIL" -oi -- "$MAILTO" if [[ -e "$logfile" ]] && ! [[ -s "$logfile" ]]; then rm "$logfile" fi ) & else echo "[ ] $basename" fi done wait } main() { local line local log_format local _ case "$1" in "") log_format='%(%Y-%m-%dT%H:%M:%S%z)T [%d]: %s\n' while read line; do printf "$log_format" -1 "$$" "$line" | tee -a master-log done < <(echo "Started:"; run_tasks 2>&1; echo "Finished.") ;; -e) shopt -s nullglob for _ in tasks/"$HOSTNAME"/*"$2"*; do $EDITOR -- tasks/"$HOSTNAME"/*"$2"* exit done $EDITOR -- "tasks/$HOSTNAME/$2" ;; -l) if which tree &> /dev/null; then tree tasks else ls -l -R tasks fi esac } cd "$(dirname "$(readlink -f "$0")")" mkdir -p logs "tasks/$HOSTNAME" main "$@"
backup.sh
Description: Bourne shell script