<commit_before>/*
client is how clients should access the system

IMPROV: Because we send so many requests at once using threads, we may have problems having buffer overflow on the socket buffer.  We could limit the number of concurrent threads sending a request.
*/
package client

import (
	"errors"
	"log"

	"github.com/mateusbraga/freestore/pkg/comm"
	"github.com/mateusbraga/freestore/pkg/view"
)

var (
	diffResultsErr = errors.New("Read Divergence")
)

type RegisterMsg struct {
	Value     interface{}
	Timestamp int

	View *view.View

	Err error
}

// Write v on the system by running the quorum write protocol.
func Write(v interface{}) error {
	immutableCurrentView := currentView.NewCopy()

	readValue, err := basicReadQuorum(immutableCurrentView)
	if err != nil {
		// Expected: oldViewError or diffResultsErr
		if oldViewError, ok := err.(*view.OldViewError); ok {
			updateCurrentView(oldViewError.NewView)
			Write(v)
		} else if err == diffResultsErr {
			// Do nothing - we will write a new value anyway
		} else {
			return err
		}
	}

	writeMsg := RegisterMsg{}
	writeMsg.Value = v
	writeMsg.Timestamp = readValue.Timestamp + 1

	err = basicWriteQuorum(immutableCurrentView, writeMsg)
	if err != nil {
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic write quorum")
			updateCurrentView(oldViewError.NewView)
			return Write(v)
		} else {
			return err
		}
	}

	return nil
}

// basicWriteQuorum writes v to all processes on the view and returns when it gets confirmation from a quorum
//
// If the view needs to be updated, it will return the new view in a *view.OldViewError.
func basicWriteQuorum(view *view.View, writeMsg RegisterMsg) error {
	resultChan := make(chan RegisterMsg, view.N())
	errChan := make(chan error, view.N())

	writeMsg.View = view

	// Send write request to all
	for _, process := range view.GetMembers() {
		go writeProcess(process, writeMsg, resultChan, errChan)
	}

	// Get quorum
	var successTotal int
	var failedTotal int
	for {
		select {
		case resultValue := <-resultChan:
			if resultValue.Err != nil {
				return resultValue.Err
			}

			successTotal++
			if successTotal == view.QuorumSize() {
				return nil
			}

		case err := <-errChan:
			log.Println("+1 error on write:", err)
			failedTotal++

			// view.F() needs an updated View, and we know we have an updated view when successTotal > 0
			if successTotal > 0 {
				if failedTotal > view.F() {
					return errors.New("failedTotal to get write quorun")
				}
			} else {
				if failedTotal == view.N() {
					return errors.New("failedTotal to get write quorun")
				}
			}
		}
	}
}

// writeProcess sends a write request with writeMsg to process and return the result through resultChan or an error through errChan
func writeProcess(process view.Process, writeMsg RegisterMsg, resultChan chan RegisterMsg, errChan chan error) {
	var reply RegisterMsg
	err := comm.SendRPCRequest(process, "ClientRequest.Write", writeMsg, &reply)
	if err != nil {
		errChan <- err
		return
	}

	resultChan <- reply
}

// Read executes the quorum read protocol.
func Read() (interface{}, error) {
	immutableCurrentView := currentView.NewCopy()

	readMsg, err := basicReadQuorum(immutableCurrentView)
	if err != nil {
		// Expected: oldViewError (will retry) or diffResultsErr (will write most current value to view).
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic read quorum")
			updateCurrentView(oldViewError.NewView)
			return Read()
		} else if err == diffResultsErr {
			log.Println("Found divergence: Going to 2nd phase of read protocol")
			return read2ndPhase(immutableCurrentView, readMsg)
		} else {
			return 0, err
		}
	}

	return readMsg.Value, nil
}

func read2ndPhase(immutableCurrentView *view.View, readMsg RegisterMsg) (interface{}, error) {
	err := basicWriteQuorum(immutableCurrentView, readMsg)
	if err != nil {
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic write quorum")
			updateCurrentView(oldViewError.NewView)
			return Read()
		} else {
			return 0, err
		}
	}
	return readMsg.Value, nil
}

// basicReadQuorum reads a RegisterMsg from all members of the view, returning the most recent one. It decides which is the most recent value as soon as it gets a quorum
//
// If the view needs to be updated, it will update the view in a *view.OldViewError.
// If values returned by the processes differ, it will return diffResultsErr
func basicReadQuorum(view *view.View) (RegisterMsg, error) {
	resultChan := make(chan RegisterMsg, view.N())
	errChan := make(chan error, view.N())

	// Send read request to all
	for _, process := range view.GetMembers() {
		go readProcess(process, view, resultChan, errChan)
	}

	// Get quorum
	var failedTotal int
	var resultArray []RegisterMsg
	var finalValue RegisterMsg
	finalValue.Timestamp = -1 // Make it negative to force value.Timestamp > finalValue.Timestamp
	for {
		select {
		case resultValue := <-resultChan:
			if resultValue.Err != nil {
				return RegisterMsg{}, resultValue.Err
			}

			resultArray = append(resultArray, resultValue)

			if resultValue.Timestamp > finalValue.Timestamp {
				finalValue = resultValue
			}

			if len(resultArray) == view.QuorumSize() {
				for _, val := range resultArray {
					if finalValue.Timestamp != val.Timestamp { // There are divergence on the processes
						return finalValue, diffResultsErr
					}
				}
				return finalValue, nil
			}
		case err := <-errChan:
			log.Println("+1 error on read:", err)
			failedTotal++

			// view.F() needs an updated View, and we know we have an updated view when len(resultArray) > 0
			if len(resultArray) > 0 {
				if failedTotal > view.F() {
					return RegisterMsg{}, errors.New("Failed to get read quorun")
				}
			} else {
				if failedTotal == view.N() {
					return RegisterMsg{}, errors.New("Failed to get read quorun")
				}
			}
		}
	}
}

// readProcess sends a read request to process and return an err through errChan or a result through resultChan
func readProcess(process view.Process, immutableCurrentView *view.View, resultChan chan RegisterMsg, errChan chan error) {
	var reply RegisterMsg
	err := comm.SendRPCRequest(process, "ClientRequest.Read", immutableCurrentView, &reply)
	if err != nil {
		errChan <- err
		return
	}

	resultChan <- reply
}
<commit_msg>Refactor client<commit_after>/*
client is how clients should access the system

*/
package client

import (
	"errors"
	"log"

	"github.com/mateusbraga/freestore/pkg/comm"
	"github.com/mateusbraga/freestore/pkg/view"
)

var (
	diffResultsErr = errors.New("Read Divergence")
)

type RegisterMsg struct {
	Value     interface{}
	Timestamp int

	View *view.View

	Err error
}

// Write v on the system by running the quorum write protocol.
func Write(v interface{}) error {
	immutableCurrentView := currentView.NewCopy()

	readValue, err := basicReadQuorum(immutableCurrentView)
	if err != nil {
		// Special cases:
		//  oldViewError: currentView is old, update it and retry
		//  diffResultsErr: can be ignored
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic read quorum of Write op")
			updateCurrentView(oldViewError.NewView)
			return Write(v)
		} else if err == diffResultsErr {
			// Do nothing - we will write a new value anyway
		} else {
			return err
		}
	}

	writeMsg := RegisterMsg{}
	writeMsg.Value = v
	writeMsg.Timestamp = readValue.Timestamp + 1
	writeMsg.View = immutableCurrentView

	err = basicWriteQuorum(immutableCurrentView, writeMsg)
	if err != nil {
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic write quorum of Write op")
			updateCurrentView(oldViewError.NewView)
			return Write(v)
		} else {
			return err
		}
	}

	return nil
}

// basicWriteQuorum writes v to all processes on the view and returns when it gets confirmation from a quorum
//
// If the view needs to be updated, it will return the new view in a *view.OldViewError.
func basicWriteQuorum(view *view.View, writeMsg RegisterMsg) error {
	resultChan := make(chan RegisterMsg, view.N())
	errChan := make(chan error, view.N())

	// Send write request to all
	for _, process := range view.GetMembers() {
		go writeProcess(process, writeMsg, resultChan, errChan)
	}

	// Get quorum
	var successTotal int
	var failedTotal int
	for {
		select {
		case resultValue := <-resultChan:
			if resultValue.Err != nil {
				return resultValue.Err
			}

			successTotal++
			if successTotal == view.QuorumSize() {
				return nil
			}

		case err := <-errChan:
			log.Println("+1 error on write:", err)
			failedTotal++

			allFailed := failedTotal == view.N()
			mostFailed := failedTotal > view.F()

			if mostFailed || allFailed {
				return errors.New("failedTotal to get write quorun")
			}
		}
	}
}

// writeProcess sends a write request with writeMsg to process and return the result through resultChan or an error through errChan
func writeProcess(process view.Process, writeMsg RegisterMsg, resultChan chan RegisterMsg, errChan chan error) {
	var reply RegisterMsg
	err := comm.SendRPCRequest(process, "ClientRequest.Write", writeMsg, &reply)
	if err != nil {
		errChan <- err
		return
	}

	resultChan <- reply
}

// Read executes the quorum read protocol.
func Read() (interface{}, error) {
	immutableCurrentView := currentView.NewCopy()

	readMsg, err := basicReadQuorum(immutableCurrentView)
	if err != nil {
		// Expected: oldViewError (will retry) or diffResultsErr (will write most current value to view).
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic read quorum of Read op")
			updateCurrentView(oldViewError.NewView)
			return Read()
		} else if err == diffResultsErr {
			log.Println("Found divergence: Going to 2nd phase of read protocol")
			return read2ndPhase(immutableCurrentView, readMsg)
		} else {
			return 0, err
		}
	}

	return readMsg.Value, nil
}

func read2ndPhase(immutableCurrentView *view.View, readMsg RegisterMsg) (interface{}, error) {
	err := basicWriteQuorum(immutableCurrentView, readMsg)
	if err != nil {
		if oldViewError, ok := err.(*view.OldViewError); ok {
			log.Println("View updated during basic write quorum of Read op (2nd phase)")
			updateCurrentView(oldViewError.NewView)
			return Read()
		} else {
			return 0, err
		}
	}
	return readMsg.Value, nil
}

// basicReadQuorum reads a RegisterMsg from all members of the view, returning the most recent one. It decides which is the most recent value as soon as it gets a quorum
//
// If the view needs to be updated, it will update the view in a *view.OldViewError.
// If values returned by the processes differ, it will return diffResultsErr
func basicReadQuorum(view *view.View) (RegisterMsg, error) {
	resultChan := make(chan RegisterMsg, view.N())
	errChan := make(chan error, view.N())

	// Send read request to all
	for _, process := range view.GetMembers() {
		go readProcess(process, view, resultChan, errChan)
	}

	// Get quorum
	var failedTotal int
	var resultArray []RegisterMsg
	var finalValue RegisterMsg
	finalValue.Timestamp = -1 // Make it negative to force value.Timestamp > finalValue.Timestamp
	for {
		select {
		case resultValue := <-resultChan:
			if resultValue.Err != nil {
				return RegisterMsg{}, resultValue.Err
			}

			resultArray = append(resultArray, resultValue)

			if resultValue.Timestamp > finalValue.Timestamp {
				finalValue = resultValue
			}

			if len(resultArray) == view.QuorumSize() {
				for _, val := range resultArray {
					if finalValue.Timestamp != val.Timestamp { // There are divergence on the processes
						return finalValue, diffResultsErr
					}
				}
				return finalValue, nil
			}
		case err := <-errChan:
			log.Println("+1 error on read:", err)
			failedTotal++

			allFailed := failedTotal == view.N()
			mostFailed := failedTotal > view.F()

			if mostFailed || allFailed {
				return RegisterMsg{}, errors.New("Failed to get read quorun")
			}
		}
	}
}

// readProcess sends a read request to process and return an err through errChan or a result through resultChan
func readProcess(process view.Process, immutableCurrentView *view.View, resultChan chan RegisterMsg, errChan chan error) {
	var reply RegisterMsg
	err := comm.SendRPCRequest(process, "ClientRequest.Read", immutableCurrentView, &reply)
	if err != nil {
		errChan <- err
		return
	}

	resultChan <- reply
}
<|endoftext|>


