Check-in [1ef1b92a15]
Overview
SHA1:1ef1b92a154dc54575d27fc0afc74e132ee040a5
Date: 2014-09-07 10:38:27
User: rkeene
Comment:Updated to use Tcl to do all the heavy lifting
Timelines: family | ancestors | descendants | both | trunk
Downloads: Tarball | ZIP archive
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2014-09-07
10:39
[7bb4db9baa] Fixed typo (user: rkeene, tags: trunk)
10:38
[1ef1b92a15] Updated to use Tcl to do all the heavy lifting (user: rkeene, tags: trunk)
07:07
[4ff216889c] Updated with basic documentation (user: rkeene, tags: trunk)
Changes

Modified .fossil-settings/ignore-glob from [1fd80210c8] to [c1734c03ec].

     1      1   appfs
     2      2   appfs.o
            3  +appfs-test
            4  +appfs-test.o
            5  +appfs.tcl.h

Modified Makefile from [6ff0798462] to [ca54464a99].

     1      1   CC = gcc
     2      2   PKG_CONFIG = pkg-config
     3         -CFLAGS = $(shell $(PKG_CONFIG) --cflags fuse)
     4         -LIBS = $(shell $(PKG_CONFIG) --libs fuse)
            3  +TCL_CFLAGS =
            4  +TCL_LDFLAGS =
            5  +TCL_LIBS = -ltcl
            6  +CFLAGS = -Wall -g3 $(shell $(PKG_CONFIG) --cflags fuse) $(TCL_CFLAGS)
            7  +LDFLAGS = $(TCL_LDFLAGS)
            8  +LIBS = $(shell $(PKG_CONFIG) --libs fuse) $(TCL_LIBS)
     5      9   PREFIX = /usr/local
     6     10   prefix = $(PREFIX)
     7     11   bindir = $(prefix)/bin
     8     12   
     9     13   all: appfs
    10     14   
    11     15   appfs: appfs.o
    12     16   	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfs appfs.o $(LIBS)
    13     17   
    14         -appfs.o: appfs.c
           18  +appfs-test: appfs-test.o
           19  +	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfs-test appfs-test.o $(LIBS)
           20  +
           21  +appfs.o: appfs.c appfs.tcl.h
           22  +	$(CC) $(CPPFLAGS) $(CFLAGS) -o appfs.o -c appfs.c
           23  +
           24  +appfs-test.o: appfs.c appfs.tcl.h
           25  +	$(CC) $(CPPFLAGS) $(CFLAGS) -DAPPFS_TEST_DRIVER=1 -o appfs-test.o -c appfs.c
           26  +
           27  +appfs.tcl.h: appfs.tcl stringify.tcl
           28  +	./stringify.tcl appfs.tcl > appfs.tcl.h.new
           29  +	mv appfs.tcl.h.new appfs.tcl.h
    15     30   
    16     31   install: appfs
    17     32   	cp appfs $(bindir)
    18     33   
           34  +test: appfs-test
           35  +	./appfs-test
           36  +
    19     37   clean:
    20     38   	rm -f appfs appfs.o
           39  +	rm -f appfs-test appfs-test.o
           40  +	rm -f appfs.tcl.h
    21     41   
    22     42   distclean: clean
    23     43   
    24         -.PHONY: all clean distclean install
           44  +.PHONY: all test clean distclean install

