ADDED appfs-cache Index: appfs-cache ================================================================== --- /dev/null +++ appfs-cache @@ -0,0 +1,18 @@ +#! /usr/bin/env bash + +case "$1" in + invalidate) + statement='UPDATE sites SET ttl = "0";' + ;; + clear) + rm -rf + statement='DELETE FROM sites; DELETE FROM packages; DELETE FROM files;' + ;; + *) + echo "Usage: appfs-cache {invalidate|clear}" >&2 + + exit 1 + ;; +esac + +exec appfsd -sqlite3 "${statement}" Index: appfsd.c ================================================================== --- appfsd.c +++ appfsd.c @@ -71,12 +71,13 @@ char source[256]; } symlink; } typeinfo; }; -static Tcl_Interp *appfs_create_TclInterp(const char *cachedir) { +static Tcl_Interp *appfs_create_TclInterp(void) { Tcl_Interp *interp; + const char *cachedir = globalThread.cachedir; int tcl_ret; APPFS_DEBUG("Creating new Tcl interpreter for TID = 0x%llx", (unsigned long long) pthread_self()); interp = Tcl_CreateInterp(); @@ -199,11 +200,11 @@ APPFS_DEBUG("Enter: hostname = %s", hostname); interp = pthread_getspecific(interpKey); if (interp == NULL) { - interp = appfs_create_TclInterp(globalThread.cachedir); + interp = appfs_create_TclInterp(); if (interp == NULL) { return; } @@ -225,11 +226,11 @@ char *retval; int tcl_ret; interp = pthread_getspecific(interpKey); if (interp == NULL) { - interp = appfs_create_TclInterp(globalThread.cachedir); + interp = appfs_create_TclInterp(); if (interp == NULL) { return(NULL); } @@ -252,11 +253,11 @@ Tcl_Interp *interp; int tcl_ret; interp = pthread_getspecific(interpKey); if (interp == NULL) { - interp = appfs_create_TclInterp(globalThread.cachedir); + interp = appfs_create_TclInterp(); if (interp == NULL) { return; } @@ -381,10 +382,36 @@ } /* Get information about a path, and optionally list children */ static int appfs_get_path_info(const char *_path, struct appfs_pathinfo *pathinfo, struct appfs_children **children) { } + +static int appfs_fuse_readlink(const char *path, char *buf, size_t size) { + struct appfs_pathinfo pathinfo; + int res = 0; + + APPFS_DEBUG("Enter (path = %s, ...)", path); + + pathinfo.type = APPFS_PATHTYPE_INVALID; + + res = appfs_get_path_info(path, &pathinfo, NULL); + if (res != 0) { + return(res); + } + + if (pathinfo.type != APPFS_PATHTYPE_SYMLINK) { + return(-EINVAL); + } + + if ((strlen(pathinfo.typeinfo.symlink.source) + 1) > size) { + return(-ENAMETOOLONG); + } + + memcpy(buf, pathinfo.typeinfo.symlink.source, strlen(pathinfo.typeinfo.symlink.source) + 1); + + return(0); +} static int appfs_fuse_getattr(const char *path, struct stat *stbuf) { struct appfs_pathinfo pathinfo; int res = 0; @@ -457,11 +484,11 @@ for (child = children; child; child = child->_next) { filler(buf, child->name, NULL, 0); } - appfs_free_list_children(children); +// appfs_free_list_children(children); return(0); } static int appfs_fuse_open(const char *path, struct fuse_file_info *fi) { @@ -469,10 +496,12 @@ const char *real_path; int fh; int gpi_ret; APPFS_DEBUG("Enter (path = %s, ...)", path); + +#if 0 if ((fi->flags & 3) != O_RDONLY) { return(-EACCES); } @@ -495,10 +524,11 @@ if (fh < 0) { return(-EIO); } fi->fh = fh; +#endif return(0); } static int appfs_fuse_close(const char *path, struct fuse_file_info *fi) { @@ -526,20 +556,37 @@ read_ret = read(fi->fh, buf, size); return(read_ret); } -static struct fuse_operations appfs_oper = { - .getattr = appfs_fuse_getattr, - .readdir = appfs_fuse_readdir, - .readlink = appfs_fuse_readlink, - .open = appfs_fuse_open, - .release = appfs_fuse_close, - .read = appfs_fuse_read -}; - -int Appfsd_Init(Tcl_Interp *interp) { +static int appfs_sqlite3(const char *sql) { + Tcl_Interp *interp; + const char *sql_ret; + int tcl_ret; + + interp = appfs_create_TclInterp(); + if (interp == NULL) { + fprintf(stderr, "Unable to create a Tcl interpreter. Aborting.\n"); + + return(1); + } + + tcl_ret = appfs_Tcl_Eval(interp, 5, "::appfs::db", "eval", sql, "row", "unset -nocomplain row(*); parray row; puts \"----\""); + sql_ret = Tcl_GetStringResult(interp); + + if (tcl_ret != TCL_OK) { + fprintf(stderr, "[error] %s\n", sql_ret); + + return(1); + } + + printf("%s\n", sql_ret); + + return(0); +} + +static int Appfsd_Init(Tcl_Interp *interp) { #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) { return(TCL_ERROR); } #endif @@ -548,15 +595,23 @@ Tcl_PkgProvide(interp, "appfsd", "1.0"); return(TCL_OK); } + +static struct fuse_operations appfs_oper = { + .getattr = appfs_fuse_getattr, + .readdir = appfs_fuse_readdir, + .readlink = appfs_fuse_readlink, + .open = appfs_fuse_open, + .release = appfs_fuse_close, + .read = appfs_fuse_read +}; int main(int argc, char **argv) { const char *cachedir = APPFS_CACHEDIR; - char dbfilename[1024]; - int pthread_ret, snprintf_ret; + int pthread_ret; globalThread.cachedir = cachedir; globalThread.boottime = time(NULL); globalThread.options.writable = 1; @@ -568,15 +623,12 @@ fprintf(stderr, "Unable to create TSD key for Tcl. Aborting.\n"); return(1); } - snprintf_ret = snprintf(dbfilename, sizeof(dbfilename), "%s/%s", cachedir, "cache.db"); - if (snprintf_ret >= sizeof(dbfilename)) { - fprintf(stderr, "Unable to set database filename. Aborting.\n"); - - return(1); + if (argc == 3 && strcmp(argv[1], "-sqlite3") == 0) { + return(appfs_sqlite3(argv[2])); } return(fuse_main(argc, argv, &appfs_oper, NULL)); } Index: appfsd.tcl ================================================================== --- appfsd.tcl +++ appfsd.tcl @@ -78,14 +78,10 @@ } return true } - proc _db {args} { - return [uplevel 1 [list ::appfs::db {*}$args]] - } - proc _normalizeOS {os} { set os [string tolower [string trim $os]] switch -- $os { "linux" - "freebsd" - "openbsd" - "netbsd" { @@ -122,10 +118,15 @@ proc init {} { if {[info exists ::appfs::init_called]} { return } + + # Force [parray] to be loaded + catch { + parray does_not_exist + } set ::appfs::init_called 1 if {![info exists ::appfs::db]} { file mkdir $::appfs::cachedir @@ -132,18 +133,18 @@ sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db] } # Create tables - _db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);} - _db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);} - _db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);} + db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);} + db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);} + db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);} # Create indexes - _db eval {CREATE INDEX IF NOT EXISTS sites_index ON sites (hostname);} - _db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, package, version, os, cpuArch);} - _db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} + db eval {CREATE INDEX IF NOT EXISTS sites_index ON sites (hostname);} + db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, package, version, os, cpuArch);} + db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} } proc download {hostname hash {method sha1}} { set url "http://$hostname/appfs/$method/$hash" set file [_cachefile $url $hash] @@ -156,11 +157,11 @@ } proc getindex {hostname} { set now [clock seconds] - set lastUpdates [_db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}] + set lastUpdates [db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}] if {[llength $lastUpdates] == 0} { set lastUpdate 0 set ttl 0 } else { set lastUpdate [lindex $lastUpdates 0] @@ -186,11 +187,11 @@ ::http::cleanup $token } if {![info exists indexhash_data]} { # Cache this result for 60 seconds - _db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);} + db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);} return -code error "Unable to fetch $url" } set indexhash [lindex [split $indexhash_data ","] 0] @@ -236,43 +237,43 @@ } lappend curr_packages $pkgInfo(hash) # 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);}] + 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 {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, haveManifest) VALUES ($hostname, $pkgInfo(hash), $pkgInfo(package), $pkgInfo(version), $pkgInfo(os), $pkgInfo(cpuArch), $pkgInfo(isLatest), 0);} + db eval {INSERT INTO packages (hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest) VALUES ($hostname, $pkgInfo(hash), $pkgInfo(package), $pkgInfo(version), $pkgInfo(os), $pkgInfo(cpuArch), $pkgInfo(isLatest), 0);} } # Look for packages that have been deleted - set found_packages [_db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}] + set found_packages [db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}] foreach package $found_packages { set found_packages_arr($package) 1 } foreach package $curr_packages { unset -nocomplain found_packages_arr($package) } foreach package [array names found_packages_arr] { - _db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;} + db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;} } - _db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);} + db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);} return COMPLETE } proc getpkgmanifest {hostname package_sha1} { - set haveManifests [_db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}] + set haveManifests [db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}] set haveManifest [lindex $haveManifests 0] if {$haveManifest} { return COMPLETE } @@ -284,11 +285,11 @@ set file [download $hostname $package_sha1] set fd [open $file] set pkgdata [read $fd] close $fd - _db transaction { + db transaction { foreach line [split $pkgdata "\n"] { set line [string trim $line] if {$line == ""} { continue @@ -318,13 +319,13 @@ set fileInfo(name) [join $work ","] 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] - _db eval {INSERT INTO files (package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory) VALUES ($package_sha1, $fileInfo(type), $fileInfo(time), $fileInfo(source), $fileInfo(size), $fileInfo(perms), $fileInfo(sha1), $fileInfo(name), $fileInfo(directory) );} - _db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;} + db eval {INSERT INTO files (package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory) VALUES ($package_sha1, $fileInfo(type), $fileInfo(time), $fileInfo(source), $fileInfo(size), $fileInfo(perms), $fileInfo(sha1), $fileInfo(name), $fileInfo(directory) );} + db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;} } } return COMPLETE } }