<commit_before>/*
Copyright 2011 The Camlistore Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"camlistore.org/pkg/auth"
	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/client/android"
	"camlistore.org/pkg/jsonconfig"
	"camlistore.org/pkg/jsonsign"
	"camlistore.org/pkg/osutil"
)

// These, if set, override the JSON config file
// ~/.config/camlistore/client-config.json
// (i.e. osutil.UserClientConfigPath()) "server" and "password" keys.
//
// A main binary must call AddFlags to expose these.
var (
	flagServer     string
	flagSecretRing string
)

func AddFlags() {
	defaultPath := osutil.UserClientConfigPath()
	flag.StringVar(&flagServer, "server", "", "Camlistore server prefix. If blank, the default from the \"server\" field of "+defaultPath+" is used. Acceptable forms: https://you.example.com, example.com:1345 (https assumed), or http://you.example.com/alt-root")
	flag.StringVar(&flagSecretRing, "secret-keyring", "", "GnuPG secret keyring file to use.")
}

// ExplicitServer returns the blobserver given in the flags, if any.
func ExplicitServer() string {
	return flagServer
}

var configOnce sync.Once
var config *clientConfig

// clientConfig holds the values found in the JSON client config file
// once it's been parsed and validated by parseConfig.
// Unless otherwise specified by the comments, no default values were
// used when parsing.
type clientConfig struct {
	auth               string
	server             string
	identity           string
	identitySecretRing string // defaults to osutil.IdentitySecretRing()
	trustedCerts       []string
	ignoredFiles       []string
}

func parseConfig() {
	if android.OnAndroid() {
		return
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
		if keyId := serverKeyId(); keyId != "" {
			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
			errMsg += hint
		}
		log.Fatal(errMsg)
	}

	conf, err := jsonconfig.ReadFile(configPath)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := jsonconfig.Obj(conf)
	config = &clientConfig{
		auth:               cfg.OptionalString("auth", ""),
		server:             cfg.OptionalString("server", ""),
		identity:           cfg.OptionalString("identity", ""),
		identitySecretRing: cfg.OptionalString("identitySecretRing", osutil.IdentitySecretRing()),
		trustedCerts:       cfg.OptionalList("trustedCerts"),
		ignoredFiles:       cfg.OptionalList("ignoredFiles"),
	}
	if err := cfg.Validate(); err != nil {
		printConfigChangeHelp(cfg)
		log.Fatalf("Error in config file: %v", err)
	}
}

// printConfigChangeHelp checks if conf contains obsolete keys,
// and prints additional help in this case.
func printConfigChangeHelp(conf jsonconfig.Obj) {
	// rename maps from old key names to the new ones.
	// If there is no new one, the value is the empty string.
	rename := map[string]string{
		"keyId":            "identity",
		"publicKeyBlobref": "",
		"selfPubKeyDir":    "",
		"secretRing":       "identitySecretRing",
	}
	oldConfig := false
	configChangedMsg := fmt.Sprintf("The client configuration file (%s) keys have changed.\n", osutil.UserClientConfigPath())
	for _, unknown := range conf.UnknownKeys() {
		for k, v := range rename {
			if unknown == k {
				if v != "" {
					configChangedMsg += fmt.Sprintf("%q should be renamed %q.\n", k, v)
				} else {
					configChangedMsg += fmt.Sprintf("%q should be removed.\n", k)
				}
				oldConfig = true
				break
			}
		}
	}
	if oldConfig {
		configChangedMsg += "Please see http://camlistore.org/docs/client-config, or use camput init to recreate a default one."
		log.Print(configChangedMsg)
	}
}

// serverKeyId returns the public gpg key id ("identity" field)
// from the user's server config , if any.
// It returns the empty string otherwise.
func serverKeyId() string {
	serverConfigFile := osutil.UserServerConfigPath()
	if _, err := os.Stat(serverConfigFile); err != nil {
		if os.IsNotExist(err) {
			return ""
		}
		log.Fatalf("Could not stat %v: %v", serverConfigFile, err)
	}
	obj, err := jsonconfig.ReadFile(serverConfigFile)
	if err != nil {
		return ""
	}
	keyId, ok := obj["identity"].(string)
	if !ok {
		return ""
	}
	return keyId
}

func cleanServer(server string) string {
	// Remove trailing slash if provided.
	if strings.HasSuffix(server, "/") {
		server = server[0 : len(server)-1]
	}
	// Default to "https://" when not specified
	if !strings.HasPrefix(server, "http") && !strings.HasPrefix(server, "https") {
		server = "https://" + server
	}
	return server
}

func serverOrDie() string {
	if flagServer != "" {
		return cleanServer(flagServer)
	}
	configOnce.Do(parseConfig)
	server := cleanServer(config.server)
	if server == "" {
		log.Fatalf("Missing or invalid \"server\" in %q", osutil.UserClientConfigPath())
	}
	return server
}

func (c *Client) useTLS() bool {
	return strings.HasPrefix(c.server, "https://")
}

// SetupAuth sets the client's authMode from the client
// configuration file or from the environment.
func (c *Client) SetupAuth() error {
	if flagServer != "" {
		// If using an explicit blobserver, don't use auth
		// configured from the config file, so we don't send
		// our password to a friend's blobserver.
		var err error
		c.authMode, err = auth.FromEnv()
		if err == auth.ErrNoAuth {
			log.Printf("Using explicit --server parameter; not using config file auth, and no auth mode set in environment")
		}
		return err
	}
	configOnce.Do(parseConfig)
	var err error
	if config.auth == "" {
		c.authMode, err = auth.FromEnv()
	} else {
		c.authMode, err = auth.FromConfig(config.auth)
	}
	return err
}

// SetupAuthFromString configures the clients authentication mode from
// an explicit auth string.
func (c *Client) SetupAuthFromString(a string) error {
	var err error
	c.authMode, err = auth.FromConfig(a)
	return err
}

// SecretRingFile returns the filename to the user's GPG secret ring.
// The value comes from either a command-line flag, the
// CAMLI_SECRET_RING environment variable, the client config file's
// "identitySecretRing" value, or the operating system default location.
func (c *Client) SecretRingFile() string {
	if flagSecretRing != "" {
		return flagSecretRing
	}
	if e := os.Getenv("CAMLI_SECRET_RING"); e != "" {
		return e
	}
	configOnce.Do(parseConfig)
	return config.identitySecretRing
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

// SignerPublicKeyBlobref returns the blobref of signer's public key.
// The blobref may not be valid (zero blob.Ref) if e.g the configuration
// is invalid or incomplete.
func (c *Client) SignerPublicKeyBlobref() blob.Ref {
	c.initSignerPublicKeyBlobrefOnce.Do(c.initSignerPublicKeyBlobref)
	return c.signerPublicKeyRef
}

func (c *Client) initSignerPublicKeyBlobref() {
	configOnce.Do(parseConfig)
	keyId := config.identity
	if keyId == "" {
		log.Fatalf("No 'identity' key in JSON configuration file %q; have you run \"camput init\"?", osutil.UserClientConfigPath())
	}
	keyRing := c.SecretRingFile()
	if !fileExists(keyRing) {
		log.Fatalf("Could not find keyId %q, because secret ring file %q does not exist.", keyId, keyRing)
	}
	entity, err := jsonsign.EntityFromSecring(keyId, keyRing)
	if err != nil {
		log.Fatalf("Couldn't find keyId %q in secret ring %v: %v", keyId, keyRing, err)
	}
	armored, err := jsonsign.ArmoredPublicKey(entity)
	if err != nil {
		log.Fatalf("Error serializing public key: %v", err)
	}

	// TODO(mpl): completely get rid of it if possible
	// http://camlistore.org/issue/377
	selfPubKeyDir := osutil.KeyBlobsDir()
	fi, err := os.Stat(selfPubKeyDir)
	if err != nil || !fi.IsDir() {
		log.Fatalf("selfPubKeyDir as %q doesn't exist or not a directory", selfPubKeyDir)
	}

	br := blob.SHA1FromString(armored)
	pubFile := filepath.Join(selfPubKeyDir, br.String()+".camli")
	fi, err = os.Stat(pubFile)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("Could not stat %q: %v", pubFile, err)
		}
		err = ioutil.WriteFile(pubFile, []byte(armored), 0644)
		if err != nil {
			log.Fatalf("Error writing public key to %q: %v", pubFile, err)
		}
	}
	c.signerPublicKeyRef = br
	c.publicKeyArmored = armored
}

// config[trustedCerts] is the list of trusted certificates fingerprints.
// Case insensitive.
// See Client.trustedCerts in client.go
const trustedCerts = "trustedCerts"

func (c *Client) initTrustedCerts() {
	if e := os.Getenv("CAMLI_TRUSTED_CERT"); e != "" {
		c.trustedCerts = strings.Split(e, ",")
		return
	}
	c.trustedCerts = []string{}
	configOnce.Do(parseConfig)
	if config.trustedCerts == nil {
		return
	}
	for _, trustedCert := range config.trustedCerts {
		c.trustedCerts = append(c.trustedCerts, strings.ToLower(trustedCert))
	}
}

func (c *Client) getTrustedCerts() []string {
	c.initTrustedCertsOnce.Do(c.initTrustedCerts)
	return c.trustedCerts
}

// config[ignoredFiles] is the list of files that camput should ignore
// and not try to upload.
// See Client.ignoredFiles in client.go
const ignoredFiles = "ignoredFiles"

func (c *Client) initIgnoredFiles() {
	if e := os.Getenv("CAMLI_IGNORED_FILES"); e != "" {
		c.ignoredFiles = strings.Split(e, ",")
		return
	}
	c.ignoredFiles = []string{}
	configOnce.Do(parseConfig)
	if config.ignoredFiles == nil {
		return
	}
	c.ignoredFiles = config.ignoredFiles
}

func (c *Client) getIgnoredFiles() []string {
	c.initIgnoredFilesOnce.Do(c.initIgnoredFiles)
	return c.ignoredFiles
}
<commit_msg>client: fix crashes when used by the Android app<commit_after>/*
Copyright 2011 The Camlistore Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"camlistore.org/pkg/auth"
	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/client/android"
	"camlistore.org/pkg/jsonconfig"
	"camlistore.org/pkg/jsonsign"
	"camlistore.org/pkg/osutil"
)

// These, if set, override the JSON config file
// ~/.config/camlistore/client-config.json
// (i.e. osutil.UserClientConfigPath()) "server" and "password" keys.
//
// A main binary must call AddFlags to expose these.
var (
	flagServer     string
	flagSecretRing string
)

func AddFlags() {
	defaultPath := osutil.UserClientConfigPath()
	flag.StringVar(&flagServer, "server", "", "Camlistore server prefix. If blank, the default from the \"server\" field of "+defaultPath+" is used. Acceptable forms: https://you.example.com, example.com:1345 (https assumed), or http://you.example.com/alt-root")
	flag.StringVar(&flagSecretRing, "secret-keyring", "", "GnuPG secret keyring file to use.")
}

// ExplicitServer returns the blobserver given in the flags, if any.
func ExplicitServer() string {
	return flagServer
}

var configOnce sync.Once
var config *clientConfig

// clientConfig holds the values found in the JSON client config file
// once it's been parsed and validated by parseConfig.
// Unless otherwise specified by the comments, no default values were
// used when parsing.
type clientConfig struct {
	auth               string
	server             string
	identity           string
	identitySecretRing string // defaults to osutil.IdentitySecretRing()
	trustedCerts       []string
	ignoredFiles       []string
}

func parseConfig() {
	if android.OnAndroid() {
		return
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
		if keyId := serverKeyId(); keyId != "" {
			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
			errMsg += hint
		}
		log.Fatal(errMsg)
	}

	conf, err := jsonconfig.ReadFile(configPath)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := jsonconfig.Obj(conf)
	config = &clientConfig{
		auth:               cfg.OptionalString("auth", ""),
		server:             cfg.OptionalString("server", ""),
		identity:           cfg.OptionalString("identity", ""),
		identitySecretRing: cfg.OptionalString("identitySecretRing", osutil.IdentitySecretRing()),
		trustedCerts:       cfg.OptionalList("trustedCerts"),
		ignoredFiles:       cfg.OptionalList("ignoredFiles"),
	}
	if err := cfg.Validate(); err != nil {
		printConfigChangeHelp(cfg)
		log.Fatalf("Error in config file: %v", err)
	}
}

// printConfigChangeHelp checks if conf contains obsolete keys,
// and prints additional help in this case.
func printConfigChangeHelp(conf jsonconfig.Obj) {
	// rename maps from old key names to the new ones.
	// If there is no new one, the value is the empty string.
	rename := map[string]string{
		"keyId":            "identity",
		"publicKeyBlobref": "",
		"selfPubKeyDir":    "",
		"secretRing":       "identitySecretRing",
	}
	oldConfig := false
	configChangedMsg := fmt.Sprintf("The client configuration file (%s) keys have changed.\n", osutil.UserClientConfigPath())
	for _, unknown := range conf.UnknownKeys() {
		for k, v := range rename {
			if unknown == k {
				if v != "" {
					configChangedMsg += fmt.Sprintf("%q should be renamed %q.\n", k, v)
				} else {
					configChangedMsg += fmt.Sprintf("%q should be removed.\n", k)
				}
				oldConfig = true
				break
			}
		}
	}
	if oldConfig {
		configChangedMsg += "Please see http://camlistore.org/docs/client-config, or use camput init to recreate a default one."
		log.Print(configChangedMsg)
	}
}

// serverKeyId returns the public gpg key id ("identity" field)
// from the user's server config , if any.
// It returns the empty string otherwise.
func serverKeyId() string {
	serverConfigFile := osutil.UserServerConfigPath()
	if _, err := os.Stat(serverConfigFile); err != nil {
		if os.IsNotExist(err) {
			return ""
		}
		log.Fatalf("Could not stat %v: %v", serverConfigFile, err)
	}
	obj, err := jsonconfig.ReadFile(serverConfigFile)
	if err != nil {
		return ""
	}
	keyId, ok := obj["identity"].(string)
	if !ok {
		return ""
	}
	return keyId
}

func cleanServer(server string) string {
	// Remove trailing slash if provided.
	if strings.HasSuffix(server, "/") {
		server = server[0 : len(server)-1]
	}
	// Default to "https://" when not specified
	if !strings.HasPrefix(server, "http") && !strings.HasPrefix(server, "https") {
		server = "https://" + server
	}
	return server
}

func serverOrDie() string {
	if flagServer != "" {
		return cleanServer(flagServer)
	}
	configOnce.Do(parseConfig)
	server := cleanServer(config.server)
	if server == "" {
		log.Fatalf("Missing or invalid \"server\" in %q", osutil.UserClientConfigPath())
	}
	return server
}

func (c *Client) useTLS() bool {
	return strings.HasPrefix(c.server, "https://")
}

// SetupAuth sets the client's authMode from the client
// configuration file or from the environment.
func (c *Client) SetupAuth() error {
	if flagServer != "" {
		// If using an explicit blobserver, don't use auth
		// configured from the config file, so we don't send
		// our password to a friend's blobserver.
		var err error
		c.authMode, err = auth.FromEnv()
		if err == auth.ErrNoAuth {
			log.Printf("Using explicit --server parameter; not using config file auth, and no auth mode set in environment")
		}
		return err
	}
	configOnce.Do(parseConfig)
	var err error
	if config == nil || config.auth == "" {
		c.authMode, err = auth.FromEnv()
	} else {
		c.authMode, err = auth.FromConfig(config.auth)
	}
	return err
}

// SetupAuthFromString configures the clients authentication mode from
// an explicit auth string.
func (c *Client) SetupAuthFromString(a string) error {
	var err error
	c.authMode, err = auth.FromConfig(a)
	return err
}

// SecretRingFile returns the filename to the user's GPG secret ring.
// The value comes from either a command-line flag, the
// CAMLI_SECRET_RING environment variable, the client config file's
// "identitySecretRing" value, or the operating system default location.
func (c *Client) SecretRingFile() string {
	if flagSecretRing != "" {
		return flagSecretRing
	}
	if e := os.Getenv("CAMLI_SECRET_RING"); e != "" {
		return e
	}
	configOnce.Do(parseConfig)
	if config == nil || config.identitySecretRing == "" {
		return osutil.IdentitySecretRing()
	}
	return config.identitySecretRing
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

// SignerPublicKeyBlobref returns the blobref of signer's public key.
// The blobref may not be valid (zero blob.Ref) if e.g the configuration
// is invalid or incomplete.
func (c *Client) SignerPublicKeyBlobref() blob.Ref {
	c.initSignerPublicKeyBlobrefOnce.Do(c.initSignerPublicKeyBlobref)
	return c.signerPublicKeyRef
}

func (c *Client) initSignerPublicKeyBlobref() {
	configOnce.Do(parseConfig)
	keyId := config.identity
	if keyId == "" {
		log.Fatalf("No 'identity' key in JSON configuration file %q; have you run \"camput init\"?", osutil.UserClientConfigPath())
	}
	keyRing := c.SecretRingFile()
	if !fileExists(keyRing) {
		log.Fatalf("Could not find keyId %q, because secret ring file %q does not exist.", keyId, keyRing)
	}
	entity, err := jsonsign.EntityFromSecring(keyId, keyRing)
	if err != nil {
		log.Fatalf("Couldn't find keyId %q in secret ring %v: %v", keyId, keyRing, err)
	}
	armored, err := jsonsign.ArmoredPublicKey(entity)
	if err != nil {
		log.Fatalf("Error serializing public key: %v", err)
	}

	// TODO(mpl): completely get rid of it if possible
	// http://camlistore.org/issue/377
	selfPubKeyDir := osutil.KeyBlobsDir()
	fi, err := os.Stat(selfPubKeyDir)
	if err != nil || !fi.IsDir() {
		log.Fatalf("selfPubKeyDir as %q doesn't exist or not a directory", selfPubKeyDir)
	}

	br := blob.SHA1FromString(armored)
	pubFile := filepath.Join(selfPubKeyDir, br.String()+".camli")
	fi, err = os.Stat(pubFile)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("Could not stat %q: %v", pubFile, err)
		}
		err = ioutil.WriteFile(pubFile, []byte(armored), 0644)
		if err != nil {
			log.Fatalf("Error writing public key to %q: %v", pubFile, err)
		}
	}
	c.signerPublicKeyRef = br
	c.publicKeyArmored = armored
}

// config[trustedCerts] is the list of trusted certificates fingerprints.
// Case insensitive.
// See Client.trustedCerts in client.go
const trustedCerts = "trustedCerts"

func (c *Client) initTrustedCerts() {
	if e := os.Getenv("CAMLI_TRUSTED_CERT"); e != "" {
		c.trustedCerts = strings.Split(e, ",")
		return
	}
	c.trustedCerts = []string{}
	configOnce.Do(parseConfig)
	if config == nil || config.trustedCerts == nil {
		return
	}
	for _, trustedCert := range config.trustedCerts {
		c.trustedCerts = append(c.trustedCerts, strings.ToLower(trustedCert))
	}
}

func (c *Client) getTrustedCerts() []string {
	c.initTrustedCertsOnce.Do(c.initTrustedCerts)
	return c.trustedCerts
}

// config[ignoredFiles] is the list of files that camput should ignore
// and not try to upload.
// See Client.ignoredFiles in client.go
const ignoredFiles = "ignoredFiles"

func (c *Client) initIgnoredFiles() {
	if e := os.Getenv("CAMLI_IGNORED_FILES"); e != "" {
		c.ignoredFiles = strings.Split(e, ",")
		return
	}
	c.ignoredFiles = []string{}
	configOnce.Do(parseConfig)
	if config == nil || config.ignoredFiles == nil {
		return
	}
	c.ignoredFiles = config.ignoredFiles
}

func (c *Client) getIgnoredFiles() []string {
	c.initIgnoredFilesOnce.Do(c.initIgnoredFiles)
	return c.ignoredFiles
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"log"

	"github.com/mateusbraga/freestore/pkg/comm"
	"github.com/mateusbraga/freestore/pkg/view"
)

// diffResultsErr is returned by readQuorum if not all answers from the servers
// were the same. It is used to indicate that Read() should do 2nd phase of the
// read protocol.
var diffResultsErr = errors.New("Read Divergence")

// readQuorum asks for the register value of all members from the current view.
// It returns the most recent value after it receives answers from a majority.
// If the client's view needs to be updated, it will update it and retry.  If
// values returned by the processes differ, it will return diffResultsErr.
func (thisClient *Client) readQuorum() (RegisterMsg, error) {
	destinationView, viewRef := thisClient.ViewAndViewRef()

	// Send write request to all
	resultChan := make(chan RegisterMsg, destinationView.NumberOfMembers())
	go broadcastRead(destinationView, viewRef, resultChan)

	// Wait for quorum
	var failedTotal int
	var resultArray []RegisterMsg
	var finalValue RegisterMsg
	for {
		receivedValue := <-resultChan

		// count success or fail
		if receivedValue.Err != nil {
			if oldViewError, ok := receivedValue.Err.(*view.OldViewError); ok {
				if destinationView.LessUpdatedThan(oldViewError.NewView) {
					log.Println("View updated during read quorum")
					thisClient.updateCurrentView(oldViewError.NewView)
					return thisClient.readQuorum()
				}
				// oldViewError.NewView is actually not more updated than current view, count as failed
				log.Println("+1 process has old view")
			}

			//log.Println("+1 error to read:", err)
			failedTotal++
		} else {
			resultArray = append(resultArray, receivedValue)

			if receivedValue.Timestamp > finalValue.Timestamp {
				finalValue = receivedValue
			}
		}

		// check conditions. this is done here to handle when a quorum leaves
		// the system. In this case, most processes would fail but we should
		// wait for one (or all) that will tell the client the updated view.
		everyProcessReturned := len(resultArray)+failedTotal == destinationView.NumberOfMembers()
		systemFailed := everyProcessReturned && failedTotal > destinationView.NumberOfToleratedFaults()
		if systemFailed {
			// maybe all processes from the view left, try to get the new view
			if thisClient.couldGetNewView() {
				return thisClient.readQuorum()
			} else {
				return RegisterMsg{}, errors.New("Failed to get read quorun")
			}
		}

		if len(resultArray) == destinationView.QuorumSize() {
			// Look for divergence on values received
			for _, val := range resultArray {
				if finalValue.Timestamp != val.Timestamp {
					return finalValue, diffResultsErr
				}
			}
			return finalValue, nil
		}
	}
}

// writeQuorum tries to write the value on writeMsg in the register of all
// processes on client's current view. It returns when it gets confirmation
// from a majority.  If the client's view needs to be updated, it will update
// it and retry.
func (thisClient *Client) writeQuorum(writeMsg RegisterMsg) error {
	destinationView, viewRef := thisClient.ViewAndViewRef()

	writeMsg.ViewRef = viewRef

	// Send write request to all
	resultChan := make(chan RegisterMsg, destinationView.NumberOfMembers())
	go broadcastWrite(destinationView, writeMsg, resultChan)

	// Wait for quorum
	var successTotal int
	var failedTotal int
	for {
		receivedValue := <-resultChan

		// count success or fail
		if receivedValue.Err != nil {
			if oldViewError, ok := receivedValue.Err.(*view.OldViewError); ok {
				if destinationView.LessUpdatedThan(oldViewError.NewView) {
					log.Println("View updated during write quorum")
					thisClient.updateCurrentView(oldViewError.NewView)
					return thisClient.writeQuorum(writeMsg)
				}
				// oldViewError.NewView is actually not more updated than current view, count as failed
				log.Println("+1 process has old view")
				// TODO remove continue, and try resending request
				continue
			}

			//log.Println("+1 error to write:", err)
			failedTotal++
		} else {
			successTotal++
		}

		// check conditions. this is done here to handle when a quorum leaves
		// the system. In this case, most processes would fail but we should
		// wait for one (or all) that will tell the client the updated view.
		everyProcessReturned := successTotal+failedTotal == destinationView.NumberOfMembers()
		systemFailed := everyProcessReturned && failedTotal > destinationView.NumberOfToleratedFaults()
		if systemFailed {
			// maybe all processes from the view left, try to get the new view
			if thisClient.couldGetNewView() {
				return thisClient.writeQuorum(writeMsg)
			} else {
				return errors.New("Failed to get write quorun")
			}
		}

		if successTotal == destinationView.QuorumSize() {
			return nil
		}
	}
}

func (thisClient *Client) couldGetNewView() bool {
	view, err := thisClient.getFurtherViewsFunc()
	if err != nil {
		return false
	}

	if view.LessUpdatedThan(thisClient.View()) {
		return false
	}

	thisClient.updateCurrentView(view)
	return true
}

func sendRead(process view.Process, viewRef view.ViewRef, resultChan chan RegisterMsg) {
	var result RegisterMsg
	err := comm.SendRPCRequest(process, "RegisterService.Read", viewRef, &result)
	if err != nil {
		resultChan <- RegisterMsg{Err: err}
		return
	}

	resultChan <- result
}

func broadcastRead(destinationView *view.View, viewRef view.ViewRef, resultChan chan RegisterMsg) {
	for _, process := range destinationView.GetMembers() {
		go sendRead(process, viewRef, resultChan)
	}
}

func sendWrite(process view.Process, writeMsg *RegisterMsg, resultChan chan RegisterMsg) {
	var result RegisterMsg
	err := comm.SendRPCRequest(process, "RegisterService.Write", writeMsg, &result)
	if err != nil {
		resultChan <- RegisterMsg{Err: err}
		return
	}
	resultChan <- result
}

func broadcastWrite(destinationView *view.View, writeMsg RegisterMsg, resultChan chan RegisterMsg) {
	for _, process := range destinationView.GetMembers() {
		go sendWrite(process, &writeMsg, resultChan)
	}
}
<commit_msg>Fix couldGetNewView<commit_after>package client

import (
	"errors"
	"log"

	"github.com/mateusbraga/freestore/pkg/comm"
	"github.com/mateusbraga/freestore/pkg/view"
)

// diffResultsErr is returned by readQuorum if not all answers from the servers
// were the same. It is used to indicate that Read() should do 2nd phase of the
// read protocol.
var diffResultsErr = errors.New("Read Divergence")

// readQuorum asks for the register value of all members from the current view.
// It returns the most recent value after it receives answers from a majority.
// If the client's view needs to be updated, it will update it and retry.  If
// values returned by the processes differ, it will return diffResultsErr.
func (thisClient *Client) readQuorum() (RegisterMsg, error) {
	destinationView, viewRef := thisClient.ViewAndViewRef()

	// Send write request to all
	resultChan := make(chan RegisterMsg, destinationView.NumberOfMembers())
	go broadcastRead(destinationView, viewRef, resultChan)

	// Wait for quorum
	var failedTotal int
	var resultArray []RegisterMsg
	var finalValue RegisterMsg
	for {
		receivedValue := <-resultChan

		// count success or fail
		if receivedValue.Err != nil {
			if oldViewError, ok := receivedValue.Err.(*view.OldViewError); ok {
				if destinationView.LessUpdatedThan(oldViewError.NewView) {
					log.Println("View updated during read quorum")
					thisClient.updateCurrentView(oldViewError.NewView)
					return thisClient.readQuorum()
				}
				// oldViewError.NewView is actually not more updated than current view, count as failed
				log.Println("+1 process has old view")
			}

			//log.Println("+1 error to read:", err)
			failedTotal++
		} else {
			resultArray = append(resultArray, receivedValue)

			if receivedValue.Timestamp > finalValue.Timestamp {
				finalValue = receivedValue
			}
		}

		// check conditions. this is done here to handle when a quorum leaves
		// the system. In this case, most processes would fail but we should
		// wait for one (or all) that will tell the client the updated view.
		everyProcessReturned := len(resultArray)+failedTotal == destinationView.NumberOfMembers()
		systemFailed := everyProcessReturned && failedTotal > destinationView.NumberOfToleratedFaults()
		if systemFailed {
			// maybe all processes from the view left, try to get the new view
			if thisClient.couldGetNewView() {
				return thisClient.readQuorum()
			} else {
				return RegisterMsg{}, errors.New("Failed to get read quorun")
			}
		}

		if len(resultArray) == destinationView.QuorumSize() {
			// Look for divergence on values received
			for _, val := range resultArray {
				if finalValue.Timestamp != val.Timestamp {
					return finalValue, diffResultsErr
				}
			}
			return finalValue, nil
		}
	}
}

// writeQuorum tries to write the value on writeMsg in the register of all
// processes on client's current view. It returns when it gets confirmation
// from a majority.  If the client's view needs to be updated, it will update
// it and retry.
func (thisClient *Client) writeQuorum(writeMsg RegisterMsg) error {
	destinationView, viewRef := thisClient.ViewAndViewRef()

	writeMsg.ViewRef = viewRef

	// Send write request to all
	resultChan := make(chan RegisterMsg, destinationView.NumberOfMembers())
	go broadcastWrite(destinationView, writeMsg, resultChan)

	// Wait for quorum
	var successTotal int
	var failedTotal int
	for {
		receivedValue := <-resultChan

		// count success or fail
		if receivedValue.Err != nil {
			if oldViewError, ok := receivedValue.Err.(*view.OldViewError); ok {
				if destinationView.LessUpdatedThan(oldViewError.NewView) {
					log.Println("View updated during write quorum")
					thisClient.updateCurrentView(oldViewError.NewView)
					return thisClient.writeQuorum(writeMsg)
				}
				// oldViewError.NewView is actually not more updated than current view, count as failed
				log.Println("+1 process has old view")
				// TODO remove continue, and try resending request
				continue
			}

			//log.Println("+1 error to write:", err)
			failedTotal++
		} else {
			successTotal++
		}

		// check conditions. this is done here to handle when a quorum leaves
		// the system. In this case, most processes would fail but we should
		// wait for one (or all) that will tell the client the updated view.
		everyProcessReturned := successTotal+failedTotal == destinationView.NumberOfMembers()
		systemFailed := everyProcessReturned && failedTotal > destinationView.NumberOfToleratedFaults()
		if systemFailed {
			// maybe all processes from the view left, try to get the new view
			if thisClient.couldGetNewView() {
				return thisClient.writeQuorum(writeMsg)
			} else {
				return errors.New("Failed to get write quorun")
			}
		}

		if successTotal == destinationView.QuorumSize() {
			return nil
		}
	}
}

func (thisClient *Client) couldGetNewView() bool {
	view, err := thisClient.getFurtherViewsFunc()
	if err != nil {
		return false
	}

	cv := thisClient.View()
	if view.LessUpdatedThan(cv) || view.Equal(cv) {
		return false
	}

	thisClient.updateCurrentView(view)
	return true
}

func sendRead(process view.Process, viewRef view.ViewRef, resultChan chan RegisterMsg) {
	var result RegisterMsg
	err := comm.SendRPCRequest(process, "RegisterService.Read", viewRef, &result)
	if err != nil {
		resultChan <- RegisterMsg{Err: err}
		return
	}

	resultChan <- result
}

func broadcastRead(destinationView *view.View, viewRef view.ViewRef, resultChan chan RegisterMsg) {
	for _, process := range destinationView.GetMembers() {
		go sendRead(process, viewRef, resultChan)
	}
}

func sendWrite(process view.Process, writeMsg *RegisterMsg, resultChan chan RegisterMsg) {
	var result RegisterMsg
	err := comm.SendRPCRequest(process, "RegisterService.Write", writeMsg, &result)
	if err != nil {
		resultChan <- RegisterMsg{Err: err}
		return
	}
	resultChan <- result
}

func broadcastWrite(destinationView *view.View, writeMsg RegisterMsg, resultChan chan RegisterMsg) {
	for _, process := range destinationView.GetMembers() {
		go sendWrite(process, &writeMsg, resultChan)
	}
}
<|endoftext|>


<commit_before>package config

type DaemonConfig struct {
	Providers Providers `yaml:"providers"`
	Version   string    `yaml:"version"`
}

type Providers struct {
	Aws        []Aws        `yaml:"aws"`
	Vsphere    []Vsphere    `yaml:"vsphere"`
	Virtualbox []Virtualbox `yaml:"virtualbox"`
	Qemu       []Qemu       `yaml:"qemu"`
}

type Aws struct {
	Name   string `yaml:"name"`
	Region string `yaml:"region"`
	Zone   string `yaml:"zone"`
}

type Vsphere struct {
	Name            string `yaml:"name"`
	VsphereUser     string `yaml:"vsphere_user"`
	VspherePassword string `yaml:"vsphere_password"`
	VsphereURL      string `yaml:"vsphere_url"`
	Datastore       string `yaml:"datastore"`
	Datacenter      string `yaml:"datacenter"`
	NetworkLabel    string `yaml:"network"`
}

type Virtualbox struct {
	Name                  string                `yaml:"name"`
	AdapterName           string                `yaml:"adapter_name"`
	VirtualboxAdapterType VirtualboxAdapterType `yaml:"adapter_type"`
}

type Qemu struct {
	Name string `yaml:"name"`
	NoGraphic bool `yaml:"no_graphic"`
}

const (
	QemuKernelFileName = "kernel"
	QemuArgsFileName   = "args"
)

type VirtualboxAdapterType string

const (
	BridgedAdapter  = VirtualboxAdapterType("bridged")
	HostOnlyAdapter = VirtualboxAdapterType("host_only")
)

type ClientConfig struct {
	Host string `yaml:"host"`
}
<commit_msg>remove unused const<commit_after>package config

type DaemonConfig struct {
	Providers Providers `yaml:"providers"`
	Version   string    `yaml:"version"`
}

type Providers struct {
	Aws        []Aws        `yaml:"aws"`
	Vsphere    []Vsphere    `yaml:"vsphere"`
	Virtualbox []Virtualbox `yaml:"virtualbox"`
	Qemu       []Qemu       `yaml:"qemu"`
}

type Aws struct {
	Name   string `yaml:"name"`
	Region string `yaml:"region"`
	Zone   string `yaml:"zone"`
}

type Vsphere struct {
	Name            string `yaml:"name"`
	VsphereUser     string `yaml:"vsphere_user"`
	VspherePassword string `yaml:"vsphere_password"`
	VsphereURL      string `yaml:"vsphere_url"`
	Datastore       string `yaml:"datastore"`
	Datacenter      string `yaml:"datacenter"`
	NetworkLabel    string `yaml:"network"`
}

type Virtualbox struct {
	Name                  string                `yaml:"name"`
	AdapterName           string                `yaml:"adapter_name"`
	VirtualboxAdapterType VirtualboxAdapterType `yaml:"adapter_type"`
}

type Qemu struct {
	Name string `yaml:"name"`
	NoGraphic bool `yaml:"no_graphic"`
}

type VirtualboxAdapterType string

const (
	BridgedAdapter  = VirtualboxAdapterType("bridged")
	HostOnlyAdapter = VirtualboxAdapterType("host_only")
)

type ClientConfig struct {
	Host string `yaml:"host"`
}
<|endoftext|>


<commit_before>package config

// SemVer is the version of mockery at build time.
var SemVer = "v0.0.0-dev"

func GetSemverInfo() string {
    return SemVer
}

type Config struct {
	All                  bool
	BuildTags            string `mapstructure:"tags"`
	Case                 string
	Config               string
	Cpuprofile           string
	Dir                  string
	DisableVersionString bool `mapstructure:"disable-version-string"`
	DryRun               bool `mapstructure:"dry-run"`
	Exported             bool `mapstructure:"exported"`
	FileName             string
	InPackage            bool
	KeepTree             bool
	LogLevel             string `mapstructure:"log-level"`
	Name                 string
	Note                 string
	Outpkg               string
	Packageprefix        string
	Output               string
	Print                bool
	Profile              string
	Quiet                bool
	Recursive            bool
	SrcPkg               string
	BoilerplateFile      string `mapstructure:"boilerplate-file"`
	// StructName overrides the name given to the mock struct and should only be nonempty
	// when generating for an exact match (non regex expression in -name).
	StructName     string
	Tags           string
	TestOnly       bool
	UnrollVariadic bool `mapstructure:"unroll-variadic"`
	Version        bool
}
<commit_msg>version string logic updated to use v0.0.0-dev when no string set<commit_after>package config

import "runtime/debug"

const (
	_defaultSemVer = "v0.0.0-dev"
)

// SemVer is the version of mockery at build time.
var SemVer = ""

func GetSemverInfo() string {
	if SemVer != "" {
		return SemVer
	}
	version, ok := debug.ReadBuildInfo()
	if ok && version.Main.Version != "(devel)" {
		return version.Main.Version
	}
	return _defaultSemVer
}

type Config struct {
	All                  bool
	BuildTags            string `mapstructure:"tags"`
	Case                 string
	Config               string
	Cpuprofile           string
	Dir                  string
	DisableVersionString bool `mapstructure:"disable-version-string"`
	DryRun               bool `mapstructure:"dry-run"`
	Exported             bool `mapstructure:"exported"`
	FileName             string
	InPackage            bool
	KeepTree             bool
	LogLevel             string `mapstructure:"log-level"`
	Name                 string
	Note                 string
	Outpkg               string
	Packageprefix        string
	Output               string
	Print                bool
	Profile              string
	Quiet                bool
	Recursive            bool
	SrcPkg               string
	BoilerplateFile      string `mapstructure:"boilerplate-file"`
	// StructName overrides the name given to the mock struct and should only be nonempty
	// when generating for an exact match (non regex expression in -name).
	StructName     string
	Tags           string
	TestOnly       bool
	UnrollVariadic bool `mapstructure:"unroll-variadic"`
	Version        bool
}
<|endoftext|>


<commit_before>package daemon

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/rivine/rivine/api"
	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/modules/blockcreator"
	"github.com/rivine/rivine/modules/consensus"
	"github.com/rivine/rivine/modules/explorer"
	"github.com/rivine/rivine/modules/gateway"
	"github.com/rivine/rivine/modules/transactionpool"
	"github.com/rivine/rivine/modules/wallet"
	"github.com/rivine/rivine/types"

	"github.com/bgentry/speakeasy"
)

// Config contains all configurable variables for rivined.
type Config struct {
	BlockchainInfo types.BlockchainInfo

	// the password required to use the http api,
	// if `AuthenticateAPI` is true, and the password is the empty string,
	// a password will be prompted when the daemon starts
	APIPassword string

	// the host:port for the HTTP API to listen on.
	// If `AllowAPIBind` is false, only localhost hosts are allowed
	APIaddr string
	// the host:port to listen for RPC calls
	RPCaddr string
	// indicates that the http API can listen on a non localhost address.
	//  If this is true, then the AuthenticateAPI parameter
	// must also be true
	AllowAPIBind bool

	// the modules to enable, this string must contain one letter
	// for each module (order does not matter).
	// All required modules must be specified,
	// if a required (parent) module is not present,
	// an error is returned
	Modules string
	// indicates that the daemon should not try to connect to
	// the bootstrap nodes
	NoBootstrap bool
	// the user agent required to connect to the http api.
	RequiredUserAgent string
	// indicates if the http api is password protected
	AuthenticateAPI bool

	// indicates if profile info should be collected while
	// the daemon is running
	Profile bool
	// name of the directory to store the profile info,
	// should this be collected
	ProfileDir string
	// the parent directory where the individual module
	// directories will be created
	RootPersistentDir string

	// Network defines the network config to use
	NetworkName string
	// optional network config constructor,
	// if you're implementing your own rivine-based blockchain,
	// you'll probably want to define this one,
	// as otherwise a pure rivine blockchain config will be created
	CreateNetworConfig func(name string) (NetworkConfig, error)
}

// DefaultConfig returns the default daemon configuration
func DefaultConfig() Config {
	return Config{
		BlockchainInfo: types.DefaultBlockchainInfo(),

		APIPassword: "",

		APIaddr:      "localhost:23110",
		RPCaddr:      ":23112",
		AllowAPIBind: false,

		Modules:           "cgtwb",
		NoBootstrap:       false,
		RequiredUserAgent: "Rivine-Agent",
		AuthenticateAPI:   false,

		Profile:           false,
		ProfileDir:        "profiles",
		RootPersistentDir: "",

		NetworkName: build.Release,
	}
}

func (cfg *Config) createConfiguredNetworkConfig() (NetworkConfig, error) {
	if cfg.NetworkName == "" {
		// default to build.Release as network name
		cfg.NetworkName = build.Release
	}
	if cfg.CreateNetworConfig != nil {
		// use custom network config creator
		return cfg.CreateNetworConfig(cfg.NetworkName)
	}

	// use default network config creator
	networkCfg := NetworkConfig{
		Constants: types.DefaultChainConstants(),
	}
	if cfg.NetworkName == "standard" {
		networkCfg.BootstrapPeers = []modules.NetAddress{
			"136.243.144.132:23112",
			"[2a01:4f8:171:1303::2]:23112",
			"bootstrap2.rivine.io:23112",
			"bootstrap3.rivine.io:23112",
		}
	}
	return networkCfg, nil
}

// verifyAPISecurity checks that the security values are consistent with a
// sane, secure system.
func verifyAPISecurity(cfg Config) error {
	// Make sure that only the loopback address is allowed unless the
	// --disable-api-security flag has been used.
	if !cfg.AllowAPIBind {
		addr := modules.NetAddress(cfg.APIaddr)
		if !addr.IsLoopback() {
			if addr.Host() == "" {
				return fmt.Errorf("a blank host will listen on all interfaces, did you mean localhost:%v?\nyou must pass --disable-api-security to bind daemon of %s to a non-localhost address", addr.Port(), cfg.BlockchainInfo.Name)
			}
			return fmt.Errorf("you must pass --disable-api-security to bind daemon of %s to a non-localhost address", cfg.BlockchainInfo.Name)
		}
		return nil
	}

	// If the --disable-api-security flag is used, enforce that
	// --authenticate-api must also be used.
	if cfg.AllowAPIBind && !cfg.AuthenticateAPI {
		return errors.New("cannot use --disable-api-security without setting an api password")
	}
	return nil
}

// processNetAddr adds a ':' to a bare integer, so that it is a proper port
// number.
func processNetAddr(addr string) string {
	_, err := strconv.Atoi(addr)
	if err == nil {
		return ":" + addr
	}
	return addr
}

// processModules makes the modules string lowercase to make checking if a
// module in the string easier, and returns an error if the string contains an
// invalid module character.
func processModules(modules string) (string, error) {
	modules = strings.ToLower(modules)
	validModules := "cgtwebd"
	invalidModules := modules
	for _, m := range validModules {
		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
	}
	if len(invalidModules) > 0 {
		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
	}
	return modules, nil
}

// processConfig checks the configuration values and performs cleanup on
// incorrect-but-allowed values.
func processConfig(config *Config) error {
	var err1 error
	config.APIaddr = processNetAddr(config.APIaddr)
	config.RPCaddr = processNetAddr(config.RPCaddr)
	config.Modules, err1 = processModules(config.Modules)
	err2 := verifyAPISecurity(*config)
	return build.JoinErrors([]error{err1, err2}, ", and ")
}

// StartDaemon uses the config parameters
// to initialize Rivine modules and start
func StartDaemon(cfg Config) (err error) {
	networkConfig, err := cfg.createConfiguredNetworkConfig()
	if err != nil {
		return
	}

	err = networkConfig.Constants.Validate()
	if err != nil {
		return err
	}
	// Silently append a subdirectory for storage with the name of the network so we don't create conflicts
	cfg.RootPersistentDir = filepath.Join(cfg.RootPersistentDir, cfg.NetworkName)
	// Check if we require an api password
	if cfg.AuthenticateAPI {
		// if its not set, ask one now
		if cfg.APIPassword == "" {
			// Prompt user for API password.
			cfg.APIPassword, err = speakeasy.Ask("Enter API password: ")
			if err != nil {
				return err
			}
		}
		if cfg.APIPassword == "" {
			return errors.New("password cannot be blank")
		}
	} else {
		// If authenticateAPI is not set, explicitly set the password to the empty string.
		// This way the api server maintains consistency with the authenticateAPI var, even if apiPassword is set (possibly by mistake)
		cfg.APIPassword = ""
	}

	// Process the config variables
	// If there is an error or inconsistency in the config, we return, so there is no need to correct any values
	err = processConfig(&cfg)
	if err != nil {
		return err
	}

	// Print a startup message.
	fmt.Println("Loading...")
	loadStart := time.Now()

	// Create the server and start serving daemon routes immediately.
	fmt.Printf("(0/%d) Loading daemon of "+cfg.BlockchainInfo.Name+"...\n", len(cfg.Modules))
	srv, err := NewServer(cfg.APIaddr, cfg.RequiredUserAgent, cfg.APIPassword,
		networkConfig.Constants, cfg.BlockchainInfo)
	if err != nil {
		return err
	}

	servErrs := make(chan error)
	go func() {
		servErrs <- srv.Serve()
	}()

	// Initialize the Rivine modules
	i := 0
	var g modules.Gateway
	if strings.Contains(cfg.Modules, "g") {
		i++
		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(cfg.Modules))
		g, err = gateway.New(cfg.RPCaddr, !cfg.NoBootstrap,
			filepath.Join(cfg.RootPersistentDir, modules.GatewayDir),
			cfg.BlockchainInfo, networkConfig.Constants, networkConfig.BootstrapPeers)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing gateway...")
			err := g.Close()
			if err != nil {
				fmt.Println("Error during gateway shutdown:", err)
			}
		}()

	}
	var cs modules.ConsensusSet
	if strings.Contains(cfg.Modules, "c") {
		i++
		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(cfg.Modules))
		cs, err = consensus.New(g, !cfg.NoBootstrap,
			filepath.Join(cfg.RootPersistentDir, modules.ConsensusDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing consensus set...")
			err := cs.Close()
			if err != nil {
				fmt.Println("Error during consensus set shutdown:", err)
			}
		}()

	}
	var e modules.Explorer
	if strings.Contains(cfg.Modules, "e") {
		i++
		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(cfg.Modules))
		e, err = explorer.New(cs,
			filepath.Join(cfg.RootPersistentDir, modules.ExplorerDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing explorer...")
			err := e.Close()
			if err != nil {
				fmt.Println("Error during explorer shutdown:", err)
			}
		}()

	}
	var tpool modules.TransactionPool
	if strings.Contains(cfg.Modules, "t") {
		i++
		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(cfg.Modules))
		tpool, err = transactionpool.New(cs, g,
			filepath.Join(cfg.RootPersistentDir, modules.TransactionPoolDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing transaction pool...")
			err := tpool.Close()
			if err != nil {
				fmt.Println("Error during transaction pool shutdown:", err)
			}
		}()
	}
	var w modules.Wallet
	if strings.Contains(cfg.Modules, "w") {
		i++
		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(cfg.Modules))
		w, err = wallet.New(cs, tpool,
			filepath.Join(cfg.RootPersistentDir, modules.WalletDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing wallet...")
			err := w.Close()
			if err != nil {
				fmt.Println("Error during wallet shutdown:", err)
			}
		}()

	}
	var b modules.BlockCreator
	if strings.Contains(cfg.Modules, "b") {
		i++
		fmt.Printf("(%d/%d) Loading block creator...\n", i, len(cfg.Modules))
		b, err = blockcreator.New(cs, tpool, w,
			filepath.Join(cfg.RootPersistentDir, modules.BlockCreatorDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing block creator...")
			err := b.Close()
			if err != nil {
				fmt.Println("Error during block creator shutdown:", err)
			}
		}()
	}

	// Create the Rivine API
	a := api.New(
		cfg.RequiredUserAgent,
		cfg.APIPassword,
		cs,
		e,
		g,
		tpool,
		w,
	)

	// connect the API to the server
	srv.mux.Handle("/", a)

	// stop the server if a kill signal is caught
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill)
	go func() {
		<-sigChan
		fmt.Println("\rCaught stop signal, quitting...")
		srv.Close()
	}()

	// Print a 'startup complete' message.
	startupTime := time.Since(loadStart)
	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")

	err = <-servErrs
	if err != nil {
		build.Critical(err)
	}

	return nil
}
<commit_msg>fix typo and improve comment of  cfg prop<commit_after>package daemon

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/rivine/rivine/api"
	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/modules/blockcreator"
	"github.com/rivine/rivine/modules/consensus"
	"github.com/rivine/rivine/modules/explorer"
	"github.com/rivine/rivine/modules/gateway"
	"github.com/rivine/rivine/modules/transactionpool"
	"github.com/rivine/rivine/modules/wallet"
	"github.com/rivine/rivine/types"

	"github.com/bgentry/speakeasy"
)

// Config contains all configurable variables for rivined.
type Config struct {
	BlockchainInfo types.BlockchainInfo

	// the password required to use the http api,
	// if `AuthenticateAPI` is true, and the password is the empty string,
	// a password will be prompted when the daemon starts
	APIPassword string

	// the host:port for the HTTP API to listen on.
	// If `AllowAPIBind` is false, only localhost hosts are allowed
	APIaddr string
	// the host:port to listen for RPC calls
	RPCaddr string
	// indicates that the http API can listen on a non localhost address.
	//  If this is true, then the AuthenticateAPI parameter
	// must also be true
	AllowAPIBind bool

	// the modules to enable, this string must contain one letter
	// for each module (order does not matter).
	// All required modules must be specified,
	// if a required (parent) module is not present,
	// an error is returned
	Modules string
	// indicates that the daemon should not try to connect to
	// the bootstrap nodes
	NoBootstrap bool
	// the user agent required to connect to the http api.
	RequiredUserAgent string
	// indicates if the http api is password protected
	AuthenticateAPI bool

	// indicates if profile info should be collected while
	// the daemon is running
	Profile bool
	// name of the directory to store the profile info,
	// should this be collected
	ProfileDir string
	// the parent directory where the individual module
	// directories will be created
	RootPersistentDir string

	// Network defines the network config to use
	NetworkName string
	// optional network config constructor,
	// if you're implementing your own rivine-based blockchain,
	// you'll probably want to define this one,
	// as otherwise a pure rivine blockchain config will be created,
	// within this function you should also Register any specific
	// transaction, conditions and fulfillments
	CreateNetworkConfig func(name string) (NetworkConfig, error)
}

// DefaultConfig returns the default daemon configuration
func DefaultConfig() Config {
	return Config{
		BlockchainInfo: types.DefaultBlockchainInfo(),

		APIPassword: "",

		APIaddr:      "localhost:23110",
		RPCaddr:      ":23112",
		AllowAPIBind: false,

		Modules:           "cgtwb",
		NoBootstrap:       false,
		RequiredUserAgent: "Rivine-Agent",
		AuthenticateAPI:   false,

		Profile:           false,
		ProfileDir:        "profiles",
		RootPersistentDir: "",

		NetworkName: build.Release,
	}
}

func (cfg *Config) createConfiguredNetworkConfig() (NetworkConfig, error) {
	if cfg.NetworkName == "" {
		// default to build.Release as network name
		cfg.NetworkName = build.Release
	}
	if cfg.CreateNetworkConfig != nil {
		// use custom network config creator
		return cfg.CreateNetworkConfig(cfg.NetworkName)
	}

	// use default network config creator
	networkCfg := NetworkConfig{
		Constants: types.DefaultChainConstants(),
	}
	if cfg.NetworkName == "standard" {
		networkCfg.BootstrapPeers = []modules.NetAddress{
			"136.243.144.132:23112",
			"[2a01:4f8:171:1303::2]:23112",
			"bootstrap2.rivine.io:23112",
			"bootstrap3.rivine.io:23112",
		}
	}
	return networkCfg, nil
}

// verifyAPISecurity checks that the security values are consistent with a
// sane, secure system.
func verifyAPISecurity(cfg Config) error {
	// Make sure that only the loopback address is allowed unless the
	// --disable-api-security flag has been used.
	if !cfg.AllowAPIBind {
		addr := modules.NetAddress(cfg.APIaddr)
		if !addr.IsLoopback() {
			if addr.Host() == "" {
				return fmt.Errorf("a blank host will listen on all interfaces, did you mean localhost:%v?\nyou must pass --disable-api-security to bind daemon of %s to a non-localhost address", addr.Port(), cfg.BlockchainInfo.Name)
			}
			return fmt.Errorf("you must pass --disable-api-security to bind daemon of %s to a non-localhost address", cfg.BlockchainInfo.Name)
		}
		return nil
	}

	// If the --disable-api-security flag is used, enforce that
	// --authenticate-api must also be used.
	if cfg.AllowAPIBind && !cfg.AuthenticateAPI {
		return errors.New("cannot use --disable-api-security without setting an api password")
	}
	return nil
}

// processNetAddr adds a ':' to a bare integer, so that it is a proper port
// number.
func processNetAddr(addr string) string {
	_, err := strconv.Atoi(addr)
	if err == nil {
		return ":" + addr
	}
	return addr
}

// processModules makes the modules string lowercase to make checking if a
// module in the string easier, and returns an error if the string contains an
// invalid module character.
func processModules(modules string) (string, error) {
	modules = strings.ToLower(modules)
	validModules := "cgtwebd"
	invalidModules := modules
	for _, m := range validModules {
		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
	}
	if len(invalidModules) > 0 {
		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
	}
	return modules, nil
}

// processConfig checks the configuration values and performs cleanup on
// incorrect-but-allowed values.
func processConfig(config *Config) error {
	var err1 error
	config.APIaddr = processNetAddr(config.APIaddr)
	config.RPCaddr = processNetAddr(config.RPCaddr)
	config.Modules, err1 = processModules(config.Modules)
	err2 := verifyAPISecurity(*config)
	return build.JoinErrors([]error{err1, err2}, ", and ")
}

// StartDaemon uses the config parameters
// to initialize Rivine modules and start
func StartDaemon(cfg Config) (err error) {
	networkConfig, err := cfg.createConfiguredNetworkConfig()
	if err != nil {
		return
	}

	err = networkConfig.Constants.Validate()
	if err != nil {
		return err
	}
	// Silently append a subdirectory for storage with the name of the network so we don't create conflicts
	cfg.RootPersistentDir = filepath.Join(cfg.RootPersistentDir, cfg.NetworkName)
	// Check if we require an api password
	if cfg.AuthenticateAPI {
		// if its not set, ask one now
		if cfg.APIPassword == "" {
			// Prompt user for API password.
			cfg.APIPassword, err = speakeasy.Ask("Enter API password: ")
			if err != nil {
				return err
			}
		}
		if cfg.APIPassword == "" {
			return errors.New("password cannot be blank")
		}
	} else {
		// If authenticateAPI is not set, explicitly set the password to the empty string.
		// This way the api server maintains consistency with the authenticateAPI var, even if apiPassword is set (possibly by mistake)
		cfg.APIPassword = ""
	}

	// Process the config variables
	// If there is an error or inconsistency in the config, we return, so there is no need to correct any values
	err = processConfig(&cfg)
	if err != nil {
		return err
	}

	// Print a startup message.
	fmt.Println("Loading...")
	loadStart := time.Now()

	// Create the server and start serving daemon routes immediately.
	fmt.Printf("(0/%d) Loading daemon of "+cfg.BlockchainInfo.Name+"...\n", len(cfg.Modules))
	srv, err := NewServer(cfg.APIaddr, cfg.RequiredUserAgent, cfg.APIPassword,
		networkConfig.Constants, cfg.BlockchainInfo)
	if err != nil {
		return err
	}

	servErrs := make(chan error)
	go func() {
		servErrs <- srv.Serve()
	}()

	// Initialize the Rivine modules
	i := 0
	var g modules.Gateway
	if strings.Contains(cfg.Modules, "g") {
		i++
		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(cfg.Modules))
		g, err = gateway.New(cfg.RPCaddr, !cfg.NoBootstrap,
			filepath.Join(cfg.RootPersistentDir, modules.GatewayDir),
			cfg.BlockchainInfo, networkConfig.Constants, networkConfig.BootstrapPeers)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing gateway...")
			err := g.Close()
			if err != nil {
				fmt.Println("Error during gateway shutdown:", err)
			}
		}()

	}
	var cs modules.ConsensusSet
	if strings.Contains(cfg.Modules, "c") {
		i++
		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(cfg.Modules))
		cs, err = consensus.New(g, !cfg.NoBootstrap,
			filepath.Join(cfg.RootPersistentDir, modules.ConsensusDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing consensus set...")
			err := cs.Close()
			if err != nil {
				fmt.Println("Error during consensus set shutdown:", err)
			}
		}()

	}
	var e modules.Explorer
	if strings.Contains(cfg.Modules, "e") {
		i++
		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(cfg.Modules))
		e, err = explorer.New(cs,
			filepath.Join(cfg.RootPersistentDir, modules.ExplorerDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing explorer...")
			err := e.Close()
			if err != nil {
				fmt.Println("Error during explorer shutdown:", err)
			}
		}()

	}
	var tpool modules.TransactionPool
	if strings.Contains(cfg.Modules, "t") {
		i++
		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(cfg.Modules))
		tpool, err = transactionpool.New(cs, g,
			filepath.Join(cfg.RootPersistentDir, modules.TransactionPoolDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing transaction pool...")
			err := tpool.Close()
			if err != nil {
				fmt.Println("Error during transaction pool shutdown:", err)
			}
		}()
	}
	var w modules.Wallet
	if strings.Contains(cfg.Modules, "w") {
		i++
		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(cfg.Modules))
		w, err = wallet.New(cs, tpool,
			filepath.Join(cfg.RootPersistentDir, modules.WalletDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing wallet...")
			err := w.Close()
			if err != nil {
				fmt.Println("Error during wallet shutdown:", err)
			}
		}()

	}
	var b modules.BlockCreator
	if strings.Contains(cfg.Modules, "b") {
		i++
		fmt.Printf("(%d/%d) Loading block creator...\n", i, len(cfg.Modules))
		b, err = blockcreator.New(cs, tpool, w,
			filepath.Join(cfg.RootPersistentDir, modules.BlockCreatorDir),
			cfg.BlockchainInfo, networkConfig.Constants)
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing block creator...")
			err := b.Close()
			if err != nil {
				fmt.Println("Error during block creator shutdown:", err)
			}
		}()
	}

	// Create the Rivine API
	a := api.New(
		cfg.RequiredUserAgent,
		cfg.APIPassword,
		cs,
		e,
		g,
		tpool,
		w,
	)

	// connect the API to the server
	srv.mux.Handle("/", a)

	// stop the server if a kill signal is caught
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill)
	go func() {
		<-sigChan
		fmt.Println("\rCaught stop signal, quitting...")
		srv.Close()
	}()

	// Print a 'startup complete' message.
	startupTime := time.Since(loadStart)
	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")

	err = <-servErrs
	if err != nil {
		build.Critical(err)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package dns

import (
	"bytes"
	"encoding/json"
	"strings"

	skymsg "github.com/skynetservices/skydns/msg"
)

type TreeCache struct {
	ChildNodes map[string]*TreeCache
	Entries    map[string]interface{}
}

func NewTreeCache() *TreeCache {
	return &TreeCache{
		ChildNodes: make(map[string]*TreeCache),
		Entries:    make(map[string]interface{}),
	}
}

func (cache *TreeCache) Serialize() (string, error) {
	b, err := json.Marshal(cache)
	if err != nil {
		return "", err
	}

	var prettyJSON bytes.Buffer
	err = json.Indent(&prettyJSON, b, "", "\t")

	if err != nil {
		return "", err
	}
	return string(prettyJSON.Bytes()), nil
}

// setEntry creates the entire path if it doesn't already exist in the cache,
// then sets the given service record under the given key. The path this entry
// would have occupied in an etcd datastore is computed from the given fqdn and
// stored as the "Key" of the skydns service; this is only required because
// skydns expects the service record to contain a key in a specific format
// (presumably for legacy compatibility). Note that the fqnd string typically
// contains both the key and all elements in the path.
func (cache *TreeCache) setEntry(key string, val *skymsg.Service, fqdn string, path ...string) {
	// TODO: Consolidate setEntry and setSubCache into a single method with a
	// type switch.
	// TODO: Insted of passing the fqdn as an argument, we can reconstruct
	// it from the path, provided callers always pass the full path to the
	// object. This is currently *not* the case, since callers first create
	// a new, empty node, populate it, then parent it under the right path.
	// So we don't know the full key till the final parenting operation.
	node := cache.ensureChildNode(path...)

	// This key is used to construct the "target" for SRV record lookups.
	// For normal service/endpoint lookups, this will result in a key like:
	// /skydns/local/cluster/svc/svcNS/svcName/record-hash
	// but for headless services that govern pods requesting a specific
	// hostname (as used by petset), this will end up being:
	// /skydns/local/cluster/svc/svcNS/svcName/pod-hostname
	val.Key = skymsg.Path(fqdn)
	node.Entries[key] = val
}

func (cache *TreeCache) getSubCache(path ...string) *TreeCache {
	childCache := cache
	for _, subpath := range path {
		childCache = childCache.ChildNodes[subpath]
		if childCache == nil {
			return nil
		}
	}
	return childCache
}

// setSubCache inserts the given subtree under the given path:key. Usually the
// key is the name of a Kubernetes Service, and the path maps to the cluster
// subdomains matching the Service.
func (cache *TreeCache) setSubCache(key string, subCache *TreeCache, path ...string) {
	node := cache.ensureChildNode(path...)
	node.ChildNodes[key] = subCache
}

func (cache *TreeCache) getEntry(key string, path ...string) (interface{}, bool) {
	childNode := cache.getSubCache(path...)
	if childNode == nil {
		return nil, false
	}
	val, ok := childNode.Entries[key]
	return val, ok
}

func (cache *TreeCache) getValuesForPathWithWildcards(path ...string) []*skymsg.Service {
	retval := []*skymsg.Service{}
	nodesToExplore := []*TreeCache{cache}
	for idx, subpath := range path {
		nextNodesToExplore := []*TreeCache{}
		if idx == len(path)-1 {
			// if path ends on an entry, instead of a child node, add the entry
			for _, node := range nodesToExplore {
				if subpath == "*" {
					nextNodesToExplore = append(nextNodesToExplore, node)
				} else {
					if val, ok := node.Entries[subpath]; ok {
						retval = append(retval, val.(*skymsg.Service))
					} else {
						childNode := node.ChildNodes[subpath]
						if childNode != nil {
							nextNodesToExplore = append(nextNodesToExplore, childNode)
						}
					}
				}
			}
			nodesToExplore = nextNodesToExplore
			break
		}

		if subpath == "*" {
			for _, node := range nodesToExplore {
				for subkey, subnode := range node.ChildNodes {
					if !strings.HasPrefix(subkey, "_") {
						nextNodesToExplore = append(nextNodesToExplore, subnode)
					}
				}
			}
		} else {
			for _, node := range nodesToExplore {
				childNode := node.ChildNodes[subpath]
				if childNode != nil {
					nextNodesToExplore = append(nextNodesToExplore, childNode)
				}
			}
		}
		nodesToExplore = nextNodesToExplore
	}

	for _, node := range nodesToExplore {
		for _, val := range node.Entries {
			retval = append(retval, val.(*skymsg.Service))
		}
	}
	return retval
}

func (cache *TreeCache) deletePath(path ...string) bool {
	if len(path) == 0 {
		return false
	}
	if parentNode := cache.getSubCache(path[:len(path)-1]...); parentNode != nil {
		if _, ok := parentNode.ChildNodes[path[len(path)-1]]; ok {
			delete(parentNode.ChildNodes, path[len(path)-1])
			return true
		}
	}
	return false
}

func (cache *TreeCache) deleteEntry(key string, path ...string) bool {
	childNode := cache.getSubCache(path...)
	if childNode == nil {
		return false
	}
	if _, ok := childNode.Entries[key]; ok {
		delete(childNode.Entries, key)
		return true
	}
	return false
}

func (cache *TreeCache) appendValues(recursive bool, ref [][]interface{}) {
	for _, value := range cache.Entries {
		ref[0] = append(ref[0], value)
	}
	if recursive {
		for _, node := range cache.ChildNodes {
			node.appendValues(recursive, ref)
		}
	}
}

func (cache *TreeCache) ensureChildNode(path ...string) *TreeCache {
	childNode := cache
	for _, subpath := range path {
		newNode, ok := childNode.ChildNodes[subpath]
		if !ok {
			newNode = NewTreeCache()
			childNode.ChildNodes[subpath] = newNode
		}
		childNode = newNode
	}
	return childNode
}

// unused function. keeping it around in commented-fashion
// in the future, we might need some form of this function so that
// we can serialize to a file in a mounted empty dir..
//const (
//	dataFile = "data.dat"
//	crcFile  = "data.crc"
//)
//func (cache *TreeCache) Serialize(dir string) (string, error) {
//	cache.m.RLock()
//	defer cache.m.RUnlock()
//	b, err := json.Marshal(cache)
//	if err != nil {
//		return "", err
//	}
//
//	if err := ensureDir(dir, os.FileMode(0755)); err != nil {
//		return "", err
//	}
//	if err := ioutil.WriteFile(path.Join(dir, dataFile), b, 0644); err != nil {
//		return "", err
//	}
//	if err := ioutil.WriteFile(path.Join(dir, crcFile), getMD5(b), 0644); err != nil {
//		return "", err
//	}
//	return string(b), nil
//}

//func ensureDir(path string, perm os.FileMode) error {
//	s, err := os.Stat(path)
//	if err != nil || !s.IsDir() {
//		return os.Mkdir(path, perm)
//	}
//	return nil
//}

//func getMD5(b []byte) []byte {
//	h := md5.New()
//	h.Write(b)
//	return []byte(fmt.Sprintf("%x", h.Sum(nil)))
//}

// unused function. keeping it around in commented-fashion
// in the future, we might need some form of this function so that
// we can restart kube-dns, deserialize the tree and have a cache
// without having to wait for kube-dns to reach out to API server.
//func Deserialize(dir string) (*TreeCache, error) {
//	b, err := ioutil.ReadFile(path.Join(dir, dataFile))
//	if err != nil {
//		return nil, err
//	}
//
//	hash, err := ioutil.ReadFile(path.Join(dir, crcFile))
//	if err != nil {
//		return nil, err
//	}
//	if !reflect.DeepEqual(hash, getMD5(b)) {
//		return nil, fmt.Errorf("Checksum failed")
//	}
//
//	var cache TreeCache
//	err = json.Unmarshal(b, &cache)
//	if err != nil {
//		return nil, err
//	}
//	cache.m = &sync.RWMutex{}
//	return &cache, nil
//}
<commit_msg>Use MarshalIndent instead of marshal and indent<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package dns

import (
	"encoding/json"
	"strings"

	skymsg "github.com/skynetservices/skydns/msg"
)

type TreeCache struct {
	ChildNodes map[string]*TreeCache
	Entries    map[string]interface{}
}

func NewTreeCache() *TreeCache {
	return &TreeCache{
		ChildNodes: make(map[string]*TreeCache),
		Entries:    make(map[string]interface{}),
	}
}

func (cache *TreeCache) Serialize() (string, error) {
	prettyJSON, err := json.MarshalIndent(cache, "", "\t")
	if err != nil {
		return "", err
	}
	return string(prettyJSON), nil
}

// setEntry creates the entire path if it doesn't already exist in the cache,
// then sets the given service record under the given key. The path this entry
// would have occupied in an etcd datastore is computed from the given fqdn and
// stored as the "Key" of the skydns service; this is only required because
// skydns expects the service record to contain a key in a specific format
// (presumably for legacy compatibility). Note that the fqnd string typically
// contains both the key and all elements in the path.
func (cache *TreeCache) setEntry(key string, val *skymsg.Service, fqdn string, path ...string) {
	// TODO: Consolidate setEntry and setSubCache into a single method with a
	// type switch.
	// TODO: Insted of passing the fqdn as an argument, we can reconstruct
	// it from the path, provided callers always pass the full path to the
	// object. This is currently *not* the case, since callers first create
	// a new, empty node, populate it, then parent it under the right path.
	// So we don't know the full key till the final parenting operation.
	node := cache.ensureChildNode(path...)

	// This key is used to construct the "target" for SRV record lookups.
	// For normal service/endpoint lookups, this will result in a key like:
	// /skydns/local/cluster/svc/svcNS/svcName/record-hash
	// but for headless services that govern pods requesting a specific
	// hostname (as used by petset), this will end up being:
	// /skydns/local/cluster/svc/svcNS/svcName/pod-hostname
	val.Key = skymsg.Path(fqdn)
	node.Entries[key] = val
}

func (cache *TreeCache) getSubCache(path ...string) *TreeCache {
	childCache := cache
	for _, subpath := range path {
		childCache = childCache.ChildNodes[subpath]
		if childCache == nil {
			return nil
		}
	}
	return childCache
}

// setSubCache inserts the given subtree under the given path:key. Usually the
// key is the name of a Kubernetes Service, and the path maps to the cluster
// subdomains matching the Service.
func (cache *TreeCache) setSubCache(key string, subCache *TreeCache, path ...string) {
	node := cache.ensureChildNode(path...)
	node.ChildNodes[key] = subCache
}

func (cache *TreeCache) getEntry(key string, path ...string) (interface{}, bool) {
	childNode := cache.getSubCache(path...)
	if childNode == nil {
		return nil, false
	}
	val, ok := childNode.Entries[key]
	return val, ok
}

func (cache *TreeCache) getValuesForPathWithWildcards(path ...string) []*skymsg.Service {
	retval := []*skymsg.Service{}
	nodesToExplore := []*TreeCache{cache}
	for idx, subpath := range path {
		nextNodesToExplore := []*TreeCache{}
		if idx == len(path)-1 {
			// if path ends on an entry, instead of a child node, add the entry
			for _, node := range nodesToExplore {
				if subpath == "*" {
					nextNodesToExplore = append(nextNodesToExplore, node)
				} else {
					if val, ok := node.Entries[subpath]; ok {
						retval = append(retval, val.(*skymsg.Service))
					} else {
						childNode := node.ChildNodes[subpath]
						if childNode != nil {
							nextNodesToExplore = append(nextNodesToExplore, childNode)
						}
					}
				}
			}
			nodesToExplore = nextNodesToExplore
			break
		}

		if subpath == "*" {
			for _, node := range nodesToExplore {
				for subkey, subnode := range node.ChildNodes {
					if !strings.HasPrefix(subkey, "_") {
						nextNodesToExplore = append(nextNodesToExplore, subnode)
					}
				}
			}
		} else {
			for _, node := range nodesToExplore {
				childNode := node.ChildNodes[subpath]
				if childNode != nil {
					nextNodesToExplore = append(nextNodesToExplore, childNode)
				}
			}
		}
		nodesToExplore = nextNodesToExplore
	}

	for _, node := range nodesToExplore {
		for _, val := range node.Entries {
			retval = append(retval, val.(*skymsg.Service))
		}
	}
	return retval
}

func (cache *TreeCache) deletePath(path ...string) bool {
	if len(path) == 0 {
		return false
	}
	if parentNode := cache.getSubCache(path[:len(path)-1]...); parentNode != nil {
		if _, ok := parentNode.ChildNodes[path[len(path)-1]]; ok {
			delete(parentNode.ChildNodes, path[len(path)-1])
			return true
		}
	}
	return false
}

func (cache *TreeCache) deleteEntry(key string, path ...string) bool {
	childNode := cache.getSubCache(path...)
	if childNode == nil {
		return false
	}
	if _, ok := childNode.Entries[key]; ok {
		delete(childNode.Entries, key)
		return true
	}
	return false
}

func (cache *TreeCache) appendValues(recursive bool, ref [][]interface{}) {
	for _, value := range cache.Entries {
		ref[0] = append(ref[0], value)
	}
	if recursive {
		for _, node := range cache.ChildNodes {
			node.appendValues(recursive, ref)
		}
	}
}

func (cache *TreeCache) ensureChildNode(path ...string) *TreeCache {
	childNode := cache
	for _, subpath := range path {
		newNode, ok := childNode.ChildNodes[subpath]
		if !ok {
			newNode = NewTreeCache()
			childNode.ChildNodes[subpath] = newNode
		}
		childNode = newNode
	}
	return childNode
}

// unused function. keeping it around in commented-fashion
// in the future, we might need some form of this function so that
// we can serialize to a file in a mounted empty dir..
//const (
//	dataFile = "data.dat"
//	crcFile  = "data.crc"
//)
//func (cache *TreeCache) Serialize(dir string) (string, error) {
//	cache.m.RLock()
//	defer cache.m.RUnlock()
//	b, err := json.Marshal(cache)
//	if err != nil {
//		return "", err
//	}
//
//	if err := ensureDir(dir, os.FileMode(0755)); err != nil {
//		return "", err
//	}
//	if err := ioutil.WriteFile(path.Join(dir, dataFile), b, 0644); err != nil {
//		return "", err
//	}
//	if err := ioutil.WriteFile(path.Join(dir, crcFile), getMD5(b), 0644); err != nil {
//		return "", err
//	}
//	return string(b), nil
//}

//func ensureDir(path string, perm os.FileMode) error {
//	s, err := os.Stat(path)
//	if err != nil || !s.IsDir() {
//		return os.Mkdir(path, perm)
//	}
//	return nil
//}

//func getMD5(b []byte) []byte {
//	h := md5.New()
//	h.Write(b)
//	return []byte(fmt.Sprintf("%x", h.Sum(nil)))
//}

// unused function. keeping it around in commented-fashion
// in the future, we might need some form of this function so that
// we can restart kube-dns, deserialize the tree and have a cache
// without having to wait for kube-dns to reach out to API server.
//func Deserialize(dir string) (*TreeCache, error) {
//	b, err := ioutil.ReadFile(path.Join(dir, dataFile))
//	if err != nil {
//		return nil, err
//	}
//
//	hash, err := ioutil.ReadFile(path.Join(dir, crcFile))
//	if err != nil {
//		return nil, err
//	}
//	if !reflect.DeepEqual(hash, getMD5(b)) {
//		return nil, fmt.Errorf("Checksum failed")
//	}
//
//	var cache TreeCache
//	err = json.Unmarshal(b, &cache)
//	if err != nil {
//		return nil, err
//	}
//	cache.m = &sync.RWMutex{}
//	return &cache, nil
//}
<|endoftext|>


<commit_before>// Package errors implements functions to manipulate errors.
package errors

import (
	"bytes"
	"fmt"
	"runtime"
)

// Application error codes.
const (
	INVALID           = "invalid"            // validation failed
	UNAUTHORIZED      = "unauthorized"       // access denied
	FORBIDDEN         = "forbidden"          // forbidden
	NOTFOUND          = "not_found"          // entity does not exist
	INTERNAL          = "internal"           // internal error
	TEMPORARYDISABLED = "temporary_disabled" // temporary disabled
	TIMEOUT           = "timeout"            // timeout
)

// ErrorCode returns the code of the root error, if available. Otherwise returns INTERNAL.
func ErrorCode(err error) string {
	if err == nil {
		return ""
	} else if e, ok := err.(*appError); ok && e.Code != "" {
		return e.Code
	} else if ok && e.err != nil {
		return ErrorCode(e.err)
	}
	return INTERNAL
}

// ErrorMessage returns the human-readable message of the error, if available.
// Otherwise returns a generic error message.
func ErrorMessage(err error) string {
	if err == nil {
		return ""
	} else if e, ok := err.(*appError); ok && e.Message != "" {
		return e.Message
	} else if ok && e.err != nil {
		return ErrorMessage(e.err)
	}
	return "An internal error has occurred. Please contact technical support."
}

// New returns an app error that formats as the given text.
func New(code string, message string) error {
	return &appError{
		Code:    code,
		Message: message,
		pc:      getPCs(),
	}
}

// Newf returns an app error that formats as the given text.
func Newf(code string, message string, args ...interface{}) error {
	return &appError{
		Code:    code,
		Message: fmt.Sprintf(message, args...),
		pc:      getPCs(),
	}
}

// Wrap adds error to the stack
func Wrap(err error, code string, message string) error {
	return &appError{
		Code:    code,
		Message: message,
		err:     err,
		pc:      getPCs(),
	}
}

// Wrapf adds error to the stack
func Wrapf(err error, code string, message string, args ...interface{}) error {
	return &appError{
		Code:    code,
		Message: fmt.Sprintf(message, args...),
		err:     err,
		pc:      getPCs(),
	}
}

type appError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	err     error
	pc      []uintptr
}

// Error returns the string representation of the error message.
// Calls StackTrace internally.
func (e *appError) Error() string {
	return e.stackTrace(true)
}

// StackTrace returns the string representation of the error stack trace,
// includeFrames appends caller pcs frames to each error message if possible.
func (e *appError) stackTrace(includeFrames bool) string {
	var buf bytes.Buffer

	// Print the current error in our stack, if any.
	if e.Code != "" {
		fmt.Fprintf(&buf, "<%s> ", e.Code)
	}

	fmt.Fprintf(&buf, "%s\n", e.Message)

	if includeFrames && len(e.pc) > 0 {
		frames := runtime.CallersFrames(e.pc)
		// Loop to get frames.
		// A fixed number of pcs can expand to an indefinite number of Frames.
		for {
			frame, more := frames.Next()
			fmt.Fprintf(&buf, "\t%s:%d %s\n", frame.File, frame.Line, frame.Function)
			if !more {
				break
			}
		}
	}

	// If wrapping an error, print its Error() message.
	if e.err != nil {
		buf.WriteString(e.err.Error())
	}

	return buf.String()
}

func getPCs() []uintptr {
	// Ask runtime.Callers for up to 4 pcs, including:
	// - runtime.Callers itself,
	// - package call stack itself
	pc := make([]uintptr, 4)
	n := runtime.Callers(0, pc)

	if n < 4 {
		return pc[:]
	}

	// pass only valid pcs to runtime.CallersFrames
	// exclude irrelevant pcs
	return pc[3:n]
}
<commit_msg>Remove function from stack trace<commit_after>// Package errors implements functions to manipulate errors.
package errors

import (
	"bytes"
	"fmt"
	"runtime"
)

// Application error codes.
const (
	INVALID           = "invalid"            // validation failed
	UNAUTHORIZED      = "unauthorized"       // access denied
	FORBIDDEN         = "forbidden"          // forbidden
	NOTFOUND          = "not_found"          // entity does not exist
	INTERNAL          = "internal"           // internal error
	TEMPORARYDISABLED = "temporary_disabled" // temporary disabled
	TIMEOUT           = "timeout"            // timeout
)

// ErrorCode returns the code of the root error, if available. Otherwise returns INTERNAL.
func ErrorCode(err error) string {
	if err == nil {
		return ""
	} else if e, ok := err.(*appError); ok && e.Code != "" {
		return e.Code
	} else if ok && e.err != nil {
		return ErrorCode(e.err)
	}
	return INTERNAL
}

// ErrorMessage returns the human-readable message of the error, if available.
// Otherwise returns a generic error message.
func ErrorMessage(err error) string {
	if err == nil {
		return ""
	} else if e, ok := err.(*appError); ok && e.Message != "" {
		return e.Message
	} else if ok && e.err != nil {
		return ErrorMessage(e.err)
	}
	return "An internal error has occurred. Please contact technical support."
}

// New returns an app error that formats as the given text.
func New(code string, message string) error {
	return &appError{
		Code:    code,
		Message: message,
		pc:      getPCs(),
	}
}

// Newf returns an app error that formats as the given text.
func Newf(code string, message string, args ...interface{}) error {
	return &appError{
		Code:    code,
		Message: fmt.Sprintf(message, args...),
		pc:      getPCs(),
	}
}

// Wrap adds error to the stack
func Wrap(err error, code string, message string) error {
	return &appError{
		Code:    code,
		Message: message,
		err:     err,
		pc:      getPCs(),
	}
}

// Wrapf adds error to the stack
func Wrapf(err error, code string, message string, args ...interface{}) error {
	return &appError{
		Code:    code,
		Message: fmt.Sprintf(message, args...),
		err:     err,
		pc:      getPCs(),
	}
}

type appError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	err     error
	pc      []uintptr
}

// Error returns the string representation of the error message.
// Calls StackTrace internally.
func (e *appError) Error() string {
	return e.stackTrace(true)
}

// StackTrace returns the string representation of the error stack trace,
// includeFrames appends caller pcs frames to each error message if possible.
func (e *appError) stackTrace(includeFrames bool) string {
	var buf bytes.Buffer

	// Print the current error in our stack, if any.
	if e.Code != "" {
		fmt.Fprintf(&buf, "<%s> ", e.Code)
	}

	fmt.Fprintf(&buf, "%s\n", e.Message)

	if includeFrames && len(e.pc) > 0 {
		frames := runtime.CallersFrames(e.pc)
		// Loop to get frames.
		// A fixed number of pcs can expand to an indefinite number of Frames.
		for {
			frame, more := frames.Next()
			fmt.Fprintf(&buf, "\t%s:%d\n", frame.File, frame.Line)

			if !more {
				break
			}
		}
	}

	// If wrapping an error, print its Error() message.
	if e.err != nil {
		buf.WriteString(e.err.Error())
	}

	return buf.String()
}

func getPCs() []uintptr {
	// Ask runtime.Callers for up to 4 pcs, including:
	// - runtime.Callers itself,
	// - package call stack itself
	pc := make([]uintptr, 4)
	n := runtime.Callers(0, pc)

	if n < 4 {
		return pc[:]
	}

	// pass only valid pcs to runtime.CallersFrames
	// exclude irrelevant pcs
	return pc[3:n]
}
<|endoftext|>


<commit_before>/*
 * Playlist parsing tests.
**/

