Initial import
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal 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
10
Makefile
Normal 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
5
debian/changelog
vendored
Normal 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
22
debian/control
vendored
Normal 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
25
debian/copyright
vendored
Normal 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
2
debian/install
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
src/bitcron usr/bin
|
||||
etc/example.cron etc/bitcron
|
23
debian/postinst
vendored
Executable file
23
debian/postinst
vendored
Executable 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
8
debian/rules
vendored
Executable 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
26
etc/example.cron
Normal 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
326
src/bitcron
Executable 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
|
Reference in New Issue
Block a user