#!/bin/sh # Copyright 2006 Alan Grow. # All rights reserved. # Licensed under the terms contained in the file 'COPYING' in the # source directory. # plopdb - manipulate the plop database: # # plopdb add /path/to/file # make a file available # plopdb update file 1158556433 # expire a file # plopdb rm file # remove all trace of a file # # this program is an almost generic flat-file database interface. transactioning # is handled by the unix fs. locking, however, is not: if you expect concurrent # usage, you should surround all calls to this program with a lock wrapper. # i suggest setlock from daemontools: http://cr.yp.to/daemontools/setlock.html. # # TODO sh pipelines discard errors, so their use is not truly safe here # TODO creative self-exec'ing could make the lock window narrower # set -e for d in /etc/plop /usr/local/etc/plop; do for f in plop.conf.default plop.conf; do test -f "$d/$f" && . "$d/$f" done done plopdbdir="$plopdir/db" plopfiledir="$plopdir/files" ploptmpdir="$plopdir/tmp" plopmasterdb="$plopdbdir/master" die() { echo "$@"; exit 111; } genkey() { path="$1" base=`basename "$path"` { echo "$base"; cat "$path"; } | $cmd_genkey | cut -f1 -d' ' return 0 } rmtxn() { key="$1" cd "$plopdbdir" for d in add remove update; do test -f "$d/$key" && rm "$d/$key" done return 0 } lstxns() { cd "$plopdbdir" for d in "$@"; do ( cd "$d"; ls | sed -e "s#^#$d/#" ) done return 0 } txncount() { lstxns "$@" | wc -l return 0 } add() { path="$1" if [ $# -gt 1 ]; then extra="$2" else now="`date +%s`" extra=$(( $now + $plopdefaultexpire )) fi base=`basename "$path"` test -e "$plopfiledir/$base" && die "error, file $base already in database." genkey "$path" | ( read key tmpfile="`$cmd_mktemp`" cp "$path" "$tmpfile" mv "$tmpfile" "$plopfiledir/$base" record="$key $base $extra" tmptxn="`$cmd_mktemp`" echo "$record" > "$tmptxn" rmtxn "$key" mv "$tmptxn" "$plopdbdir/add/$key" ) return 0 } remove() { file="$1" base=`basename "$file"` test -e "$plopfiledir/$base" || die "error, file $base not in database." genkey "$plopfiledir/$base" | ( read key record="$key $base" tmptxn="`$cmd_mktemp`" echo "$record" > "$tmptxn" rmtxn "$key" mv "$tmptxn" "$plopdbdir/remove/$key" ) return 0 } update() { file="$1" extra="$2" base=`basename "$file"` test -e "$plopfiledir/$base" || die "error, file $base not in database." genkey "$plopfiledir/$base" | ( read key record="$key $base $extra" tmptxn="`$cmd_mktemp`" echo "$record" > "$tmptxn" rmtxn "$key" mv "$tmptxn" "$plopdbdir/update/$key" ) return 0 } rebuild() { tmptxn="`$cmd_mktemp`" tmpmaster="`$cmd_mktemp`" master="$plopdbdir/master" if [ "`txncount remove update`" -gt 0 ]; then lstxns remove update | \ while read f; do cat "$plopdbdir/$f"; done | \ sort -k 1 -t' ' -u | \ join -v 2 - "$master" | \ sort -k 1 -t' ' -u > "$tmptxn" mv "$tmptxn" "$tmpmaster" master="$tmpmaster" fi if [ "`txncount add update`" -gt 0 ]; then lstxns add update | \ while read f; do cat "$plopdbdir/$f"; done | \ { cat; cat "$master"; } | \ sort -k 1 -t' ' -u > "$tmptxn" mv "$tmptxn" "$tmpmaster" master="$tmpmaster" fi if [ "$master" != "$plopdbdir/master" ]; then mv "$master" "$plopdbdir/master" fi lstxns add update remove | \ while read f; do rm "$plopdbdir/$f"; done return 0 } cleanup() { tmplist="`$cmd_mktemp`" cut -f2 -d' ' "$plopdbdir/master" | sort -u > "$tmplist" ls "$plopfiledir" | sort -u | join -v 1 - "$tmplist" | \ while read f; do rm "$plopfiledir/$f"; done rm -f "$ploptmpdir/"* return 0 } cmd="$1" shift case "$cmd" in add) add "$@" rebuild cleanup ;; remove|rm) remove "$1" rebuild cleanup ;; update) update "$@" rebuild cleanup ;; rebuild) rebuild ;; cleanup) cleanup ;; *) die "usage: $0 add|rm|update file" ;; esac