<commit_before>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	/*
	 * IOCTL commands --- we will commandeer 0x4C ('L')
	 */
	LOOP_SET_CAPACITY = 0x4C07
	LOOP_CHANGE_FD    = 0x4C06
	LOOP_GET_STATUS64 = 0x4C05
	LOOP_SET_STATUS64 = 0x4C04
	LOOP_GET_STATUS   = 0x4C03
	LOOP_SET_STATUS   = 0x4C02
	LOOP_CLR_FD       = 0x4C01
	LOOP_SET_FD       = 0x4C00
	LO_NAME_SIZE      = 64
	LO_KEY_SIZE       = 32
	/* /dev/loop-control interface */
	LOOP_CTL_ADD      = 0x4C80
	LOOP_CTL_REMOVE   = 0x4C81
	LOOP_CTL_GET_FREE = 0x4C82
	SYS_ioctl         = 16
)

//http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/
//The .dep is the name + .dep

var (
	l       = log.New(os.Stdout, "tcz: ", 0)
	host    = flag.String("h", "tinycorelinux.net", "Host name for packages")
	version = flag.String("v", "5.x", "tinycore version")
	arch    = flag.String("a", "x86_64", "tinycore architecture")
	port    = flag.String("p", "80", "Host port")
	tcz     string
)

// consider making this a goroutine which pushes the string down the channel.
func findloop() (name string, err error) {
	cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0)
	if err != nil {
		log.Fatalf("/dev/loop-control: %v", err)
	}
	defer syscall.Close(cfd)
	a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0)
	if errno != 0 {
		log.Fatalf("ioctl: %v\n", err)
	}
	log.Printf("a %v b %v err %v\n", a, b, err)
	name = fmt.Sprintf("/dev/loop%d", a)
	return name, nil
}
func linkone(p string, i os.FileInfo, err error) error {
	l.Printf("symtree: p %v\n", p)
	if err != nil {
		return err
	}

	// the tree of symlinks starts at /tmp/tcloop
	packagel := filepath.SplitList(p)
	// surely there's a better way.
	n := append([]string{"/"}, packagel[2:]...)
	to := path.Join(n...)

	l.Printf("symtree: remove %v\n", to)
	os.Remove(to)
	l.Printf("symtree: symlink %v to %v\n", p, to)
	return os.Symlink(p, to)
}
func clonetree(tree string) error {
	lt := len(tree)
	err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			l.Printf("walking, dir %v\n", path)
			os.MkdirAll(path[lt:], 0700)
			return nil
		}
		// all else gets a symlink.
		l.Printf("Need to symlnk %v to %v\n", path, path)
		os.Symlink(path, path[lt:])
		return nil
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	return err
}

func fetch(p string) error {
	// path.Join doesn't quite work here.
	fullpath := path.Join(tcz, p)
	if _, err := os.Stat(fullpath); err != nil {
		cmd := "http://" + *host + ":" + *port + "/" + fullpath
		l.Printf("Fetch %v\n", cmd)

		resp, err := http.Get(cmd)
		if err != nil {
			l.Fatalf("Get of %v failed: %v\n", cmd, err)
		}
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			l.Printf("%v Not OK! %v\n", cmd, resp.Status)
			return syscall.ENOENT
		}

		l.Printf("resp %v err %v\n", resp, err)
		// we have the whole tcz in resp.Body.
		// First, save it to /tcz/name
		f, err := os.Create(fullpath)
		if err != nil {
			l.Fatal("Create of :%v: failed: %v\n", fullpath, err)
		} else {
			l.Printf("created %v f %v\n", fullpath, f)
		}

		if c, err := io.Copy(f, resp.Body); err != nil {
			l.Fatal(err)
		} else {
			/* OK, these are compressed tars ... */
			l.Printf("c %v err %v\n", c, err)
		}
		f.Close()
	}
	return nil
}

// deps is ALL the packages we need fetched or not
// this may even let us work with parallel tcz, ALMOST
func installPackage(tczName string, deps map[string]bool) error {
	l.Printf("installPackage: %v %v\n", tczName, deps)
	depName := tczName + ".dep"
	// path.Join doesn't quite work here.
	if err := fetch(tczName); err != nil {
		l.Fatal(err)
	}
	deps[tczName] = true
	l.Printf("Fetched %v\n", tczName)
	// now fetch dependencies if any.
	if err := fetch(depName); err == nil {
		l.Printf("Fetched dep ok!\n")
	} else {
		l.Printf("No dep file found\n")
		if err := ioutil.WriteFile(path.Join(tcz, depName), []byte{}, os.FileMode(0444)); err != nil {
			l.Printf("Tried to write Blank file %v, failed %v\n", depName, err)
		}
		return nil
	}
	// read deps file
	deplist, err := ioutil.ReadFile(path.Join(tcz, depName))
	if err != nil {
		l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err)
	}
	l.Printf("deplist for %v is :%v:\n", depName, deplist)
	for _, v := range strings.Split(string(deplist), "\n") {
		// split("name\n") gets you a 2-element array with second
		// element the empty string
		if len(v) == 0 {
			break
		}
		l.Printf("FOR %v get package %v\n", tczName, v)
		if deps[v] {
			continue
		}
		if err := installPackage(v, deps); err != nil {
			return err
		}
	}
	return nil

}

func setupPackages(tczName string, deps map[string]bool) error {
	l.Printf("setupPackages: @ %v deps %v\n", tczName, deps)
	for v := range deps {
		cmdName := strings.Split(v, ".")[0]
		packagePath := path.Join("/tmp/tcloop", cmdName)
		if err := os.MkdirAll(packagePath, 0700); err != nil {
			l.Fatal(err)
		}

		loopname, err := findloop()
		if err != nil {
			l.Fatal(err)
		}
		l.Printf("findloop gets %v err %v\n", loopname, err)
		pkgpath := path.Join(tcz, v)
		ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatal("%v: %v\n", pkgpath, err)
		}
		lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatal("%v: %v\n", loopname, err)
		}
		l.Printf("ffd %v lfd %v\n", ffd, lfd)
		a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd))
		if errno != 0 {
			l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno)
		}
		/* now mount it. The convention is the mount is in /tmp/tcloop/packagename */
		if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil {
			l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, err)
		}
		err = clonetree(packagePath)
		if err != nil {
			l.Fatalf("clonetree:  %v\n", err)
		}
	}
	return nil

}

func main() {
	flag.Parse()
	needPackages := make(map[string]bool)
	tcz = path.Join("/", *version, *arch, "tcz")
	if len(os.Args) < 2 {
		os.Exit(1)
	}
	cmdName := flag.Args()[0]
	tczName := cmdName + ".tcz"

	if err := os.MkdirAll(tcz, 0700); err != nil {
		l.Fatal(err)
	}

	if err := os.MkdirAll("/tmp/tcloop", 0700); err != nil {
		l.Fatal(err)
	}

	// path.Join doesn't quite work here.
	if err := installPackage(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
	l.Printf("After installpackages: needPackages %v\n", needPackages)

	if err := setupPackages(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
}
<commit_msg>tcz: break the absolute mirroring of tinycore package server paths and local cache<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	/*
	 * IOCTL commands --- we will commandeer 0x4C ('L')
	 */
	LOOP_SET_CAPACITY = 0x4C07
	LOOP_CHANGE_FD    = 0x4C06
	LOOP_GET_STATUS64 = 0x4C05
	LOOP_SET_STATUS64 = 0x4C04
	LOOP_GET_STATUS   = 0x4C03
	LOOP_SET_STATUS   = 0x4C02
	LOOP_CLR_FD       = 0x4C01
	LOOP_SET_FD       = 0x4C00
	LO_NAME_SIZE      = 64
	LO_KEY_SIZE       = 32
	/* /dev/loop-control interface */
	LOOP_CTL_ADD      = 0x4C80
	LOOP_CTL_REMOVE   = 0x4C81
	LOOP_CTL_GET_FREE = 0x4C82
	SYS_ioctl         = 16
)

//http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/
//The .dep is the name + .dep

var (
	l       = log.New(os.Stdout, "tcz: ", 0)
	host    = flag.String("h", "tinycorelinux.net", "Host name for packages")
	version = flag.String("v", "5.x", "tinycore version")
	arch    = flag.String("a", "x86_64", "tinycore architecture")
	port    = flag.String("p", "80", "Host port")
	tczServerDir     string
	tczLocalPackageDir string
)

// consider making this a goroutine which pushes the string down the channel.
func findloop() (name string, err error) {
	cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0)
	if err != nil {
		log.Fatalf("/dev/loop-control: %v", err)
	}
	defer syscall.Close(cfd)
	a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0)
	if errno != 0 {
		log.Fatalf("ioctl: %v\n", err)
	}
	log.Printf("a %v b %v err %v\n", a, b, err)
	name = fmt.Sprintf("/dev/loop%d", a)
	return name, nil
}
func linkone(p string, i os.FileInfo, err error) error {
	l.Printf("symtree: p %v\n", p)
	if err != nil {
		return err
	}

	// the tree of symlinks starts at /tmp/tcloop
	packagel := filepath.SplitList(p)
	// surely there's a better way.
	n := append([]string{"/"}, packagel[2:]...)
	to := path.Join(n...)

	l.Printf("symtree: remove %v\n", to)
	os.Remove(to)
	l.Printf("symtree: symlink %v to %v\n", p, to)
	return os.Symlink(p, to)
}
func clonetree(tree string) error {
	lt := len(tree)
	err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			l.Printf("walking, dir %v\n", path)
			os.MkdirAll(path[lt:], 0700)
			return nil
		}
		// all else gets a symlink.
		l.Printf("Need to symlnk %v to %v\n", path, path)
		os.Symlink(path, path[lt:])
		return nil
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	return err
}

