#! /usr/bin/env bash
#
# Copyright (c) 2014 Roy Keene
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
appfsd_options=()
if [ "$1" == "--cachedir" ]; then
appfsd_options=("${appfsd_options[@]}" '--cachedir' "$2")
shift; shift;
fi
function call_appfsd() {
appfsd "${appfsd_options[@]}" "$@"
}
function invalidate() {
call_appfsd --sqlite3 'UPDATE sites SET ttl = "0";'
}
function remove_site() {
local site
site="$1"
call_appfsd --sqlite3 'DELETE FROM sites WHERE hostname = '"'$site'"'; DELETE FROM packages WHERE hostname = '"'$site'"';' || return 1
clean
}
function clean() {
call_appfsd --tcl "$(cat <<\_EOF_
unset -nocomplain row
::appfs::db eval {SELECT sha1, hostname FROM packages;} row {
set hostname [::appfs::db onecolumn {SELECT hostname FROM sites WHERE hostname = $row(hostname) LIMIT 1;}]
if {$hostname == ""} {
continue
}
set valid_sha1($row(sha1)) 1
::appfs::db eval {SELECT file_sha1 FROM files WHERE file_sha1 IS NOT NULL AND file_sha1 != '' AND package_sha1 = $row(sha1);} subrow {
set valid_sha1($subrow(file_sha1)) 1
}
}
foreach file [glob -nocomplain -tails -directory $::appfs::cachedir {[0-9a-f][0-9a-f]/*/*/*/*}] {
set sha1 [string map [list "/" "" "\\" ""] $file]
set file [file join $::appfs::cachedir $file]
if {[info exists valid_sha1($sha1)]} {
continue
}
puts "Cleaning $file"
file delete -force -- $file
}
_EOF_
)"
}
function clear() {
local package
package="$1"
if [ -n "${package}" ]; then
echo "not implemented" >&2
return 1
fi
call_appfsd --tcl 'file delete -force -- {*}[glob -directory $::appfs::cachedir {[0-9a-f][0-9a-f]}]' || return 1
call_appfsd --sqlite3 'DELETE FROM sites; DELETE FROM packages; DELETE FROM files; VACUUM;' || return 1
}
function install() {
local site packages
local package packagedir
local includeLib
includeLib='0'
if [ "$1" = '-lib' ]; then
shift
includeLib='1'
fi
site="$1"
shift
packages=("$@")
if [ -z "${site}" -o -z "${packages[*]}" ]; then
echo "usage: appfs-cache install <site> <package>..." >&2
return 1
fi
for package in "${packages[@]}"; do
packagedir="/opt/appfs/${site}/${package}/platform/latest"
## XXX:TODO: Allow installation to other locations
ln -fs "${packagedir}"/bin/* /bin/
if [ "${includeLib}" = '1' ]; then
ln -fs "${packagedir}"/lib/* /lib/
fi
done
return 0
}
case "$1" in
install)
shift
install "$@" || exit 1
exit 0
;;
invalidate)
invalidate || exit 1
;;
remove-site)
remove_site "$2" || exit 1
;;
clean)
clean || exit 1
;;
clear)
clear "$2" || exit 1
;;
*)
echo "Usage: appfs-cache {invalidate|clean|clear|clear <package>|remove-site <site>}" >&2
exit 1
;;
esac
pkill -HUP appfsd
exit 0