Index: README.md ================================================================== --- README.md +++ README.md @@ -25,7 +25,7 @@ /opt/appfs/hostname/{sha1,package/os-cpuArch/version}/file Fetches: http://hostname/appfs/sha1/ Database -------- - hostname_to_packages(hostname, sha1, package, version, os, cpuArch, isLatest) - package_to_files(package_sha1, type, time, source, size, file_sha1, name) + packages(hostname, sha1, package, version, os, cpuArch, isLatest) + files(package_sha1, type, time, source, size, file_sha1, file_name, file_directory) Index: appfs.c ================================================================== --- appfs.c +++ appfs.c @@ -1,8 +1,9 @@ #define FUSE_USE_VERSION 26 #include +#include #include #include #include #include #include @@ -112,32 +113,49 @@ return("unknown"); } return("unknown"); } + +static int appfs_Tcl_Eval(Tcl_Interp *interp, int objc, const char *cmd, ...) { + Tcl_Obj **objv; + const char *arg; + va_list argp; + int retval; + int i; + + objv = ckalloc(sizeof(*objv) * objc); + objv[0] = Tcl_NewStringObj(cmd, -1); + + va_start(argp, cmd); + for (i = 1; i < objc; i++) { + arg = va_arg(argp, const char *); + objv[i] = Tcl_NewStringObj(arg, -1); + } + va_end(argp); + + retval = Tcl_EvalObjv(interp, objc, objv, 0); + + ckfree(objv); + + return(retval); +} static struct appfs_package *appfs_getindex(const char *hostname, int *package_count_p) { - Tcl_Obj *objv[2]; - Tcl_Obj *packages_tcl; int tcl_ret; if (package_count_p == NULL) { return(NULL); } - objv[0] = Tcl_NewStringObj("::appfs::getindex", -1); - objv[1] = Tcl_NewStringObj(hostname, -1); - - tcl_ret = Tcl_EvalObjv(interp, 2, objv, 0); + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getindex", hostname); if (tcl_ret != TCL_OK) { APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(interp)); return(NULL); } - packages_tcl = Tcl_GetObjResult(interp); - return(NULL); } static int appfs_getfile(const char *hostname, const char *sha1) { } @@ -217,11 +235,19 @@ tcl_ret = Tcl_Eval(interp, "" #include "appfs.tcl.h" ""); if (tcl_ret != TCL_OK) { - fprintf(stderr, "Unable to initialize Tcl AppFS Script. Aborting.\n"); + fprintf(stderr, "Unable to initialize Tcl AppFS script. Aborting.\n"); + + return(1); + } + + tcl_ret = appfs_Tcl_Eval(interp, 1, "::appfs::init"); + if (tcl_ret != TCL_OK) { + fprintf(stderr, "Unable to initialize Tcl AppFS script (::appfs::init). Aborting.\n"); + fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); return(1); } #ifdef APPFS_TEST_DRIVER Index: appfs.tcl ================================================================== --- appfs.tcl +++ appfs.tcl @@ -2,11 +2,10 @@ package require http 2.7 package require sqlite3 namespace eval ::appfs { - variable sites [list] variable cachedir "/tmp/appfs-cache" proc _hash_sep {hash {seps 4}} { for {set idx 0} {$idx < $seps} {incr idx} { append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/" @@ -42,10 +41,31 @@ } } return $file } + + proc _db {args} { + return [uplevel 1 [list ::appfs::db {*}$args]] + } + + proc init {} { + if {[info exists ::appfs::init_called]} { + return + } + + set ::appfs::init_called 1 + + if {![info exists ::appfs::db]} { + file mkdir $::appfs::cachedir + + sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db] + } + + _db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest);} + _db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, file_sha1, file_name, file_directory);} + } proc getindex {hostname} { if {[string match "*\[/~\]*" $hostname]} { return -code error "Invalid hostname" } @@ -98,13 +118,66 @@ if {![regexp {^[0-9a-f]*$} $pkgInfo(hash)]} { continue } set packages($pkgInfo(package)) [array get pkgInfo] + + # Do not do any additional work if we already have this package + set existing_packages [_db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}] + if {[lsearch -exact $existing_packages $pkgInfo(package)] != -1} { + continue + } + + if {$pkgInfo(isLatest)} { + _db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);} + } + + _db eval {INSERT INTO packages (hostname, sha1, package, version, os, cpuArch, isLatest) VALUES ($hostname, $pkgInfo(hash), $pkgInfo(package), $pkgInfo(version), $pkgInfo(os), $pkgInfo(cpuArch), $pkgInfo(isLatest) );} + + set file [download $hostname $pkgInfo(hash)] + set fd [open $file] + set pkgdata [read $fd] + close $fd + + foreach line [split $pkgdata "\n"] { + set line [string trim $line] + + if {[string match "*/*" $line]} { + continue + } + + if {$line == ""} { + continue + } + + set work [split $line ","] + + unset -nocomplain fileInfo + set fileInfo(type) [lindex $work 0] + set fileInfo(time) [lindex $work 1] + set fileInfo(name) [lindex $work end] + + set fileInfo(name) [split [string trim $fileInfo(name) "/"] "/"] + set fileInfo(directory) [join [lrange $fileInfo(name) 0 end-1] "/"] + set fileInfo(name) [lindex $fileInfo(name) end] + + set work [lrange $work 2 end-1] + switch -- $fileInfo(type) { + "file" { + set fileInfo(size) [lindex $work 0] + set fileInfo(sha1) [lindex $work 1] + } + "symlink" { + set fileInfo(source) [lindex $work 0] + } + } + + _db eval {INSERT INTO files (package_sha1, type, time, source, size, file_sha1, file_name, file_directory) VALUES ($pkgInfo(hash), $fileInfo(type), $fileInfo(time), $fileInfo(source), $fileInfo(size), $fileInfo(sha1), $fileInfo(name), $fileInfo(directory) );} + } } - return [array get packages] + return COMPLETE } proc download {hostname hash {method sha1}} { set url "http://$hostname/appfs/$method/$hash" set file [_cachefile $url $hash]