Modified README.md from [e24612c7fa] to [ab12e62b97].

     5      5   
     6      6   Paths
     7      7   -----
     8      8   AppFS should normally be mounted on "/opt/appfs".
     9      9   
    10     10   /opt/appfs/hostname
    11     11   	Fetches: http://hostname/appfs/index
    12         -	Contains CSV file: type,extraData
    13         -		type == package; extraData = package,version,os,cpuArch,sha1
    14         -		type == latest; extradata = package,version,os,cpuArch
           12  +	Contains CSV file: hash,extraData
           13  +
           14  +	Fetches: http://hostname/appfs/sha1/<hash>
           15  +	Contains CSV file: package,version,os,cpuArch,sha1,isLatest
    15     16   
    16     17   /opt/appfs/hostname/package/os-cpuArch/version
    17     18   /opt/appfs/hostname/sha1/
    18         -	Fetches: http://hostname/appfs/<sha1>
           19  +	Fetches: http://hostname/appfs/sha1/<sha1>
    19     20   	Contains CSV file:
    20     21   		type,time,extraData,name
    21     22   		type == directory; extraData = (null)
    22     23   		type == symlink; extraData = source
    23     24   		type == file; extraData = size,sha1
    24     25   
    25     26   /opt/appfs/hostname/{sha1,package/os-cpuArch/version}/file
    26         -	Fetches: http://hostname/appfs/<sha1>
           27  +	Fetches: http://hostname/appfs/sha1/<sha1>
    27     28   