func fetch(p string) error {

	fullpath := path.Join(tczLocalPackageDir, p)
	packageName := path.Join(tczServerDir, p)
	if _, err := os.Stat(fullpath); err != nil {
		// path.Join doesn't quite work here. It will try to do file-system-like
		// joins and it ends up remove the // and replacing it with a clash.
		cmd := "http://" + *host + ":" + *port + "/" + packageName
		l.Printf("Fetch %v\n", cmd)

		resp, err := http.Get(cmd)
		if err != nil {
			l.Fatalf("Get of %v failed: %v\n", cmd, err)
		}
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			l.Printf("%v Not OK! %v\n", cmd, resp.Status)
			return syscall.ENOENT
		}

		l.Printf("resp %v err %v\n", resp, err)
		// we have the whole tcz in resp.Body.
		// First, save it to /tcz/name
		f, err := os.Create(fullpath)
		if err != nil {
			l.Fatal("Create of :%v: failed: %v\n", fullpath, err)
		} else {
			l.Printf("created %v f %v\n", fullpath, f)
		}

		if c, err := io.Copy(f, resp.Body); err != nil {
			l.Fatal(err)
		} else {
			/* OK, these are compressed tars ... */
			l.Printf("c %v err %v\n", c, err)
		}
		f.Close()
	}
	return nil
}

// deps is ALL the packages we need fetched or not
// this may even let us work with parallel tcz, ALMOST
func installPackage(tczName string, deps map[string]bool) error {
	l.Printf("installPackage: %v %v\n", tczName, deps)
	depName := tczName + ".dep"
	// path.Join doesn't quite work here.
	if err := fetch(tczName); err != nil {
		l.Fatal(err)
	}
	deps[tczName] = true
	l.Printf("Fetched %v\n", tczName)
	// now fetch dependencies if any.
	if err := fetch(depName); err == nil {
		l.Printf("Fetched dep ok!\n")
	} else {
		l.Printf("No dep file found\n")
		if err := ioutil.WriteFile(path.Join(tczLocalPackageDir, depName), []byte{}, os.FileMode(0444)); err != nil {
			l.Printf("Tried to write Blank file %v, failed %v\n", depName, err)
		}
		return nil
	}
	// read deps file
	deplist, err := ioutil.ReadFile(path.Join(tczLocalPackageDir, depName))
	if err != nil {
		l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err)
	}
	l.Printf("deplist for %v is :%v:\n", depName, deplist)
	for _, v := range strings.Split(string(deplist), "\n") {
		// split("name\n") gets you a 2-element array with second
		// element the empty string
		if len(v) == 0 {
			break
		}
		l.Printf("FOR %v get package %v\n", tczName, v)
		if deps[v] {
			continue
		}
		if err := installPackage(v, deps); err != nil {
			return err
		}
	}
	return nil

}

func setupPackages(tczName string, deps map[string]bool) error {
	l.Printf("setupPackages: @ %v deps %v\n", tczName, deps)
	for v := range deps {
		cmdName := strings.Split(v, ".")[0]
		packagePath := path.Join("/tmp/tcloop", cmdName)
		if err := os.MkdirAll(packagePath, 0700); err != nil {
			l.Fatal(err)
		}

		loopname, err := findloop()
		if err != nil {
			l.Fatal(err)
		}
		l.Printf("findloop gets %v err %v\n", loopname, err)
		pkgpath := path.Join(tczLocalPackageDir, v)
		ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatal("%v: %v\n", pkgpath, err)
		}
		lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatal("%v: %v\n", loopname, err)
		}
		l.Printf("ffd %v lfd %v\n", ffd, lfd)
		a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd))
		if errno != 0 {
			l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno)
		}
		/* now mount it. The convention is the mount is in /tmp/tcloop/packagename */
		if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil {
			l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, err)
		}
		err = clonetree(packagePath)
		if err != nil {
			l.Fatalf("clonetree:  %v\n", err)
		}
	}
	return nil

}