package m3u8

import (
	"bufio"
	"fmt"
	"os"
	"testing"
)

func TestDecodeMasterPlaylist(t *testing.T) {
	f, err := os.Open("sample-playlists/master.m3u8")
	if err != nil {
		fmt.Println(err)
	}
	p := NewMasterPlaylist()
	err = p.Decode(bufio.NewReader(f), false)
	if err != nil {
		fmt.Println(err)
	}
	// check parsed values
	if p.ver != 3 {
		panic(fmt.Sprintf("Version of parsed playlist = %d (must = 3)", p.ver))
	}
	// TODO check other values
}

func TestDecodeMediaPlaylist(t *testing.T) {
	f, err := os.Open("sample-playlists/wowza-vod-chunklist.m3u8")
	if err != nil {
		panic(err)
	}
	p, err := NewMediaPlaylist(5, 798)
	if err != nil {
		panic(fmt.Sprintf("Create media playlist failed: %s", err))
	}
	err = p.Decode(bufio.NewReader(f), true)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Playlist object: %+v\n", p)
	// check parsed values
	if p.ver != 3 {
		panic(fmt.Sprintf("Version of parsed playlist = %d (must = 3)", p.ver))
	}
	if p.TargetDuration != 12 {
		panic(fmt.Sprintf("TargetDuration of parsed playlist = %f (must = 12.0)", p.TargetDuration))
	}
	if !p.Closed {
		panic("This is a closed (VOD) playlist but Close field = false")
	}
	// TODO check other values…

	//fmt.Println(p.Encode(true).String())
}
<commit_msg>Removed debug output.<commit_after>/*
 * Playlist parsing tests.
**/