Modified appfs.c from [566a5d022c] to [8303f7df05].

     1      1   #define FUSE_USE_VERSION 26
     2      2   
     3         -#include <fuse.h>
            3  +#include <string.h>
     4      4   #include <errno.h>
     5         -#include <string.h>
     6      5   #include <fcntl.h>
            6  +#include <stdio.h>
            7  +#include <fuse.h>
            8  +#include <tcl.h>
            9  +
           10  +#define APPFS_DEBUG(x...) { fprintf(stderr, "%i:%s: ", __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }
           11  +
           12  +Tcl_Interp *interp;
           13  +
           14  +typedef enum {
           15  +	APPFS_OS_UNKNOWN,
           16  +	APPFS_OS_ALL,
           17  +	APPFS_OS_LINUX,
           18  +	APPFS_OS_MACOSX,
           19  +	APPFS_OS_FREEBSD,
           20  +	APPFS_OS_OPENBSD,
           21  +	APPFS_OS_SOLARIS
           22  +} appfs_os_t;
           23  +
           24  +typedef enum {
           25  +	APPFS_CPU_UNKNOWN,
           26  +	APPFS_CPU_ALL,
           27  +	APPFS_CPU_AMD64,
           28  +	APPFS_CPU_I386,
           29  +	APPFS_CPU_ARM
           30  +} appfs_cpuArch_t;
           31  +
           32  +struct appfs_package {
           33  +	char name[128];
           34  +	char version[64];
           35  +	appfs_os_t os;
           36  +	appfs_cpuArch_t cpuArch;
           37  +	int isLatest;
           38  +};
           39  +
           40  +static appfs_os_t appfs_convert_os_fromString(const char *os) {
           41  +	if (strcasecmp(os, "Linux") == 0) {
           42  +		return(APPFS_OS_LINUX);
           43  +	}
           44  +
           45  +	if (strcasecmp(os, "Darwin") == 0 || strcasecmp(os, "Mac OS") == 0 || strcasecmp(os, "Mac OS X") == 0) {
           46  +		return(APPFS_OS_MACOSX);
           47  +	}
           48  +
           49  +	if (strcasecmp(os, "noarch") == 0) {
           50  +		return(APPFS_OS_ALL);
           51  +	}
           52  +
           53  +	return(APPFS_OS_UNKNOWN);
           54  +}
           55  +
           56  +static const char *appfs_convert_os_toString(appfs_os_t os) {
           57  +	switch (os) {
           58  +		case APPFS_OS_ALL:
           59  +			return("noarch");
           60  +		case APPFS_OS_LINUX:
           61  +			return("linux");
           62  +		case APPFS_OS_MACOSX:
           63  +			return("macosx");
           64  +		case APPFS_OS_FREEBSD:
           65  +			return("freebsd");
           66  +		case APPFS_OS_OPENBSD:
           67  +			return("openbsd");
           68  +		case APPFS_OS_SOLARIS:
           69  +			return("freebsd");
           70  +		case APPFS_CPU_UNKNOWN:
           71  +			return("unknown");
           72  +	}
           73  +
           74  +	return("unknown");
           75  +}
           76  +
           77  +static appfs_cpuArch_t appfs_convert_cpu_fromString(const char *cpu) {
           78  +	if (strcasecmp(cpu, "amd64") == 0 || strcasecmp(cpu, "x86_64") == 0) {
           79  +		return(APPFS_CPU_AMD64);
           80  +	}
           81  +
           82  +	if (strcasecmp(cpu, "i386") == 0 || \
           83  +	    strcasecmp(cpu, "i486") == 0 || \
           84  +	    strcasecmp(cpu, "i586") == 0 || \
           85  +	    strcasecmp(cpu, "i686") == 0 || \
           86  +	    strcasecmp(cpu, "ix86") == 0) {
           87  +		return(APPFS_CPU_I386);
           88  +	}
           89  +
           90  +	if (strcasecmp(cpu, "arm") == 0) {
           91  +		return(APPFS_CPU_ARM);
           92  +	}
           93  +
           94  +	if (strcasecmp(cpu, "noarch") == 0) {
           95  +		return(APPFS_CPU_ALL);
           96  +	}
           97  +
           98  +	return(APPFS_CPU_UNKNOWN);
           99  +}
          100  +
          101  +static const char *appfs_convert_cpu_toString(appfs_cpuArch_t cpu) {
          102  +	switch (cpu) {
          103  +		case APPFS_CPU_ALL:
          104  +			return("noarch");
          105  +		case APPFS_CPU_AMD64:
          106  +			return("amd64");
          107  +		case APPFS_CPU_I386:
          108  +			return("ix86");
          109  +		case APPFS_CPU_ARM:
          110  +			return("arm");
          111  +		case APPFS_CPU_UNKNOWN:
          112  +			return("unknown");
          113  +	}
          114  +
          115  +	return("unknown");
          116  +}
          117  +
          118  +static struct appfs_package *appfs_getindex(const char *hostname, int *package_count_p) {
          119  +	Tcl_Obj *objv[2];
          120  +	int tcl_ret;
          121  +
          122  +	if (package_count_p == NULL) {
          123  +		return(NULL);
          124  +	}
          125  +
          126  +	objv[0] = Tcl_NewStringObj("::appfs::getindex", -1);
          127  +	objv[1] = Tcl_NewStringObj(hostname, -1);
          128  +
          129  +	tcl_ret = Tcl_EvalObjv(interp, 2, &objv, 0);
          130  +	if (tcl_ret != TCL_OK) {
          131  +		APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(interp));
          132  +
          133  +		return(NULL);
          134  +	}
          135  +
          136  +	printf("result: %s\n", Tcl_GetStringResult(interp));
          137  +
          138  +	return(NULL);
          139  +}
          140  +
          141  +static int appfs_getfile(const char *hostname, const char *sha1) {
          142  +}
          143  +
          144  +static int appfs_getmanifest(const char *hostname, const char *sha1) {
          145  +}
     7    146   
     8    147   static int appfs_getattr(const char *path, struct stat *stbuf) {
     9    148   	int res = 0;
    10    149   
          150  +	APPFS_DEBUG("Enter (path = %s, ...)", path);
          151  +
    11    152   	memset(stbuf, 0, sizeof(struct stat));
    12         -	if (strcmp(path, "/") == 0) {
    13         -		stbuf->st_mode = S_IFDIR | 0755;
    14         -		stbuf->st_nlink = 2;
    15         -	} else {
    16         -		res = -ENOENT;
    17         -	}
          153  +
          154  +	stbuf->st_mode = S_IFDIR | 0755;
          155  +	stbuf->st_nlink = 2;
    18    156   
    19    157   	return res;
    20    158   }
    21    159   
    22    160   static int appfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
    23         -	if (strcmp(path, "/") != 0) {
    24         -		return(-ENOENT);
    25         -	}
          161  +	APPFS_DEBUG("Enter (path = %s, ...)", path);
    26    162   
    27    163   	filler(buf, ".", NULL, 0);
    28    164   	filler(buf, "..", NULL, 0);
    29    165   
    30    166   	return 0;
    31    167   }
    32    168   
    33    169   static int appfs_open(const char *path, struct fuse_file_info *fi) {
    34    170   	return(-ENOENT);
    35         -
    36         -	if ((fi->flags & 3) != O_RDONLY)
    37         -		return -EACCES;
    38         -
    39         -	return 0;
    40    171   }
    41    172   
    42    173   static int appfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
    43    174   	return(-ENOENT);
    44    175   }
          176  +
          177  +#ifdef APPFS_TEST_DRIVER
          178  +static int appfs_test_driver(void) {
          179  +	struct appfs_package *packages;
          180  +	int packages_count = 0;
          181  +
          182  +	packages = appfs_getindex("rkeene.org", &packages_count);
          183  +	if (packages == NULL || packages_count == 0) {
          184  +		fprintf(stderr, "Unable to fetch package index from rkeene.org.\n");
          185  +
          186  +		return(1);
          187  +	}
          188  +
          189  +	return(0);
          190  +}
          191  +#endif
    45    192   
    46    193   static struct fuse_operations appfs_oper = {
    47    194   	.getattr	= appfs_getattr,
    48    195   	.readdir	= appfs_readdir,
    49    196   	.open		= appfs_open,
    50    197   	.read		= appfs_read,
    51    198   };
    52    199   
    53    200   int main(int argc, char **argv) {
          201  +	int tcl_ret;
          202  +
          203  +	interp = Tcl_CreateInterp();
          204  +	if (interp == NULL) {
          205  +		fprintf(stderr, "Unable to create Tcl Interpreter.  Aborting.\n");
          206  +
          207  +		return(1);
          208  +	}
          209  +
          210  +	tcl_ret = Tcl_Init(interp);
          211  +	if (tcl_ret != TCL_OK) {
          212  +		fprintf(stderr, "Unable to initialize Tcl.  Aborting.\n");
          213  +
          214  +		return(1);
          215  +	}
          216  +
          217  +	tcl_ret = Tcl_Eval(interp, ""
          218  +#include "appfs.tcl.h"
          219  +	"");
          220  +	if (tcl_ret != TCL_OK) {
          221  +		fprintf(stderr, "Unable to initialize Tcl AppFS Script.  Aborting.\n");
          222  +
          223  +		return(1);
          224  +	}
          225  +
          226  +#ifdef APPFS_TEST_DRIVER
          227  +	return(appfs_test_driver());
          228  +#else
    54    229   	return(fuse_main(argc, argv, &appfs_oper, NULL));
          230  +#endif
    55    231   }
    56    232    