func main() {
	flag.Parse()
	needPackages := make(map[string]bool)
	tczServerDir = path.Join("/", *version, *arch, "tcz")
	tczLocalPackageDir = path.Join("tcz", tczServerDir)
	if len(os.Args) < 2 {
		os.Exit(1)
	}
	cmdName := flag.Args()[0]
	tczName := cmdName + ".tcz"

	if err := os.MkdirAll(tczLocalPackageDir, 0700); err != nil {
		l.Fatal(err)
	}

	if err := os.MkdirAll("/tmp/tcloop", 0700); err != nil {
		l.Fatal(err)
	}

	// path.Join doesn't quite work here.
	if err := installPackage(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
	l.Printf("After installpackages: needPackages %v\n", needPackages)

	if err := setupPackages(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode implementation
type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	var err error

	if len(args) == 0 {
		return nil, errors.New("Incorrect number of arguments. At least one Meter's name is required.")
	}

	for _,name := range args {
		if len(name) == 0{
			continue
		}
		err = stub.PutState( "kwh_" + name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be created")
		}
		err = stub.PutState( name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be created")
		}
	}

	return nil, nil
}

// Deletes an entity from state
func (t *SimpleChaincode) settle(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	//var err error
	var key string
	var val, amount, previous_val int
	var exchange_rate float64

	exchange_rate = 0.1;

	var keys []string
	var values []int

	for i := 1; i < 10; i++ {
		key = strconv.Itoa(i)
		value, err := stub.GetState(key)
		if err != nil {
			continue
		}
		if value == nil {
			continue
		}
		val, _ = strconv.Atoi(string(value))
		keys = append(keys, key)
		values = append(values, val)

	}
	for index,name := range keys {
		amount = int(float64(values[index])*-1*exchange_rate);
		//f := "change"
		//queryArgs := []string{name,string(amount)}
		//_, err := stub.InvokeChaincode("2780b7463c57f343a9e107854c4b53150018cdd8fd74ca970c028de6bfa707f6e9f6cf2b20f0af4fdd04d2167651eb29c7bfabf19e6a93ae2aff65f55202d0e6", f, queryArgs)
		//if err != nil {
		//	errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", err.Error())
		//	fmt.Printf(errStr)
		//	return nil, errors.New(errStr)
		//}
		value, err := stub.GetState(name)
		if err != nil {
			jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
			return nil, errors.New(jsonResp)
		}if(previous_val == nil){
			previous_val = 1
		}

		previous_val, _ = strconv.Atoi(string(value));

		err = stub.PutState(name, []byte(strconv.Itoa(amount + previous_val)))
		//err = stub.PutState(name, []byte(strconv.Itoa(40)))

		if err != nil {
			return nil, err
		}
		err = stub.PutState("kwh_" + name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be updated")
		}
	}

	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {

	if function == "settle" {
		return t.settle(stub, args)
	}

	if function != "report" {
		return nil, errors.New("Unimplemented '" + function + "' invoked")
	}

	var name string    // Entities
	var val int // Asset holdings
	var err error

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2")
	}

	name = args[0]
	val, _ = strconv.Atoi(string(args[1]))

	err = stub.PutState("kwh_" + name, []byte(strconv.Itoa(val)))

	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Query callback representing the query of a chaincode
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {

	if function == "balance" {
		return t.balance(stub, args)
	}

	if function != "reported_kwh" {
		return nil, errors.New("Invalid query function name. Expecting \"querybalance\"")
	}
	var name string // Entities
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the Meter to query")
	}

	name = args[0]

	// Get the state from the ledger
	value, err := stub.GetState("kwh_" + name)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	if value == nil {
		jsonResp := "{\"Error\":\"Nil amount for Meter" + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + name + "\",\"Amount\":\"" + string(value) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return value, nil
}

func (t *SimpleChaincode) balance(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	var name string // Entities
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the Meter to query")
	}

	name = args[0]

	// Get the state from the ledger
	value, err := stub.GetState(name)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	if value == nil {
		jsonResp := "{\"Error\":\"Nil amount for Meter " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + name + "\",\"Amount\":\"" + string(value) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return value, nil
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<commit_msg>type was updated<commit_after>package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode implementation
type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	var err error

	if len(args) == 0 {
		return nil, errors.New("Incorrect number of arguments. At least one Meter's name is required.")
	}

	for _,name := range args {
		if len(name) == 0{
			continue
		}
		err = stub.PutState( "kwh_" + name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be created")
		}
		err = stub.PutState( name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be created")
		}
	}

	return nil, nil
}

// Deletes an entity from state
func (t *SimpleChaincode) settle(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	//var err error
	var key string
	var val, amount, previous_val int
	var exchange_rate float64

	exchange_rate = 0.1;

	var keys []string
	var values []int

	for i := 1; i < 10; i++ {
		key = strconv.Itoa(i)
		value, err := stub.GetState(key)
		if err != nil {
			continue
		}
		if value == nil {
			continue
		}
		val, _ = strconv.Atoi(string(value))
		keys = append(keys, key)
		values = append(values, val)

	}
	for index,name := range keys {
		amount = int(float64(values[index])*-1*exchange_rate);
		//f := "change"
		//queryArgs := []string{name,string(amount)}
		//_, err := stub.InvokeChaincode("2780b7463c57f343a9e107854c4b53150018cdd8fd74ca970c028de6bfa707f6e9f6cf2b20f0af4fdd04d2167651eb29c7bfabf19e6a93ae2aff65f55202d0e6", f, queryArgs)
		//if err != nil {
		//	errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", err.Error())
		//	fmt.Printf(errStr)
		//	return nil, errors.New(errStr)
		//}
		value, err := stub.GetState(name)
		if err != nil {
			jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
			return nil, errors.New(jsonResp)
		}
		if(value == nil){
			previous_val = 0
		}else{
			previous_val, _ = strconv.Atoi(string(value));
		}

		err = stub.PutState(name, []byte(strconv.Itoa(amount + previous_val)))
		//err = stub.PutState(name, []byte(strconv.Itoa(40)))

		if err != nil {
			return nil, err
		}
		err = stub.PutState("kwh_" + name, []byte(strconv.Itoa(0)));
		if err != nil {
			return nil, errors.New("Meter cannot be updated")
		}
	}

	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {

	if function == "settle" {
		return t.settle(stub, args)
	}

	if function != "report" {
		return nil, errors.New("Unimplemented '" + function + "' invoked")
	}

	var name string    // Entities
	var val int // Asset holdings
	var err error

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2")
	}

	name = args[0]
	val, _ = strconv.Atoi(string(args[1]))

	err = stub.PutState("kwh_" + name, []byte(strconv.Itoa(val)))

	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Query callback representing the query of a chaincode
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {

	if function == "balance" {
		return t.balance(stub, args)
	}

	if function != "reported_kwh" {
		return nil, errors.New("Invalid query function name. Expecting \"querybalance\"")
	}
	var name string // Entities
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the Meter to query")
	}

	name = args[0]

	// Get the state from the ledger
	value, err := stub.GetState("kwh_" + name)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	if value == nil {
		jsonResp := "{\"Error\":\"Nil amount for Meter" + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + name + "\",\"Amount\":\"" + string(value) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return value, nil
}

func (t *SimpleChaincode) balance(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	var name string // Entities
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the Meter to query")
	}

	name = args[0]

	// Get the state from the ledger
	value, err := stub.GetState(name)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	if value == nil {
		jsonResp := "{\"Error\":\"Nil amount for Meter " + name + "\"}"
		return nil, errors.New(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + name + "\",\"Amount\":\"" + string(value) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return value, nil
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<|endoftext|>


<commit_before>package hostqueue

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"

	"github.com/sendgrid/sendgrid-go"
)

func sendReminder(group Group, r *http.Request) {
	c := appengine.NewContext(r)

	creds, err := GetCreds(c)
	if err != nil {
		panic(err)
	}

	sg := sendgrid.NewSendGridClient(creds.Username, creds.Pass)
	sg.Client = urlfetch.Client(c)
	email := group.GroupEmail
	hostName := group.Hosts[group.Next].HostName
	c.Infof("Email: %v", email)
	c.Infof("Host Name: %v", hostName)

	subject := "This weeks hosting reminder for " + group.GroupName
	html := hostName + hostMessage
	sendEmail(email, subject, html, r)
}

func sendSkipMessage(group Group, r *http.Request) {
	c := appengine.NewContext(r)

	email := group.GroupEmail
	var buffer bytes.Buffer
	for _, element := range group.Hosts {
		buffer.WriteString(element.HostName)
	}

	hosts := make([]string, len(group.Hosts))
	for i, element := range group.Hosts {
		hosts[i] = element.HostName
	}
	c.Infof("hosts: %s", strings.Join(hosts[:], ","))
	c.Infof("Email: %v", email)

	html := fmt.Sprintf(skipMessage, strings.Join(hosts[:], ", "))
	sendEmail(email, "See you next week", html, r)
}

func sendHostConfirmedMessage(group Group, r *http.Request) {
	c := appengine.NewContext(r)

	email := group.GroupEmail
	var buffer bytes.Buffer
	for _, element := range group.Hosts {
		buffer.WriteString(element.HostName)
	}

	hosts := make([]string, len(group.Hosts))
	for i, element := range group.Hosts {
		hosts[i] = element.HostName
	}
	c.Infof("hosts: %s", strings.Join(hosts[:], ","))
	c.Infof("Email: %v", email)

	html := fmt.Sprintf(confirmedMessage, group.Hosts[len(group.Hosts)-1].HostName, strings.Join(hosts[:], ", "))
	sendEmail(email, "See you next week", html, r)

}

func sendEmail(email string, subject string, html string, r *http.Request) {
	c := appengine.NewContext(r)

	creds, err := GetCreds(c)
	if err != nil {
		panic(err)
	}

	sg := sendgrid.NewSendGridClient(creds.Username, creds.Pass)
	sg.Client = urlfetch.Client(c)

	message := sendgrid.NewMail()
	message.AddTo(email)
	message.SetSubject(subject)
	message.SetHTML(html)
	message.SetFrom(from)

	err = sg.Send(message)
	if err != nil {
		//TODO:  autocreate an issue in github if this is unique in the last month
		c.Infof("Message: %v", message)
		panic(err)
	}
}

/*App engine allows for environment variables, but they are stored in app.yaml
and I don't want my mail creds pushed to a public repo.  */
type EmailCreds struct {
	Username string `json:"username"`
	Pass     string `json:"password"`
}

func GetCreds(ctx appengine.Context) (EmailCreds, error) {
	k := datastore.NewKey(ctx, "EmailCreds", "singleton_creds", 0, nil)
	var ec EmailCreds
	err := datastore.Get(ctx, k, &ec)
	if err != nil {
		return EmailCreds{}, err
	}

	return ec, nil
}

const from = "reminder@hostqueue-1146.appspotmail.com"

const hostMessage = ` it is your turn to host!
Respond with 'yes' to host, 'no' to go to the next in line to host, or 'skip' for everyone to skip this week and host next week.
`

const skipMessage = `See you next week with the following turn order:  %s`

const confirmedMessage = `The %s has agreed to host this week.  The rotation for next week will be: %s`
<commit_msg>added the status link.<commit_after>package hostqueue

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"

	"github.com/sendgrid/sendgrid-go"
)

func sendReminder(group Group, r *http.Request) {
	c := appengine.NewContext(r)

	creds, err := GetCreds(c)
	if err != nil {
		panic(err)
	}

	sg := sendgrid.NewSendGridClient(creds.Username, creds.Pass)
	sg.Client = urlfetch.Client(c)
	email := group.GroupEmail
	hostName := group.Hosts[group.Next].HostName
	c.Infof("Email: %v", email)
	c.Infof("Host Name: %v", hostName)

	subject := "This weeks hosting reminder for " + group.GroupName
	html := hostName + hostMessage
	sendEmail(email, subject, html, r)
}

func sendSkipMessage(group Group, r *http.Request) {
	c := appengine.NewContext(r)

	email := group.GroupEmail
	var buffer bytes.Buffer
	for _, element := range group.Hosts {
		buffer.WriteString(element.HostName)
	}

	hosts := make([]string, len(group.Hosts))
	for i, element := range group.Hosts {
		hosts[i] = element.HostName
	}
	c.Infof("hosts: %s", strings.Join(hosts[:], ","))
	c.Infof("Email: %v", email)

	html := fmt.Sprintf(skipMessage, strings.Join(hosts[:], ", "))
	sendEmail(email, "See you next week", html, r)
}

func sendHostConfirmedMessage(group Group, r *http.Request) {
	c := appengine.NewContext(r)

	email := group.GroupEmail
	var buffer bytes.Buffer
	for _, element := range group.Hosts {
		buffer.WriteString(element.HostName)
	}

	hosts := make([]string, len(group.Hosts))
	for i, element := range group.Hosts {
		hosts[i] = element.HostName
	}
	c.Infof("hosts: %s", strings.Join(hosts[:], ","))
	c.Infof("Email: %v", email)

	html := fmt.Sprintf(confirmedMessage, group.Hosts[len(group.Hosts)-1].HostName, strings.Join(hosts[:], ", "))
	sendEmail(email, "See you next week", html, r)

}

func sendEmail(email string, subject string, html string, r *http.Request) {
	c := appengine.NewContext(r)

	creds, err := GetCreds(c)
	if err != nil {
		panic(err)
	}

	sg := sendgrid.NewSendGridClient(creds.Username, creds.Pass)
	sg.Client = urlfetch.Client(c)

	message := sendgrid.NewMail()
	message.AddTo(email)
	message.SetSubject(subject)
	message.SetHTML(html)
	message.SetFrom(from)

	err = sg.Send(message)
	if err != nil {
		//TODO:  autocreate an issue in github if this is unique in the last month
		c.Infof("Message: %v", message)
		panic(err)
	}
}

/*App engine allows for environment variables, but they are stored in app.yaml
and I don't want my mail creds pushed to a public repo.  */
type EmailCreds struct {
	Username string `json:"username"`
	Pass     string `json:"password"`
}

func GetCreds(ctx appengine.Context) (EmailCreds, error) {
	k := datastore.NewKey(ctx, "EmailCreds", "singleton_creds", 0, nil)
	var ec EmailCreds
	err := datastore.Get(ctx, k, &ec)
	if err != nil {
		return EmailCreds{}, err
	}

	return ec, nil
}

const from = "reminder@hostqueue-1146.appspotmail.com"

const hostMessage = ` it is your turn to host!
Respond with 'yes' to host, 'no' to go to the next in line to host, or 'skip' for everyone to skip this week and host next week.

To get the live rotation status go here:  http://hostqueue-1146.appspot.com/group/status/a2b57006-7df8-444a-842a-dd366cd228bd
`

const skipMessage = `See you next week with the following turn order:  %s
To get the live rotation status go here:  http://hostqueue-1146.appspot.com/group/status/a2b57006-7df8-444a-842a-dd366cd228bd
`

const confirmedMessage = `The %s has agreed to host this week.  The rotation for next week will be: %s

To get the live rotation status go here:  http://hostqueue-1146.appspot.com/group/status/a2b57006-7df8-444a-842a-dd366cd228bd
`
<|endoftext|>


<commit_before>package bitswap

import (
	"sync"
	"time"

	process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
	procctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"

	key "github.com/ipfs/go-ipfs/blocks/key"
	wantlist "github.com/ipfs/go-ipfs/exchange/bitswap/wantlist"
	logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log"
	peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer"
)

var TaskWorkerCount = 8

func (bs *Bitswap) startWorkers(px process.Process, ctx context.Context) {
	// Start up a worker to handle block requests this node is making
	px.Go(func(px process.Process) {
		bs.providerQueryManager(ctx)
	})

	// Start up workers to handle requests from other nodes for the data on this node
	for i := 0; i < TaskWorkerCount; i++ {
		i := i
		px.Go(func(px process.Process) {
			bs.taskWorker(ctx, i)
		})
	}

	// Start up a worker to manage periodically resending our wantlist out to peers
	px.Go(func(px process.Process) {
		bs.rebroadcastWorker(ctx)
	})

	// Start up a worker to manage sending out provides messages
	px.Go(func(px process.Process) {
		bs.provideCollector(ctx)
	})

	// Spawn up multiple workers to handle incoming blocks
	// consider increasing number if providing blocks bottlenecks
	// file transfers
	px.Go(bs.provideWorker)
}

func (bs *Bitswap) taskWorker(ctx context.Context, id int) {
	idmap := logging.LoggableMap{"ID": id}
	defer log.Info("bitswap task worker shutting down...")
	for {
		log.Event(ctx, "Bitswap.TaskWorker.Loop", idmap)
		select {
		case nextEnvelope := <-bs.engine.Outbox():
			select {
			case envelope, ok := <-nextEnvelope:
				if !ok {
					continue
				}
				log.Event(ctx, "Bitswap.TaskWorker.Work", logging.LoggableMap{
					"ID":     id,
					"Target": envelope.Peer.Pretty(),
					"Block":  envelope.Block.Multihash().B58String(),
				})

				bs.wm.SendBlock(ctx, envelope)
			case <-ctx.Done():
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

func (bs *Bitswap) provideWorker(px process.Process) {

	limit := make(chan struct{}, provideWorkerMax)

	limitedGoProvide := func(k key.Key, wid int) {
		defer func() {
			// replace token when done
			<-limit
		}()
		ev := logging.LoggableMap{"ID": wid}

		ctx := procctx.OnClosingContext(px) // derive ctx from px
		defer log.EventBegin(ctx, "Bitswap.ProvideWorker.Work", ev, &k).Done()

		ctx, cancel := context.WithTimeout(ctx, provideTimeout) // timeout ctx
		defer cancel()

		if err := bs.network.Provide(ctx, k); err != nil {
			log.Warning(err)
		}
	}

	// worker spawner, reads from bs.provideKeys until it closes, spawning a
	// _ratelimited_ number of workers to handle each key.
	for wid := 2; ; wid++ {
		ev := logging.LoggableMap{"ID": 1}
		log.Event(procctx.OnClosingContext(px), "Bitswap.ProvideWorker.Loop", ev)

		select {
		case <-px.Closing():
			return
		case k, ok := <-bs.provideKeys:
			if !ok {
				log.Debug("provideKeys channel closed")
				return
			}
			select {
			case <-px.Closing():
				return
			case limit <- struct{}{}:
				go limitedGoProvide(k, wid)
			}
		}
	}
}

func (bs *Bitswap) provideCollector(ctx context.Context) {
	defer close(bs.provideKeys)
	var toProvide []key.Key
	var nextKey key.Key
	var keysOut chan key.Key

	for {
		select {
		case blk, ok := <-bs.newBlocks:
			if !ok {
				log.Debug("newBlocks channel closed")
				return
			}
			if keysOut == nil {
				nextKey = blk.Key()
				keysOut = bs.provideKeys
			} else {
				toProvide = append(toProvide, blk.Key())
			}
		case keysOut <- nextKey:
			if len(toProvide) > 0 {
				nextKey = toProvide[0]
				toProvide = toProvide[1:]
			} else {
				keysOut = nil
			}
		case <-ctx.Done():
			return
		}
	}
}

func (bs *Bitswap) rebroadcastWorker(parent context.Context) {
	ctx, cancel := context.WithCancel(parent)
	defer cancel()

	broadcastSignal := time.NewTicker(rebroadcastDelay.Get())
	defer broadcastSignal.Stop()

	tick := time.NewTicker(10 * time.Second)
	defer tick.Stop()

	for {
		log.Event(ctx, "Bitswap.Rebroadcast.idle")
		select {
		case <-tick.C:
			n := bs.wm.wl.Len()
			if n > 0 {
				log.Debug(n, "keys in bitswap wantlist")
			}
		case <-broadcastSignal.C: // resend unfulfilled wantlist keys
			log.Event(ctx, "Bitswap.Rebroadcast.active")
			for _, e := range bs.wm.wl.Entries() {
				bs.findKeys <- &e
			}
		case <-parent.Done():
			return
		}
	}
}

func (bs *Bitswap) providerQueryManager(ctx context.Context) {
	var activeLk sync.Mutex
	active := make(map[key.Key]*wantlist.Entry)

	for {
		select {
		case e := <-bs.findKeys:
			activeLk.Lock()
			if _, ok := active[e.Key]; ok {
				activeLk.Unlock()
				continue
			}
			active[e.Key] = e
			activeLk.Unlock()

			go func(e *wantlist.Entry) {
				child, cancel := context.WithTimeout(e.Ctx, providerRequestTimeout)
				defer cancel()
				providers := bs.network.FindProvidersAsync(child, e.Key, maxProvidersPerRequest)
				for p := range providers {
					go func(p peer.ID) {
						err := bs.network.ConnectTo(child, p)
						if err != nil {
							log.Debug("failed to connect to provider %s: %s", p, err)
						}
					}(p)
				}
				activeLk.Lock()
				delete(active, e.Key)
				activeLk.Unlock()
			}(e)

		case <-ctx.Done():
			return
		}
	}
}
<commit_msg>bitswap: fix a minor data race<commit_after>package bitswap

import (
	"sync"
	"time"

	process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
	procctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"

	key "github.com/ipfs/go-ipfs/blocks/key"
	wantlist "github.com/ipfs/go-ipfs/exchange/bitswap/wantlist"
	logging "gx/ipfs/QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR/go-log"
	peer "gx/ipfs/QmRBqJF7hb8ZSpRcMwUt8hNhydWcxGEhtk81HKq6oUwKvs/go-libp2p-peer"
)

var TaskWorkerCount = 8

func (bs *Bitswap) startWorkers(px process.Process, ctx context.Context) {
	// Start up a worker to handle block requests this node is making
	px.Go(func(px process.Process) {
		bs.providerQueryManager(ctx)
	})

	// Start up workers to handle requests from other nodes for the data on this node
	for i := 0; i < TaskWorkerCount; i++ {
		i := i
		px.Go(func(px process.Process) {
			bs.taskWorker(ctx, i)
		})
	}

	// Start up a worker to manage periodically resending our wantlist out to peers
	px.Go(func(px process.Process) {
		bs.rebroadcastWorker(ctx)
	})

	// Start up a worker to manage sending out provides messages
	px.Go(func(px process.Process) {
		bs.provideCollector(ctx)
	})

	// Spawn up multiple workers to handle incoming blocks
	// consider increasing number if providing blocks bottlenecks
	// file transfers
	px.Go(bs.provideWorker)
}

func (bs *Bitswap) taskWorker(ctx context.Context, id int) {
	idmap := logging.LoggableMap{"ID": id}
	defer log.Info("bitswap task worker shutting down...")
	for {
		log.Event(ctx, "Bitswap.TaskWorker.Loop", idmap)
		select {
		case nextEnvelope := <-bs.engine.Outbox():
			select {
			case envelope, ok := <-nextEnvelope:
				if !ok {
					continue
				}
				log.Event(ctx, "Bitswap.TaskWorker.Work", logging.LoggableMap{
					"ID":     id,
					"Target": envelope.Peer.Pretty(),
					"Block":  envelope.Block.Multihash().B58String(),
				})

				bs.wm.SendBlock(ctx, envelope)
			case <-ctx.Done():
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

func (bs *Bitswap) provideWorker(px process.Process) {

	limit := make(chan struct{}, provideWorkerMax)

	limitedGoProvide := func(k key.Key, wid int) {
		defer func() {
			// replace token when done
			<-limit
		}()
		ev := logging.LoggableMap{"ID": wid}

		ctx := procctx.OnClosingContext(px) // derive ctx from px
		defer log.EventBegin(ctx, "Bitswap.ProvideWorker.Work", ev, &k).Done()

		ctx, cancel := context.WithTimeout(ctx, provideTimeout) // timeout ctx
		defer cancel()

		if err := bs.network.Provide(ctx, k); err != nil {
			log.Warning(err)
		}
	}

	// worker spawner, reads from bs.provideKeys until it closes, spawning a
	// _ratelimited_ number of workers to handle each key.
	for wid := 2; ; wid++ {
		ev := logging.LoggableMap{"ID": 1}
		log.Event(procctx.OnClosingContext(px), "Bitswap.ProvideWorker.Loop", ev)

		select {
		case <-px.Closing():
			return
		case k, ok := <-bs.provideKeys:
			if !ok {
				log.Debug("provideKeys channel closed")
				return
			}
			select {
			case <-px.Closing():
				return
			case limit <- struct{}{}:
				go limitedGoProvide(k, wid)
			}
		}
	}
}

func (bs *Bitswap) provideCollector(ctx context.Context) {
	defer close(bs.provideKeys)
	var toProvide []key.Key
	var nextKey key.Key
	var keysOut chan key.Key

	for {
		select {
		case blk, ok := <-bs.newBlocks:
			if !ok {
				log.Debug("newBlocks channel closed")
				return
			}
			if keysOut == nil {
				nextKey = blk.Key()
				keysOut = bs.provideKeys
			} else {
				toProvide = append(toProvide, blk.Key())
			}
		case keysOut <- nextKey:
			if len(toProvide) > 0 {
				nextKey = toProvide[0]
				toProvide = toProvide[1:]
			} else {
				keysOut = nil
			}
		case <-ctx.Done():
			return
		}
	}
}

func (bs *Bitswap) rebroadcastWorker(parent context.Context) {
	ctx, cancel := context.WithCancel(parent)
	defer cancel()

	broadcastSignal := time.NewTicker(rebroadcastDelay.Get())
	defer broadcastSignal.Stop()

	tick := time.NewTicker(10 * time.Second)
	defer tick.Stop()

	for {
		log.Event(ctx, "Bitswap.Rebroadcast.idle")
		select {
		case <-tick.C:
			n := bs.wm.wl.Len()
			if n > 0 {
				log.Debug(n, "keys in bitswap wantlist")
			}
		case <-broadcastSignal.C: // resend unfulfilled wantlist keys
			log.Event(ctx, "Bitswap.Rebroadcast.active")
			for _, e := range bs.wm.wl.Entries() {
				e := e
				bs.findKeys <- &e
			}
		case <-parent.Done():
			return
		}
	}
}

func (bs *Bitswap) providerQueryManager(ctx context.Context) {
	var activeLk sync.Mutex
	active := make(map[key.Key]*wantlist.Entry)

	for {
		select {
		case e := <-bs.findKeys:
			activeLk.Lock()
			if _, ok := active[e.Key]; ok {
				activeLk.Unlock()
				continue
			}
			active[e.Key] = e
			activeLk.Unlock()

			go func(e *wantlist.Entry) {
				child, cancel := context.WithTimeout(e.Ctx, providerRequestTimeout)
				defer cancel()
				providers := bs.network.FindProvidersAsync(child, e.Key, maxProvidersPerRequest)
				for p := range providers {
					go func(p peer.ID) {
						err := bs.network.ConnectTo(child, p)
						if err != nil {
							log.Debug("failed to connect to provider %s: %s", p, err)
						}
					}(p)
				}
				activeLk.Lock()
				delete(active, e.Key)
				activeLk.Unlock()
			}(e)

		case <-ctx.Done():
			return
		}
	}
}
<|endoftext|>


<commit_before>// `Tracey` is a simple library which allows for much easier function enter / exit logging
package tracey

import (
    "os"
    "fmt"
    "log"
    "regexp"
    "strings"
    "strconv"

    "reflect"
    "runtime"
)

// These options represent the various settings which tracey exposes.
// A pointer to this structure is expected to be passed into the
// `tracey.New(...)` function below.
type Options struct {

    // Setting "DisableTracing" to "true" will cause tracey to return
    // no-op'd functions for both exit() and enter(). The default value
    // for this is "false" which enables tracing.
    DisableTracing      bool

    // Setting the "CustomLogger" to nil will cause tracey to log to
    // os.Stdout. Otherwise, this is a pointer to an object as returned
    // from `log.New(...)`.
    CustomLogger        *log.Logger

    // Setting "DisableDepthValue" to "true" will cause tracey to not
    // prepend the printed function's depth to enter() and exit() messages.
    // The default value is "false", which logs the depth value.
    DisableDepthValue   bool

    // Setting "DisableNesting" to "true" will cause tracey to not indent
    // any messages from nested functions. The default value is "false"
    // which enables nesting by prepending "SpacesPerIndent" number of
    // spaces per level nested.
    DisableNesting      bool
    SpacesPerIndent     int    `default:"2"`

    // Setting "EnterMessage" or "ExitMessage" will override the default
    // value of "Enter: " and "EXIT:  " respectively.
    EnterMessage        string `default:"ENTER: "`
    ExitMessage         string `default:"EXIT:  "`

    // Private member, used to keep track of how many levels of nesting
    // the current trace functions have navigated.
    currentDepth        int
}

// Main entry-point for the tracey lib. Calling New with nil will
// result in the default options being used.
func New(opts *Options) (func(string), func(...interface{}) string) {
    var options Options
    if opts != nil {
        options = *opts
    }

    // If tracing is not enabled, just return no-op functions
    if options.DisableTracing {
        return func(string) {}, func(...interface{}) string { return "" }
    }

    // Revert to stdout if no logger is defined
    if options.CustomLogger == nil {
        options.CustomLogger = log.New(os.Stdout, "", 0)
    }

    // Use reflect to deduce "default" values for the
    // Enter and Exit messages (if they are not set)
    reflectedType := reflect.TypeOf(options)
    if options.EnterMessage == "" {
        field, _ := reflectedType.FieldByName("EnterMessage")
        options.EnterMessage = field.Tag.Get("default")
    }
    if options.ExitMessage == "" {
        field, _ := reflectedType.FieldByName("ExitMessage")
        options.ExitMessage = field.Tag.Get("default")
    }

    // If nesting is enabled, and the spaces are not specified,
    // use the "default" value
    if options.DisableNesting {
        options.SpacesPerIndent = 0
    } else if options.SpacesPerIndent == 0 {
        field, _ := reflectedType.FieldByName("SpacesPerIndent")
        options.SpacesPerIndent, _ = strconv.Atoi(field.Tag.Get("default"))
    }

    //
    // Define functions we will use and return to the caller
    //
    _spacify := func() string {
        spaces := strings.Repeat(" ", options.currentDepth * options.SpacesPerIndent)
        if !options.DisableDepthValue {
            return fmt.Sprintf("[%2d]%s", options.currentDepth, spaces)
        }
        return spaces
    }

    // Increment function to increase the current depth value
    _incrementDepth := func() {
        options.currentDepth += 1
    }

    // Decrement function to decrement the current depth value
    //  + panics if current depth value is < 0
    _decrementDepth := func() {
        options.currentDepth -= 1
        if options.currentDepth < 0 {
            panic("Depth is negative! Should never happen!")
        }
    }

    // Enter function, invoked on function entry
    _enter := func(args ...interface{}) string {
        defer _incrementDepth()
        traceMessage := ""

        // Figure out the name of the caller and use that instead
        pc := make([]uintptr, 2)
        runtime.Callers(2, pc)
        fObject := runtime.FuncForPC(pc[0])
        fnName := regexp.MustCompile(`^.*\.(.*)$`).ReplaceAllString(fObject.Name(), "$1")

        if len(args) > 0 {
            if fmtStr, ok := args[0].(string); ok {
                // "$FN" will be replaced by the name of the function
                // We have a string leading args, assume its to be formatted
                traceMessage = fmt.Sprintf(fmtStr, args[1:]...)
            }
        } else {
            traceMessage = fnName;
        }

        traceMessage = regexp.MustCompile(`\$FN`).ReplaceAllString(traceMessage, fnName)

        options.CustomLogger.Printf("%s%s%s\n", _spacify(), options.EnterMessage, traceMessage)
        return traceMessage
    }

    // Exit function, invoked on function exit (usually deferred)
    _exit := func(s string) {
        _decrementDepth()
        options.CustomLogger.Printf("%s%s%s\n", _spacify(), options.ExitMessage, s)
    }

    return _exit, _enter
}
<commit_msg>Move regex to global scope<commit_after>// `Tracey` is a simple library which allows for much easier function enter / exit logging
package tracey

import (
    "os"
    "fmt"
    "log"
    "regexp"
    "strings"
    "strconv"

    "reflect"
    "runtime"
)

// Define a global regex for extracting function names
var RE_stripFnPreamble = regexp.MustCompile(`^.*\.(.*)$`)
var RE_detectFN        = regexp.MustCompile(`\$FN`)

// These options represent the various settings which tracey exposes.
// A pointer to this structure is expected to be passed into the
// `tracey.New(...)` function below.
type Options struct {

    // Setting "DisableTracing" to "true" will cause tracey to return
    // no-op'd functions for both exit() and enter(). The default value
    // for this is "false" which enables tracing.
    DisableTracing      bool

    // Setting the "CustomLogger" to nil will cause tracey to log to
    // os.Stdout. Otherwise, this is a pointer to an object as returned
    // from `log.New(...)`.
    CustomLogger        *log.Logger

    // Setting "DisableDepthValue" to "true" will cause tracey to not
    // prepend the printed function's depth to enter() and exit() messages.
    // The default value is "false", which logs the depth value.
    DisableDepthValue   bool

    // Setting "DisableNesting" to "true" will cause tracey to not indent
    // any messages from nested functions. The default value is "false"
    // which enables nesting by prepending "SpacesPerIndent" number of
    // spaces per level nested.
    DisableNesting      bool
    SpacesPerIndent     int    `default:"2"`

    // Setting "EnterMessage" or "ExitMessage" will override the default
    // value of "Enter: " and "EXIT:  " respectively.
    EnterMessage        string `default:"ENTER: "`
    ExitMessage         string `default:"EXIT:  "`

    // Private member, used to keep track of how many levels of nesting
    // the current trace functions have navigated.
    currentDepth        int
}

// Main entry-point for the tracey lib. Calling New with nil will
// result in the default options being used.
func New(opts *Options) (func(string), func(...interface{}) string) {
    var options Options
    if opts != nil {
        options = *opts
    }

    // If tracing is not enabled, just return no-op functions
    if options.DisableTracing {
        return func(string) {}, func(...interface{}) string { return "" }
    }

    // Revert to stdout if no logger is defined
    if options.CustomLogger == nil {
        options.CustomLogger = log.New(os.Stdout, "", 0)
    }

    // Use reflect to deduce "default" values for the
    // Enter and Exit messages (if they are not set)
    reflectedType := reflect.TypeOf(options)
    if options.EnterMessage == "" {
        field, _ := reflectedType.FieldByName("EnterMessage")
        options.EnterMessage = field.Tag.Get("default")
    }
    if options.ExitMessage == "" {
        field, _ := reflectedType.FieldByName("ExitMessage")
        options.ExitMessage = field.Tag.Get("default")
    }

    // If nesting is enabled, and the spaces are not specified,
    // use the "default" value
    if options.DisableNesting {
        options.SpacesPerIndent = 0
    } else if options.SpacesPerIndent == 0 {
        field, _ := reflectedType.FieldByName("SpacesPerIndent")
        options.SpacesPerIndent, _ = strconv.Atoi(field.Tag.Get("default"))
    }

    //
    // Define functions we will use and return to the caller
    //
    _spacify := func() string {
        spaces := strings.Repeat(" ", options.currentDepth * options.SpacesPerIndent)
        if !options.DisableDepthValue {
            return fmt.Sprintf("[%2d]%s", options.currentDepth, spaces)
        }
        return spaces
    }

    // Increment function to increase the current depth value
    _incrementDepth := func() {
        options.currentDepth += 1
    }

    // Decrement function to decrement the current depth value
    //  + panics if current depth value is < 0
    _decrementDepth := func() {
        options.currentDepth -= 1
        if options.currentDepth < 0 {
            panic("Depth is negative! Should never happen!")
        }
    }

    // Enter function, invoked on function entry
    _enter := func(args ...interface{}) string {
        defer _incrementDepth()
        traceMessage := ""

        // Figure out the name of the caller and use that instead
        pc := make([]uintptr, 2)
        runtime.Callers(2, pc)
        fObject := runtime.FuncForPC(pc[0])
        fnName := RE_stripFnPreamble.ReplaceAllString(fObject.Name(), "$1")

        if len(args) > 0 {
            if fmtStr, ok := args[0].(string); ok {
                // "$FN" will be replaced by the name of the function
                // We have a string leading args, assume its to be formatted
                traceMessage = fmt.Sprintf(fmtStr, args[1:]...)
            }
        } else {
            traceMessage = fnName;
        }

        traceMessage = RE_detectFN.ReplaceAllString(traceMessage, fnName)

        options.CustomLogger.Printf("%s%s%s\n", _spacify(), options.EnterMessage, traceMessage)
        return traceMessage
    }

    // Exit function, invoked on function exit (usually deferred)
    _exit := func(s string) {
        _decrementDepth()
        options.CustomLogger.Printf("%s%s%s\n", _spacify(), options.ExitMessage, s)
    }

    return _exit, _enter
}
<|endoftext|>


<commit_before>/*
The filesystem package provides a unified interface to a real backed filesystem.
An example use case for this might be a server that wants to provide CRUD access
to a filesystem.
*/
package filesystem

import (
	"errors"
	"fmt"
	trie "github.com/tchap/go-patricia/patricia"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

var (
	ErrFileNoExist = errors.New("file does not exist")
	ErrPathNoExist = errors.New("path does not exist")
	ErrFileExists  = errors.New("file already exists")
)

// FSFiler knows everything that a normal os.FileInfo does, but it also
// has access to the underlying file.
type FSFiler interface {
	os.FileInfo
	File() []byte
}

// A FSFile object contains all the information that you would normally find
// in a os.FileInfo object, plus the file itself in a byte slice.
type FSFile struct {
	os.FileInfo
	file []byte
}

var _ FSFiler = (*FSFile)(nil)

func (fsfile *FSFile) File() []byte {
	return fsfile.file
}

// Filesystemer is the base interface that any filesystem should implement.
type Filesystemer interface {
	//ArbitraryAction()
	PathExists(path string) bool
	MkPath(path string) error
	RmPath(path string) error // Applies to both paths + files
	MkFile(path string, content []byte) error
	GetFile(path string) (FSFiler, error)
	RootPath() string
}

// A TransientFilesystemer represents an in-memory representation of a filesystem.
type TransientFilesystemer interface {
	Filesystemer
	Sync() error
}

// A transient filesystem has an in-memory representation of an underlying
// filesystem, for fast access.  The in-memory structure will be updated
// when paths are added via the object.  When the filesystem is changed
// outside of this API, Sync() should be called to bring the in-memory
// representation and actual disk contents back into sync.
type TransientFilesystem struct {
	trie     *trie.Trie // not thread safe, and not embedded
	rootPath string
	lock     sync.RWMutex
}

var _ TransientFilesystemer = (*TransientFilesystem)(nil)

// Sync brings the in-memory struct and underlying file system into sync.
// Walk the entire tree and recreate it from scratch.
func (fs *TransientFilesystem) Sync() error {
	newTrie := trie.NewTrie()
	err := filepath.Walk(fs.RootPath(), func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		fi, err := os.Stat(path)
		if err != nil {
			return err
		}
		if !newTrie.Insert(trie.Prefix(path), fi) {
			return fmt.Errorf("Path %s already exists in tree", path)
		}
		return nil
	})
	if err != nil {
		return err
	}

	fs.lock.Lock()
	defer fs.lock.Unlock()
	fs.trie = newTrie
	return nil
}

// NewTransientFilesystem returns a new TransientFilesystem.  Expects an
// absolute path.
func NewTransientFilesystem(path string) (*TransientFilesystem, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, fmt.Errorf("Unable to stat path %s", path)
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("Specified path %s is not a directory", path)
	}
	fs := &TransientFilesystem{trie: trie.NewTrie(), rootPath: path}
	if err = fs.Sync(); err != nil {
		return nil, err
	}
	return fs, nil
}

// RootPath returns the real absolute path of the filesystem
func (fs *TransientFilesystem) RootPath() string {
	return fs.rootPath
}

// PathExists checks to see that the given path exists.
// The path argument is expected to be relative to the rootPath of the
// TransientFilesystem.  Works for files and directories.
func (fs *TransientFilesystem) PathExists(path string) bool {
	absPath := realPath(fs, path)
	fs.lock.RLock()
	defer fs.lock.RUnlock()
	return fs.trie.Match(trie.Prefix(absPath))
}

// MkPath makes a new directory under the current rootPath.  The path variable
// is expected to be relative to the rootPath.  The diectory will both be added
// to the internal representation AND be added to the real filesystem.  Fails
// if the  specified path is a file, returns nil if the specified path already
// exists.
func (fs *TransientFilesystem) MkPath(path string) error {
	absPath := realPath(fs, path)

	// actually make path
	if absPath == fs.RootPath() {
		return nil
	}
	fi, err := os.Stat(absPath)
	if err != nil && !os.IsNotExist(err) {
		return ErrFileNoExist
	}
	if fi != nil && !fi.IsDir() {
		return ErrFileExists
	}
	doTrieGet := func() (bool, error) {
		fs.lock.RLock()
		defer fs.lock.RUnlock()
		if fs.trie.Match(trie.Prefix(absPath)) {
			if fs.trie.Get(trie.Prefix(absPath)).(os.FileInfo).IsDir() {
				return true, nil
			} else {
				return false, fmt.Errorf("Specified path %s exists in memory as a file, but does not exist on disk", path)
			}
		}
		return false, nil
	}
	existsInMem, err := doTrieGet()
	if err != nil {
		return err
	} else if existsInMem {
		return nil
	}
	if err = os.MkdirAll(absPath, 0755); err != nil {
		return err
	}
	fi, err = os.Stat(absPath)
	if err != nil {
		return err
	}

	// insert into trie
	insertIntoTrie := func() error {
		fs.lock.Lock()
		defer fs.lock.Unlock()
		if success := fs.trie.Insert(trie.Prefix(absPath), fi); !success {
			return fmt.Errorf("path %s already exists in memory")
		}
		return nil
	}
	if err := insertIntoTrie(); err != nil {
		// recover
		if rmErr := os.RemoveAll(absPath); rmErr != nil {
			// wow this is bad, failure inserting into the trie, AND failure
			// cleaning up after ourselves
			return fmt.Errorf("failure creating path %s in memory: %s, and failure cleaning up alrready created path %s: %s", path, err.Error(), path, rmErr.Error())
		}
		return err
	}
	return nil
}

// RmPath recursively removes a path from the filesystem.  The path argument
// can be either a file or directory relative to the rootPath.  Returns an
// error if the path does not exist, or if there was an error removing
// part of the path.
func (fs *TransientFilesystem) RmPath(path string) error {

	absPath := realPath(fs, path)
	switch path {
	case "/", "":
		return fmt.Errorf("unwilling to delete root filesystem path %s", fs.RootPath())
	default:
	}

	doGet := func() interface{} {
		fs.lock.RLock()
		defer fs.lock.RUnlock()
		return fs.trie.Get(trie.Prefix(absPath))
	}
	item := doGet()
	if item == nil {
		return ErrPathNoExist
	}
	if !item.(os.FileInfo).IsDir() {
		return ErrFileExists
	}
	// Make this a function so we can release the mutex ASAP
	doTrieDelete := func() error {
		fs.lock.Lock()
		defer fs.lock.Unlock()
		if deleted := fs.trie.Delete(trie.Prefix(absPath)); !deleted {
			return fmt.Errorf("path %s does not exist in memory", path)
		}
		return nil
	}
	if err := doTrieDelete(); err != nil {
		return err
	}
	if err := os.RemoveAll(absPath); err != nil {
		// put item back in the tree
		// eat the error here
		fs.trie.Insert(trie.Prefix(absPath), item)
		// possibly in an unknown state at this point.  whomp whomp.
		return err
	}
	return nil
}

// MkFile makes a file consisting of the provided content at the given path.
// If the path does not already exist, an error will be returned.  If the file
// already exists, an error will be returned.
func (fs *TransientFilesystem) MkFile(path string, content []byte) error {
	if !fs.PathExists(filepath.Dir(path)) {
		return ErrPathNoExist
	} else if fs.PathExists(path) {
		return ErrFileExists
	}

	absPath := realPath(fs, path)
	if err := ioutil.WriteFile(absPath, content, 0644); err != nil {
		return err
	}
	doInsertTrie := func() error {
		fs.lock.Lock()
		defer fs.lock.Unlock()
		fi, err := os.Stat(absPath)
		if err != nil {
			return err
		}
		if success := fs.trie.Insert(trie.Prefix(absPath), fi); !success {
			// path already exists in trie
			if err := os.RemoveAll(absPath); err != nil {
				// this is bad
				return fmt.Errorf("file %s already exists in memory and error removing file %s: %s", path, path, err)
			}
			// technically this is a little misleading, as it refers to the
			// trie, not what's on disk, but c'est la vie
			return ErrFileExists
		}
		return nil
	}
	if err := doInsertTrie(); err != nil {
		return err
	}
	return nil
}

// GetFile gets the file at path and returns a FSFile object, which both
// describes the file and has a pointer to a copy of the file in a byte slice.
// If the files does not exist or path is actually a directory, an error is returned.
func (fs *TransientFilesystem) GetFile(path string) (FSFiler, error) {
	absPath := realPath(fs, path)
	if !fs.PathExists(path) {
		return nil, ErrFileNoExist
	} else if fi, err := os.Stat(realPath(fs, path)); err != nil || fi.IsDir() {
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("Specified path %s is a directory", realPath(fs, path))
	}
	item := fs.trie.Get(trie.Prefix(absPath))
	if item == nil {
		return nil, ErrPathNoExist
	}
	fileBytes, _ := ioutil.ReadFile(absPath)
	fi, _ := os.Stat(realPath(fs, path))
	return &FSFile{FileInfo: fi, file: fileBytes}, nil
}

// A passthrough file system doesn't have an internal representation of the
// actual filesystem - it is merely a proxy to the actual filesystem.  The
// penalty of disk access is paid, but it is never out of sync with the actual
// contents of the filesystem.  This implementation does not currently deal
// with file permissions.  That is, to say, files/direcories created via this
// implementation will be subject to ownership/permissions rules enforced by the kernel.
// If you want to be able to set custom ownership or permissions, provide your own
// implementation.
type PassThroughFilesystem struct {
	rootPath string
}

var _ Filesystemer = (*PassThroughFilesystem)(nil)

// Create a new PassThroughFileSystem, given an absolute path to a directory.
// Will fail if the given path is inaccessible for any reason (including its
// inexistence), or is the path is not a directory.
func NewPassThroughFilesystem(path string) (*PassThroughFilesystem, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, fmt.Errorf("Unable to stat path %s", path)
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("Specified path %s is not a directory", path)
	}
	return &PassThroughFilesystem{rootPath: path}, nil
}

// RootPath returns the real absolute path of the filesystem
func (fs *PassThroughFilesystem) RootPath() string {
	return fs.rootPath
}

// realPath returns the absolute path of the PassThroughFilesystem's
// rootPath plus the given relPath.
func realPath(fs Filesystemer, relPath string) string {
	tgtPath := filepath.Join(fs.RootPath(), relPath)
	return filepath.Clean(tgtPath)
}

// PathExists checks to see that the given path exists.
// The path argument is expected to be relative to the rootPath of the
// PassThroughFilesystem.  Works for files and directories.
func (fs *PassThroughFilesystem) PathExists(path string) bool {
	_, err := os.Stat(realPath(fs, path))
	return !os.IsNotExist(err)
}

// MkPath makes a new directory under the current rootPath.  The path variable
// is expected to be relative to the rootPath.  Returns nil if the path already
// exists as a dir, and an error if the path already exists and is a file.
func (fs *PassThroughFilesystem) MkPath(path string) error {
	absPath := realPath(fs, path)
	if absPath == fs.rootPath {
		return nil
	}
	fi, err := os.Stat(absPath)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	if fi != nil && !fi.IsDir() {
		return ErrFileExists
	}
	return os.MkdirAll(absPath, 0755)
}

// rmPathOnDisk removes the specified path from disk.  The given path is
// relative to the root of the associated Filesystemer object.
func rmPathOnDisk(fs Filesystemer, path string) error {
	absPath := realPath(fs, path)
	switch path {
	case "/", "":
		return fmt.Errorf("unwilling to delete root filesystem path %s", fs.RootPath())
	default:
	}
	if !fs.PathExists(path) {
		return ErrPathNoExist
	}
	return os.RemoveAll(absPath)
}

// RmPath recursively removes a path from the filesystem.  The path argument
// can be either a file or directory relative to the rootPath.  Returns an
// error if the path does not exist, or if there was an error removing
// part of the path.
func (fs *PassThroughFilesystem) RmPath(path string) error {
	return rmPathOnDisk(fs, path)
}

// MkFile makes a file consisting of the provided content at the given path.
// If the path does not already exist, an error will be returned.  If the file
// already exists, an error will be returned.
func (fs *PassThroughFilesystem) MkFile(path string, content []byte) error {
	if !fs.PathExists(filepath.Dir(path)) {
		return ErrPathNoExist
	} else if fs.PathExists(path) {
		return ErrFileExists
	}

	return ioutil.WriteFile(realPath(fs, path), content, 0644)
}

// GetFile gets the file at path and returns a FSFile object, which both
// describes the file and has a pointer to a copy of the file in a byte slice.
// If the files does not exist or path is actually a directory, an error is returned.
func (fs *PassThroughFilesystem) GetFile(path string) (FSFiler, error) {
	if !fs.PathExists(path) {
		return nil, ErrFileNoExist
	} else if fi, err := os.Stat(realPath(fs, path)); err != nil || fi.IsDir() {
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("Specified path %s is a directory", realPath(fs, path))
	}
	fileBytes, _ := ioutil.ReadFile(realPath(fs, path))
	fi, _ := os.Stat(realPath(fs, path))
	return &FSFile{FileInfo: fi, file: fileBytes}, nil
}
<commit_msg>FS cleanup<commit_after>/*
The filesystem package provides a unified interface to a real backed filesystem.
An example use case for this might be a server that wants to provide CRUD access
to a filesystem.
*/
package filesystem

import (
	"errors"
	"fmt"
	trie "github.com/tchap/go-patricia/patricia"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

var (
	ErrFileNoExist = errors.New("file does not exist")
	ErrPathNoExist = errors.New("path does not exist")
	ErrFileExists  = errors.New("file already exists")
)

// FSFiler knows everything that a normal os.FileInfo does, but it also
// has access to the underlying file.
type FSFiler interface {
	os.FileInfo
	File() []byte
}

// A FSFile object contains all the information that you would normally find
// in a os.FileInfo object, plus the file itself in a byte slice.
type FSFile struct {
	os.FileInfo
	file []byte
}

var _ FSFiler = (*FSFile)(nil)

// File returns the file stored in a FSFile
func (fsfile *FSFile) File() []byte {
	return fsfile.file
}

// Filesystemer is the base interface that any filesystem should implement.
type Filesystemer interface {
	//ArbitraryAction()
	PathExists(path string) bool
	MkPath(path string) error
	RmPath(path string) error // Applies to both paths + files
	MkFile(path string, content []byte) error
	GetFile(path string) (FSFiler, error)
	RootPath() string
}

// A TransientFilesystemer represents an in-memory representation of a filesystem.
type TransientFilesystemer interface {
	Filesystemer
	Sync() error
}

// A transient filesystem has an in-memory representation of an underlying
// filesystem, for fast access.  The in-memory structure will be updated
// when paths are added via the object.  When the filesystem is changed
// outside of this API, Sync() should be called to bring the in-memory
// representation and actual disk contents back into sync.
type TransientFilesystem struct {
	trie     *trie.Trie // not thread safe, and not embedded
	rootPath string
	lock     sync.RWMutex
}

var _ TransientFilesystemer = (*TransientFilesystem)(nil)

// Sync brings the in-memory struct and underlying file system into sync.
// Walk the entire tree and recreate it from scratch.
func (fs *TransientFilesystem) Sync() error {
	newTrie := trie.NewTrie()
	err := filepath.Walk(fs.RootPath(), func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		fi, err := os.Stat(path)
		if err != nil {
			return err
		}
		if !newTrie.Insert(trie.Prefix(path), fi) {
			return fmt.Errorf("Path %s already exists in tree", path)
		}
		return nil
	})
	if err != nil {
		return err
	}

	fs.lock.Lock()
	defer fs.lock.Unlock()
	fs.trie = newTrie
	return nil
}

// NewTransientFilesystem returns a new TransientFilesystem.  Expects an
// absolute path to the location on disk.  Returns an error if the path cannot
// be created, or if a file already exists in that location.
func NewTransientFilesystem(absPath string) (*TransientFilesystem, error) {
	fi, err := os.Stat(absPath)
	if err != nil {
		return nil, fmt.Errorf("Unable to stat path %s", absPath)
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("Specified path %s is not a directory", absPath)
	}
	fs := &TransientFilesystem{trie: trie.NewTrie(), rootPath: absPath}
	if err = fs.Sync(); err != nil {
		return nil, err
	}
	return fs, nil
}

// RootPath returns the real absolute path of the filesystem
func (fs *TransientFilesystem) RootPath() string {
	return fs.rootPath
}

// PathExists checks to see that the given path exists.
// The path argument is expected to be relative to the rootPath of the
// TransientFilesystem.  Works for files and directories.
func (fs *TransientFilesystem) PathExists(path string) bool {
	absPath := realPath(fs, path)
	fs.lock.RLock()
	defer fs.lock.RUnlock()
	return fs.trie.Match(trie.Prefix(absPath))
}

// MkPath makes a new directory under the current rootPath.  The path variable
// is expected to be relative to the rootPath.  The diectory will both be added
// to the internal representation AND be added to the real filesystem.  Fails
// if the  specified path is a file, returns nil if the specified path already
// exists.
func (fs *TransientFilesystem) MkPath(path string) error {
	absPath := realPath(fs, path)

	// actually make path
	if absPath == fs.RootPath() {
		return nil
	}
	fi, err := os.Stat(absPath)
	if err != nil && !os.IsNotExist(err) {
		return ErrFileNoExist
	}
	if fi != nil && !fi.IsDir() {
		return ErrFileExists
	}
	doTrieGet := func() (bool, error) {
		fs.lock.RLock()
		defer fs.lock.RUnlock()
		if fs.trie.Match(trie.Prefix(absPath)) {
			if fs.trie.Get(trie.Prefix(absPath)).(os.FileInfo).IsDir() {
				return true, nil
			} else {
				return false, fmt.Errorf("Specified path %s exists in memory as a file, but does not exist on disk", path)
			}
		}
		return false, nil
	}
	existsInMem, err := doTrieGet()
	if err != nil {
		return err
	} else if existsInMem {
		return nil
	}
	if err = os.MkdirAll(absPath, 0755); err != nil {
		return err
	}
	fi, err = os.Stat(absPath)
	if err != nil {
		return err
	}

	// insert into trie
	insertIntoTrie := func() error {
		fs.lock.Lock()
		defer fs.lock.Unlock()
		if success := fs.trie.Insert(trie.Prefix(absPath), fi); !success {
			return fmt.Errorf("path %s already exists in memory", path)
		}
		return nil
	}
	if err := insertIntoTrie(); err != nil {
		// recover
		if rmErr := os.RemoveAll(absPath); rmErr != nil {
			// wow this is bad, failure inserting into the trie, AND failure
			// cleaning up after ourselves
			return fmt.Errorf("failure creating path %s in memory: %s, and failure cleaning up alrready created path %s: %s", path, err.Error(), path, rmErr.Error())
		}
		return err
	}
	return nil
}

// RmPath recursively removes a path from the filesystem.  The path argument
// can be either a file or directory relative to the rootPath.  Returns an
// error if the path does not exist, or if there was an error removing
// part of the path.
func (fs *TransientFilesystem) RmPath(path string) error {

	absPath := realPath(fs, path)
	switch path {
	case "/", "":
		return fmt.Errorf("unwilling to delete root filesystem path %s", fs.RootPath())
	default:
	}

	doGet := func() interface{} {
		fs.lock.RLock()
		defer fs.lock.RUnlock()
		return fs.trie.Get(trie.Prefix(absPath))
	}
	item := doGet()
	if item == nil {
		return ErrPathNoExist
	}
	if !item.(os.FileInfo).IsDir() {
		return ErrFileExists
	}
	// Make this a function so we can release the mutex ASAP
	doTrieDelete := func() error {
		fs.lock.Lock()
		defer fs.lock.Unlock()
		if deleted := fs.trie.Delete(trie.Prefix(absPath)); !deleted {
			return fmt.Errorf("path %s does not exist in memory", path)
		}
		return nil
	}
	if err := doTrieDelete(); err != nil {
		return err
	}
	if err := os.RemoveAll(absPath); err != nil {
		// put item back in the tree
		// eat the error here
		fs.trie.Insert(trie.Prefix(absPath), item)
		// possibly in an unknown state at this point.  whomp whomp.
		return err
	}
	return nil
}

// MkFile makes a file consisting of the provided content at the given path.
// If the path does not already exist, an error will be returned.  If the file
// already exists, an error will be returned.
func (fs *TransientFilesystem) MkFile(path string, content []byte) error {
	if !fs.PathExists(filepath.Dir(path)) {
		return ErrPathNoExist
	} else if fs.PathExists(path) {
		return ErrFileExists
	}

	absPath := realPath(fs, path)
	if err := ioutil.WriteFile(absPath, content, 0644); err != nil {
		return err
	}
	doInsertTrie := func() error {
		fs.lock.Lock()
		defer fs.lock.Unlock()
		fi, err := os.Stat(absPath)
		if err != nil {
			return err
		}
		if success := fs.trie.Insert(trie.Prefix(absPath), fi); !success {
			// path already exists in trie
			if err := os.RemoveAll(absPath); err != nil {
				// this is bad
				return fmt.Errorf("file %s already exists in memory and error removing file %s: %s", path, path, err)
			}
			// technically this is a little misleading, as it refers to the
			// trie, not what's on disk, but c'est la vie
			return ErrFileExists
		}
		return nil
	}
	if err := doInsertTrie(); err != nil {
		return err
	}
	return nil
}

// GetFile gets the file at path and returns a FSFile object, which both
// describes the file and has a pointer to a copy of the file in a byte slice.
// If the files does not exist or path is actually a directory, an error is returned.
func (fs *TransientFilesystem) GetFile(path string) (FSFiler, error) {
	absPath := realPath(fs, path)
	if !fs.PathExists(path) {
		return nil, ErrFileNoExist
	} else if fi, err := os.Stat(realPath(fs, path)); err != nil || fi.IsDir() {
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("Specified path %s is a directory", realPath(fs, path))
	}
	item := fs.trie.Get(trie.Prefix(absPath))
	if item == nil {
		return nil, ErrPathNoExist
	}
	fileBytes, _ := ioutil.ReadFile(absPath)
	fi, _ := os.Stat(realPath(fs, path))
	return &FSFile{FileInfo: fi, file: fileBytes}, nil
}

// A passthrough file system doesn't have an internal representation of the
// actual filesystem - it is merely a proxy to the actual filesystem.  The
// penalty of disk access is paid, but it is never out of sync with the actual
// contents of the filesystem.  This implementation does not currently deal
// with file permissions.  That is, to say, files/direcories created via this
// implementation will be subject to ownership/permissions rules enforced by the kernel.
// If you want to be able to set custom ownership or permissions, provide your own
// implementation.
type PassThroughFilesystem struct {
	rootPath string
}

var _ Filesystemer = (*PassThroughFilesystem)(nil)

// Create a new PassThroughFileSystem, given an absolute path to a directory.
// Will fail if the given path is inaccessible for any reason (including its
// inexistence), or if the path is not a directory.
func NewPassThroughFilesystem(absPath string) (*PassThroughFilesystem, error) {
	fi, err := os.Stat(absPath)
	if err != nil {
		return nil, fmt.Errorf("Unable to stat path %s", absPath)
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("Specified path %s is not a directory", absPath)
	}
	return &PassThroughFilesystem{rootPath: absPath}, nil
}

// RootPath returns the real absolute path of the filesystem
func (fs *PassThroughFilesystem) RootPath() string {
	return fs.rootPath
}

// realPath returns the absolute path of the PassThroughFilesystem's
// rootPath plus the given relPath.
func realPath(fs Filesystemer, relPath string) string {
	tgtPath := filepath.Join(fs.RootPath(), relPath)
	return filepath.Clean(tgtPath)
}

// PathExists checks to see that the given path exists.
// The path argument is expected to be relative to the rootPath of the
// PassThroughFilesystem.  Works for files and directories.
func (fs *PassThroughFilesystem) PathExists(path string) bool {
	_, err := os.Stat(realPath(fs, path))
	return !os.IsNotExist(err)
}

// MkPath makes a new directory under the current rootPath.  The path variable
// is expected to be relative to the rootPath.  Returns nil if the path already
// exists as a dir, and an error if the path already exists and is a file.
func (fs *PassThroughFilesystem) MkPath(path string) error {
	absPath := realPath(fs, path)
	if absPath == fs.rootPath {
		return nil
	}
	fi, err := os.Stat(absPath)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	if fi != nil && !fi.IsDir() {
		return ErrFileExists
	}
	return os.MkdirAll(absPath, 0755)
}

// rmPathOnDisk removes the specified path from disk.  The given path is
// relative to the root of the associated Filesystemer object.
func rmPathOnDisk(fs Filesystemer, path string) error {
	absPath := realPath(fs, path)
	switch path {
	case "/", "":
		return fmt.Errorf("unwilling to delete root filesystem path %s", fs.RootPath())
	default:
	}
	if !fs.PathExists(path) {
		return ErrPathNoExist
	}
	return os.RemoveAll(absPath)
}

// RmPath recursively removes a path from the filesystem.  The path argument
// can be either a file or directory relative to the rootPath.  Returns an
// error if the path does not exist, or if there was an error removing
// part of the path.
func (fs *PassThroughFilesystem) RmPath(path string) error {
	return rmPathOnDisk(fs, path)
}

// MkFile makes a file consisting of the provided content at the given path.
// If the path does not already exist, an error will be returned.  If the file
// already exists, an error will be returned.
func (fs *PassThroughFilesystem) MkFile(path string, content []byte) error {
	if !fs.PathExists(filepath.Dir(path)) {
		return ErrPathNoExist
	} else if fs.PathExists(path) {
		return ErrFileExists
	}

	return ioutil.WriteFile(realPath(fs, path), content, 0644)
}

// GetFile gets the file at path and returns a FSFile object, which both
// describes the file and has a pointer to a copy of the file in a byte slice.
// If the files does not exist or path is actually a directory, an error is returned.
func (fs *PassThroughFilesystem) GetFile(path string) (FSFiler, error) {
	if !fs.PathExists(path) {
		return nil, ErrFileNoExist
	} else if fi, err := os.Stat(realPath(fs, path)); err != nil || fi.IsDir() {
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("Specified path %s is a directory", realPath(fs, path))
	}
	fileBytes, _ := ioutil.ReadFile(realPath(fs, path))
	fi, _ := os.Stat(realPath(fs, path))
	return &FSFile{FileInfo: fi, file: fileBytes}, nil
}
<|endoftext|>