#!/bin/sh #$Id$ #Copyright (c) 2020-2021 Pierre Pronchery #This file is part of DeforaOS Devel scripts # #Redistribution and use in source and binary forms, with or without #modification, are permitted provided that the following conditions #are met: #1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. #2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE EDGEBSD PROJECT AND CONTRIBUTORS #``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED #TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR #PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS #BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR #CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF #SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS #INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN #CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) #ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #POSSIBILITY OF SUCH DAMAGE. #variables #executables DATABASE="database" DEBUG="_debug" LOCK="mkdir" UNLOCK="rmdir" SED="sed" #settings PROGNAME="deforaos-jobs" DATABASE_CONFFILE="$PROGNAME.conf" DATABASE_ENGINE="sqlite3" DATABASE_FILE="$PROGNAME.db" DATABASE_INITFILE="$PROGNAME.sql" DEVNULL="/dev/null" PREFIX="/usr/local" QUERY_ADD_BEGIN="INSERT INTO jobs (command) VALUES ('" QUERY_ADD_END="')" QUERY_EXEC_SELECT="SELECT jobs_id, command FROM jobs WHERE started IS NULL ORDER BY jobs_id ASC LIMIT 1" QUERY_INIT="CREATE TABLE jobs (jobs_id INTEGER PRIMARY KEY, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, command VARCHAR(255), started TIMESTAMP DEFAULT NULL, pid INTEGER DEFAULT NULL, code INTEGER DEFAULT NULL, completed TIMESTAMP DEFAULT NULL)" QUERY_LIST="SELECT * FROM jobs" SYSCONFDIR="$PREFIX/etc" VENDOR="DeforaOS" VERBOSE=1 #load local settings [ -f "$SYSCONFDIR/$VENDOR/$PROGNAME.conf" ] && . "$SYSCONFDIR/$VENDOR/$PROGNAME.conf" [ -f "$HOME/.config/$VENDOR/$PROGNAME.conf" ] && . "$HOME/.config/$VENDOR/$PROGNAME.conf" #Error codes: # 1 Usage error # 2 Generic error # 3 Could not lock the database # 4 Could not unlock the database #functions #database_add _database_add() { if [ $# -ne 1 ]; then _usage return $? fi command="$1" query="$QUERY_ADD_BEGIN$(echo "$command" | _database_escape)$QUERY_ADD_END" _info "Queueing command: $command" _database_init || return 2 _database_query "$query" > "$DEVNULL" || return 2 } #database_escape _database_escape() { $SED -e "s,',''," } #database_exec _database_exec() { if [ $# -ne 0 ]; then _usage return $? fi _database_init || return 2 _database_lock "$DATABASE_FILE" || return 3 _database_query "$QUERY_EXEC_SELECT" | (IFS="|" read header read empty jobs_id command empty if [ -z "$jobs_id" ]; then _database_unlock "$DATABASE_FILE" || return 4 return 0 fi #XXX TOCTOU QUERY="UPDATE jobs SET started=datetime() WHERE jobs_id='$jobs_id'" _database_query "$QUERY" > "$DEVNULL" _database_unlock "$DATABASE_FILE" code=-1 if [ -n "$command" ]; then _info "Executing command: $command" $DEBUG sh -c "$command" & pid=$! QUERY="UPDATE jobs SET pid='$pid' WHERE jobs_id='$jobs_id'" _database_query "$QUERY" > "$DEVNULL" wait $pid code=$? fi QUERY="UPDATE jobs SET completed=datetime(), code='$code' WHERE jobs_id='$jobs_id'" _database_query "$QUERY" > "$DEVNULL") } #database_init _database_init() { ret=0 if [ ! -f "$DATABASE_CONFFILE" ]; then echo "filename=$DATABASE_FILE" > "$DATABASE_CONFFILE" fi if [ ! -f "$DATABASE_FILE" ]; then _info "Initializing the database" _database_lock "$DATABASE_FILE" || return 3 _database_query "$QUERY_INIT" > "$DEVNULL" || ret=2 _database_unlock "$DATABASE_FILE" || return 4 fi return $ret } #database_list _database_list() { if [ $# -ne 0 ]; then _usage return $? fi _database_init || return 2 _info "Listing the jobs" _database_query "$QUERY_LIST" || return 2 } #database_lock _database_lock() { filename="$1" $DEBUG $LOCK "$filename.lock" } #database_query _database_query() { $DEBUG $DATABASE -d "$DATABASE_ENGINE" -C "$DATABASE_CONFFILE" "$@" } #database_unlock _database_unlock() { filename="$1" $DEBUG $UNLOCK "$filename.lock" } #debug _debug() { [ $VERBOSE -ge 2 ] && echo "$@" 1>&3 "$@" } #error _error() { echo "$PROGNAME: $@" 1>&2 return 2 } #info _info() { [ $VERBOSE -eq 0 ] || echo "$PROGNAME: $@" } #usage _usage() { echo "Usage: $PROGNAME [-d directory][-qv] add command" 1>&2 echo " $PROGNAME [-d directory][-qv] exec" 1>&2 echo " $PROGNAME [-d directory][-qv] list" 1>&2 return 1 } #main directory= while getopts "O:d:qv" name; do case "$name" in O) export "${OPTARG%%=*}"="${OPTARG#*=}" ;; d) directory="$OPTARG" ;; q) VERBOSE=0 ;; v) VERBOSE=$((VERBOSE + 1)) ;; ?) _usage exit $? ;; esac done shift $((OPTIND - 1)) if [ $# -lt 1 ]; then _usage exit $? fi exec 3>&1 method="_usage" case "$1" in add|list|exec) method="_database_$1" ;; *) _error "$1: Operation not supported" ;; esac shift if [ -n "$directory" ]; then (cd "$directory" && "$method" "$@") else "$method" "$@" fi