Initial import

This commit is contained in:
Pim van Pelt
2025-07-13 23:39:04 +02:00
commit e3264e6489
10 changed files with 455 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
# Debian packaging artifacts
debian/.debhelper/
debian/.gocache/
debian/files
debian/bitcron
debian/*.substvars
debian/debhelper-build-stamp
debian/*.debhelper

10
Makefile Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/make -f
.PHONY: pkg-deb deb-clean
pkg-deb:
dpkg-buildpackage -us -uc
clean:
rm -f ../bitcron_*.deb ../bitcron_*.changes ../bitcron_*.tar.xz ../bitcron_*.dsc ../bitcron_*.buildinfo
rm -rf debian/.debhelper debian/files debian/bitcron debian/*.substvars debian/debhelper-build-stamp

5
debian/changelog vendored Normal file
View File

@@ -0,0 +1,5 @@
bitcron (1.0-1) unstable; urgency=medium
* Initial release.
-- Pim van Pelt <pim@ipng.ch> Sun, 13 Jul 2025 12:00:00 +0000

22
debian/control vendored Normal file
View File

@@ -0,0 +1,22 @@
Source: bitcron
Section: admin
Priority: optional
Maintainer: Pim van Pelt <pim@ipng.ch>
Build-Depends: debhelper-compat (= 13)
Standards-Version: 4.6.0
Homepage: https://git.ipng.ch/ipng/bitcron
Vcs-Git: https://git.ipng.ch/ipng/bitcron.git
Vcs-Browser: https://git.ipng.ch/ipng/bitcron
Package: bitcron
Architecture: all
Depends: ${misc:Depends}, bash, sendmail | mail-transport-agent
Description: Enhanced cron script framework with error handling and reporting
Bitcron is a framework for writing cron scripts that provides enhanced
error handling, warning management, and automatic email reporting capabilities.
It makes cron jobs more robust and easier to monitor by providing structured
logging and escalation mechanisms.
.
The framework includes functions for warning(), error(), and fatal() reporting,
automatic log generation, and configurable email notifications for both
successful runs and error conditions.

25
debian/copyright vendored Normal file
View File

@@ -0,0 +1,25 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: bitcron
Upstream-Contact: Pim van Pelt <pim@bit.nl>
Source: https://github.com/pimvanpelt/bitcron
Files: *
Copyright: Pim van Pelt <pim@bit.nl>
License: GPL-2+
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

2
debian/install vendored Normal file
View File

@@ -0,0 +1,2 @@
src/bitcron usr/bin
etc/example.cron etc/bitcron

23
debian/postinst vendored Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
set -e
case "$1" in
configure)
# Create the log directory for bitcron
mkdir -p /var/log/bitcron
chmod 755 /var/log/bitcron
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0

8
debian/rules vendored Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_clean:
override_dh_auto_build:
override_dh_auto_configure:

26
etc/example.cron Normal file
View File

@@ -0,0 +1,26 @@
NAME="mycron"
AUTHOR="Pim van Pelt"
MAILTO="pim@ipng.ch"
ESCALATE_MAILTO="pim+escalate@ipng.ch"
MASTERLOG="/var/log/bitcron/${NAME}.log"
LINT='$Id$'
# You can test this thing with:
# ./bitcron -n example.cron foo bar baz
bitcron_main()
{
echo "Version: $LINT"
if [ $# -ne 0 ]; then
echo "Your commandline arguments were:"
while [ $# -gt 0 ]; do echo $1; shift; done
fi
warning "You beter watch it .."
error "Oops, now you blew it!"
fatal "I refuse to work with you any longer!!!"
// Not reached
echo "Not reached, because of a fatal error"
}

326
src/bitcron Executable file
View File

@@ -0,0 +1,326 @@
#!/usr/bin/env bash
# Author: Pim van Pelt <pim@ipng.ch>
# This is the master cron file format. It is designed to be quiet
# and not shout anything at all to stdout/stderr in normal use.
#
# Usage: bitcron [-n] /path/to/crondefinition.cron [arg [arg ..]]
#
# This program eats either one or two arguments from the commandline.
# If -n is given as the first argument, output is sent to stdout rather
# than MAILTO or ESCALATE_MAILTO (this is a `dryrun'). The next argument
# is a filename consisting of the cron definition. The file is sourced
# and executed, but within a framework that makes crons much easier to
# live with. You SHOULD have the filename of your crondefinition end
# in ".cron", so it is apparent to other people that this is indeed
# a cron definition. All other arguments are passed to the crontab's
# main function, called bitcron_main; see below.
#
# The cron definition file consists of the following shell variables:
#
# PATH: Optional. You can set a standard path for your cron to search
# programs in. Note that cron sets a minimal path, so you probably need
# this if you are going to call programs from weird places.
# Example: PATH=$PATH
#
# AUTHOR: Mandatory. Your full name, so we can track the author in case
# something goes horribly wrong.
# Example: AUTHOR="Pim van Pelt"
#
# NAME: Mandatory. The name of your script. This MUST be a terse
# descriptive name consisting of [A-Za-z0-9\-\_], and in particular
# no whitespace. It's used in logs and mails.
# Example: NAME="example"
#
# MASTERLOG: Optional. If this is set, the transcript of the log is copied
# to this file upon completion. If it is not set, the transcript is silently
# deleted. Please make sure that this filename is writable for the calling
# user, and that its name reveals who wrote it (including $NAME is a good
# idea.
# Example: MASTERLOG="/var/log/cron-${NAME}.log"
#
# MAILTO: Optional. If this is set, the transcript of the log is mailed to
# this user using /usr/sbin/sendmail -t, in the event of no warnings,
# errors, or fatal errors, aka 'if all went well'. If it is not set, the
# mail is not sent on succesful completion of this cronjob. Please use
# with care if your cron runs often.
# Example: MAILTO="pim+noise@ipng.nl"
#
# ESCALATE_MAILTO: Mandatory. If this is set, the transcript of the log
# is mailed to this user using /usr/sbin/sendmail -t, in the event of
# any warning, error or fatal error, aka 'if anything borked'. Please
# make sure that this address is not a single person, but rather a role
# account. This ensures better closure and continuity.
# Example: ESCALATE_MAILTO="pim@ipng.nl"
#
# Variables your script MUST NOT change are:
# ERR, WRN, HOSTNAME, FATAL, FQDN
#
# Your cron-definition does not shebang. It begins with these variables.
# It then has at least one function called bitcron_main(), in which you write
# your cron just like you normally would, but each step is prepended by a
# short oneliner describing what you are doing and appended with a line
# stating you are done doing it, followed by an empty line.
#
# In your bitcron_main() script, you have three functions at your disposal:
# warning $TEXT
# error $TEXT
# fatal $TEXT
#
# where warning() prints the TEXT and increments the warning counter, error()
# increments the error counter and fatal() increments the error counter and
# immediately halts execution of the cron.
#
# For example:
# bitcron_main()
# {
# echo "Running rsync"
# rsync blabla || warning "Could not rsync"
# echo "Done (rsync)"
# echo ""
#
# echo "Doing something crucial"
# /usr/local/bin/crucial.sh blabla || \
# fatal "Could not do the crucial stuff, aborting!"
# echo "Done (crucial)"
# echo ""
#
# echo "Succesfully executed the cron"
# }
#
# In the event of a fatal error (retval of crucial.sh != 0), the line
# 'echo "Done (crucial)"' will not be executed, rather the cronscript
# stops and mails to the escalation mail address that there was a serious
# error which needs human intervention.
#
# Return values of bitcron depend on the presence of errors or warnings.
# If there are no errors nor warnings, 0 is returned to the calling shell.
# If there are errors, 127 is returned. If there are no errors, but there
# are warnings, 126 is returned. If you somewhere set the RETVAL variable
# (which is optional), that value is returned instead.
#
# Please note that your bitcron_main() function MUST NOT use exit. This messes up
# the clean execution of this script, prohibiting it from mailing or
# returning a decent exitcode to the calling shell.
#
BITCRON_SEARCHPATH="./ ~/bitcron/ /etc/bitcron/ /usr/local/etc/bitcron/"
BITCRON_REPORTTOOL="/usr/sbin/sendmail -t"
fatal()
{
echo ""
echo "===== FATAL ====="
echo $1
echo "===== FATAL ====="
ERR=$(($ERR + 1))
FATAL=1;
bitcron_end
}
error()
{
echo ""
echo "===== ERROR ====="
echo $1
echo "===== ERROR ====="
ERR=$(($ERR + 1))
}
warning()
{
echo ""
echo "===== WARNING ====="
echo $1
echo "===== WARNING ====="
WRN=$(($WRN + 1))
}
bitcron_report()
{
if [ $WRN -eq 0 -a $ERR -eq 0 ]; then
if [ ! -z "$MAILTO" ]; then
(
echo "From: $HOSTNAME $NAME <`whoami`@$FQDN>"
echo "To: $MAILTO"
echo "Subject: bitcron $NAME - succesful"
echo "Date: `date '+%a, %d %b %Y %T %z'`"
echo "Errors-To: $MAILTO"
echo "Reply-To: $MAILTO"
echo "X-bitcron: $NAME"
echo "X-Precedence: automatic"
echo ""
echo "Dear reader,"
echo ""
echo "This is the $0 program on $HOSTNAME informing"
echo "you I have succesfully executed the bitcron $NAME."
echo ""
echo "The log that I have for this cronjob follows:"
cat $LOG
echo ""
echo "This mail is purely FYI, user intervention is not required."
echo ""
echo "-- "
echo "Cheers,"
echo " $HOSTNAME"
) | ${BITCRON_REPORTTOOL}
fi
else
(
echo "From: $HOSTNAME $NAME <`whoami`@$FQDN>"
echo "To: $ESCALATE_MAILTO"
if [ ! -z "$MAILTO" ]; then
echo "CC: $MAILTO"
fi
if [ $FATAL -eq 0 ]; then
echo "Subject: bitcron $NAME - $ERR error(s), $WRN warning(s)"
else
echo "Subject: bitcron $NAME - FATAL - $ERR error(s), $WRN warning(s)"
fi
echo "Date: `date '+%a, %d %b %Y %T %z'`"
echo "Errors-To: $ESCALATE_MAILTO"
echo "Reply-To: $ESCALATE_MAILTO"
echo "X-Precedence: automatic"
echo "X-bitcron: $NAME"
echo "X-Errors: $ERR"
echo "X-Warnings: $WRN"
echo ""
echo "Dear reader,"
echo ""
echo "This is the $0 program on $HOSTNAME informing"
echo "you that there was a problem running the bitcron $NAME."
echo ""
echo "The logfile that lead up to this:"
cat $LOG
echo ""
echo "I hope you can deal with this situation ASAP."
echo ""
echo "-- "
echo "Cheers,"
echo " $HOSTNAME"
) | ${BITCRON_REPORTTOOL}
fi
}
bitcron_begin()
{
if [ -z "$MASTERLOG" ]; then
LOG="/tmp/bitcron-$NAME.$$"
else
LOG="${MASTERLOG}.$$"
fi
mkdir -p $(dirname $LOG)
rm -f -- $LOG
exec 3>&1 4>&2 1>$LOG 2>&1
WRN=0
ERR=0
FATAL=0
FQDN=$(hostname)
HOSTNAME=$(hostname -s)
if [ `uname -s` = "Linux" ]; then
HOSTNAME=$(hostname -s)
FQDN=$(hostname -f)
fi
if [ "A`echo $FQDN | grep '\.'`" = "A" ]; then
FQDN="${FQDN}.paphosting.net"
fi
echo "Beginning $NAME cronjob at `date`"
echo ""
}
bitcron_end()
{
echo ""
echo "Done with $NAME cronjob at `date`"
echo ""
echo "There were $ERR error(s) and $WRN warning(s)"
if [ $FATAL -ne 0 ]; then
echo "This script had a fatal error!"
fi
exec 1>&3 2>&4
bitcron_report
if [ ! -z "$MASTERLOG" ]; then
mv -f -- "$LOG" "$MASTERLOG"
fi
if [ ! -z $RETVAL ]; then
exit $RETVAL
elif [ $ERR -ne 0 ]; then
exit 127
elif [ $WRN -ne 0 ]; then
exit 126
fi
exit 0
}
bitcron_usage()
{
echo ""
echo "Usage: bitcron [-n] /path/to/crondefinition.cron [arg [arg ..]]"
echo ""
echo "Please read the bitcron script. It has documentation at the"
echo "top of the file. Look in $0 for more details."
echo "-- Author: Pim van Pelt <pim@bit.nl>"
}
# Here goes!
if [ $# -lt 1 ]; then
bitcron_usage
exit 128
fi
# See if we are in dryrun mode.
if [ $1 = "-n" ]; then
BITCRON_REPORTTOOL="/bin/cat"
shift
fi
BITCRON_DEFINITION=$1
shift;
#
# Search through paths if the cron definition was not an absolute path.
#
if [ ${BITCRON_DEFINITION%%/*} ]; then
for BITCRON_DIR in ${BITCRON_SEARCHPATH}; do
if [ -r ${BITCRON_DIR}${BITCRON_DEFINITION} ]; then
break;
fi
done
else
BITCRON_DIR=""
fi
if [ ! -r ${BITCRON_DIR}${BITCRON_DEFINITION} ]; then
echo "bitcron: Unreadable cron definition: ${BITCRON_DIR}${BITCRON_DEFINITION}"
if [ ${BITCRON_DEFINITION%%/*} ]; then
echo "I tried in: ${BITCRON_SEARCHPATH}"
fi
bitcron_usage
exit 128
fi
. ${BITCRON_DIR}${BITCRON_DEFINITION}
if [ -z "$NAME" ]; then
echo "bitcron: Missing mandatory variable NAME in ${BITCRON_DIR}${BITCRON_DEFINITION}"
bitcron_usage
exit 128
fi
if [ -z "$AUTHOR" ]; then
echo "bitcron: Missing mandatory variable AUTHOR in ${BITCRON_DIR}${BITCRON_DEFINITION}"
bitcron_usage
exit 128
fi
if [ -z "$ESCALATE_MAILTO" ]; then
echo "bitcron: Missing mandatory variable ESCALATE_MAILTO in ${BITCRON_DIR}${BITCRON_DEFINITION}"
bitcron_usage
exit 128
fi
bitcron_begin
bitcron_main $*
bitcron_end