package m3u8

import (
	"bufio"
	"fmt"
	"os"
	"testing"
)

func TestDecodeMasterPlaylist(t *testing.T) {
	f, err := os.Open("sample-playlists/master.m3u8")
	if err != nil {
		fmt.Println(err)
	}
	p := NewMasterPlaylist()
	err = p.Decode(bufio.NewReader(f), false)
	if err != nil {
		fmt.Println(err)
	}
	// check parsed values
	if p.ver != 3 {
		panic(fmt.Sprintf("Version of parsed playlist = %d (must = 3)", p.ver))
	}
	// TODO check other values
}

func TestDecodeMediaPlaylist(t *testing.T) {
	f, err := os.Open("sample-playlists/wowza-vod-chunklist.m3u8")
	if err != nil {
		panic(err)
	}
	p, err := NewMediaPlaylist(5, 798)
	if err != nil {
		panic(fmt.Sprintf("Create media playlist failed: %s", err))
	}
	err = p.Decode(bufio.NewReader(f), true)
	if err != nil {
		panic(err)
	}
	//fmt.Printf("Playlist object: %+v\n", p)
	// check parsed values
	if p.ver != 3 {
		panic(fmt.Sprintf("Version of parsed playlist = %d (must = 3)", p.ver))
	}
	if p.TargetDuration != 12 {
		panic(fmt.Sprintf("TargetDuration of parsed playlist = %f (must = 12.0)", p.TargetDuration))
	}
	if !p.Closed {
		panic("This is a closed (VOD) playlist but Close field = false")
	}
	// TODO check other values…

	//fmt.Println(p.Encode(true).String())
}
<|endoftext|>