Added appfs.tcl version [b32246b25e].

            1  +#! /usr/bin/env tclsh
            2  +
            3  +package require http
            4  +
            5  +namespace eval ::appfs {
            6  +	variable sites [list]
            7  +	variable cachedir "/tmp/appfs-cache"
            8  +
            9  +	proc _hash_sep {hash {seps 4}} {
           10  +		for {set idx 0} {$idx < $seps} {incr idx} {
           11  +			append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/"
           12  +		}
           13  +		append retval "[string range $hash [expr {$idx * 2}] end]"
           14  +
           15  +		return $retval
           16  +	}
           17  +
           18  +	proc _cachefile {url key {keyIsHash 1}} {
           19  +		if {$keyIsHash} {
           20  +			set key [_hash_sep $key]
           21  +		}
           22  +
           23  +		set file [file join $::appfs::cachedir $key]
           24  +
           25  +		file mkdir -- [file dirname $file]
           26  +
           27  +		if {![file exists $file]} {
           28  +			set tmpfile "${file}.new"
           29  +
           30  +			set fd [open $tmpfile "w"]
           31  +
           32  +			set token [::http::geturl $url -channel $fd]
           33  +			set ncode [::http::ncode $token]
           34  +			::http::reset $token
           35  +			close $fd
           36  +
           37  +			if {$ncode == "200"} {
           38  +				file rename -force -- $tmpfile $file
           39  +			} else {
           40  +				file delete -force -- $tmpfile
           41  +			}
           42  +		}
           43  +
           44  +		return $file
           45  +	}
           46  +
           47  +	proc getindex {hostname} {
           48  +		if {[string match "*\[/~\]*" $hostname]} {
           49  +			return -code error "Invalid hostname"
           50  +		}
           51  +
           52  +		set url "http://$hostname/appfs/index"
           53  +
           54  +		set indexcachefile [_cachefile $url "SERVERS/[string tolower $hostname]" 0]
           55  +
           56  +		if {![file exists $indexcachefile]} {
           57  +			return -code error "Unable to fetch $url"
           58  +		}
           59  +
           60  +		set fd [open $indexcachefile]
           61  +		gets $fd indexhash_data
           62  +		set indexhash [lindex [split $indexhash_data ","] 0]
           63  +		close $fd
           64  +
           65  +		set file [download $hostname $indexhash]
           66  +		set fd [open $file]
           67  +		set data [read $fd]
           68  +		close $fd
           69  +
           70  +		array set packages [list]
           71  +		foreach line [split $data "\n"] {
           72  +			set line [string trim $line]
           73  +
           74  +			if {[string match "*/*" $line]} {
           75  +				continue
           76  +			}
           77  +
           78  +			if {$line == ""} {
           79  +				continue
           80  +			}
           81  +
           82  +			set work [split $line ","]
           83  +
           84  +			unset -nocomplain pkgInfo
           85  +			set pkgInfo(package)  [lindex $work 0]
           86  +			set pkgInfo(version)  [lindex $work 1]
           87  +			set pkgInfo(os)       [lindex $work 2]
           88  +			set pkgInfo(cpuArch)  [lindex $work 3]
           89  +			set pkgInfo(hash)     [string tolower [lindex $work 4]]
           90  +			set pkgInfo(hash_type) "sha1"
           91  +			set pkgInfo(isLatest) [expr {!![lindex $work 5]}]
           92  +
           93  +			if {[string length $pkgInfo(hash)] != 40} {
           94  +				continue
           95  +			}
           96  +
           97  +			if {![regexp {^[0-9a-f]*$} $pkgInfo(hash)]} {
           98  +				continue
           99  +			}
          100  +
          101  +			set packages($pkgInfo(package)) [array get pkgInfo]
          102  +		}
          103  +
          104  +		return [array get packages]
          105  +	}
          106  +
          107  +	proc download {hostname hash {method sha1}} {
          108  +		set url "http://$hostname/appfs/$method/$hash"
          109  +		set file [_cachefile $url $hash]
          110  +
          111  +		if {![file exists $file]} {
          112  +			return -code error "Unable to fetch"
          113  +		}
          114  +
          115  +		return $file
          116  +	}
          117  +}

Added stringify.tcl version [07e25e6903].

            1  +#! /usr/bin/env tclsh
            2  +
            3  +proc stringifyfile {filename {key 0}} {
            4  +	catch {
            5  +		set fd [open $filename r]
            6  +	}
            7  +
            8  +	if {![info exists fd]} {
            9  +		return ""
           10  +	}
           11  +
           12  +	set data [read -nonewline $fd]
           13  +	close $fd
           14  +
           15  +	foreach line [split $data \n] {
           16  +		set line [string map [list "\\" "\\\\" "\"" "\\\""] $line]
           17  +		append ret "	\"$line\\n\"\n"
           18  +	}
           19  +
           20  +	return $ret
           21  +}
           22  +
           23  +foreach file $argv {
           24  +	puts -nonewline [stringifyfile $file]
           25  +}
           26  +
           27  +exit 0