<commit_before>/*
scryptauth is a GO library for secure password handling

It uses hmac(scrypt(user_password, salt), server_key) to protect against
both dictionarry attacks and DB leaks.

scryptauth additionally provides encode/decode routines using base64 to create strings
for storing into a DB.

Copyright Michael Gebetsroither 2012 (michael \x40 mgeb \x2e org)
*/
package scryptauth

import (
	"code.google.com/p/go.crypto/scrypt"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"errors"
)

type ScryptAuth struct {
	HmacKey []byte  // HMAC key used to secure scrypt hash
	PwCost  uint    // PwCost parameter used to calculate N parameter of scrypt (1<<PwCost == N)

	// scrypt parameter
	R int
	P int
}

const (
	// Key length and salt length are 32 bytes (256 bits)
	KEYLENGTH = 32
)

// Initialise ScryptAuth struct
func New(pw_cost uint, hmac_key []byte) (*ScryptAuth, error) {
	if pw_cost > 32 {
		return nil, errors.New("scryptauth new() - invalid pw_cost specified")
	}
	if len(hmac_key) != KEYLENGTH {
		return nil, errors.New("scryptauth new() - unsupported hmac_key length")
	}
	return &ScryptAuth{HmacKey: hmac_key, PwCost: pw_cost, R: 8, P: 1}, nil
}

// Create hash_ref suitable from Check()
func (s ScryptAuth) Hash(pw_cost uint, user_password, salt []byte) (hash_ref []byte, err error) {
	scrypt_hash, err := scrypt.Key(user_password, salt, 1<<pw_cost, s.R, s.P, KEYLENGTH)
	if err != nil {
		return
	}
	hmac := hmac.New(sha256.New, s.HmacKey)
	if _, err = hmac.Write(scrypt_hash); err != nil {
		return
	}
	hash_ref = hmac.Sum(nil)
	return
}

// Check / Verify user_password against hash_ref/salt
func (s ScryptAuth) Check(pw_cost uint, hash_ref, user_password, salt []byte) (chk bool, err error) {
	result_hash, err := s.Hash(pw_cost, user_password, salt)
	if err != nil {
		return false, err
	}
	if subtle.ConstantTimeCompare(result_hash, hash_ref) != 1 {
		return false, errors.New("Error: Hash verification failed")
	}
	return true, nil
}

// Generate hash_ref and create new salt from crypto.rand
func (s ScryptAuth) Gen(user_password []byte) (hash, salt []byte, err error) {
	salt = make([]byte, KEYLENGTH)
	salt_length, err := rand.Read(salt)
	if salt_length != KEYLENGTH {
		return nil, nil, errors.New("Insufficient random bytes for salt")
	}
	if err != nil {
		return nil, nil, err
	}

	hash, err = s.Hash(s.PwCost, user_password, salt)
	if err != nil {
		return nil, nil, err
	}
	return
}
<commit_msg>Provide proper synopsis for godoc<commit_after>/* scryptauth is a GO library for secure password handling

It uses hmac(scrypt(user_password, salt), server_key) to protect against
both dictionarry attacks and DB leaks.

scryptauth additionally provides encode/decode routines using base64 to create strings
for storing into a DB.

Copyright Michael Gebetsroither 2012 (michael \x40 mgeb \x2e org)
*/
package scryptauth

import (
	"code.google.com/p/go.crypto/scrypt"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"errors"
)

type ScryptAuth struct {
	HmacKey []byte  // HMAC key used to secure scrypt hash
	PwCost  uint    // PwCost parameter used to calculate N parameter of scrypt (1<<PwCost == N)

	// scrypt parameter
	R int
	P int
}

const (
	// Key length and salt length are 32 bytes (256 bits)
	KEYLENGTH = 32
)

// Initialise ScryptAuth struct
func New(pw_cost uint, hmac_key []byte) (*ScryptAuth, error) {
	if pw_cost > 32 {
		return nil, errors.New("scryptauth new() - invalid pw_cost specified")
	}
	if len(hmac_key) != KEYLENGTH {
		return nil, errors.New("scryptauth new() - unsupported hmac_key length")
	}
	return &ScryptAuth{HmacKey: hmac_key, PwCost: pw_cost, R: 8, P: 1}, nil
}

// Create hash_ref suitable from Check()
func (s ScryptAuth) Hash(pw_cost uint, user_password, salt []byte) (hash_ref []byte, err error) {
	scrypt_hash, err := scrypt.Key(user_password, salt, 1<<pw_cost, s.R, s.P, KEYLENGTH)
	if err != nil {
		return
	}
	hmac := hmac.New(sha256.New, s.HmacKey)
	if _, err = hmac.Write(scrypt_hash); err != nil {
		return
	}
	hash_ref = hmac.Sum(nil)
	return
}

// Check / Verify user_password against hash_ref/salt
func (s ScryptAuth) Check(pw_cost uint, hash_ref, user_password, salt []byte) (chk bool, err error) {
	result_hash, err := s.Hash(pw_cost, user_password, salt)
	if err != nil {
		return false, err
	}
	if subtle.ConstantTimeCompare(result_hash, hash_ref) != 1 {
		return false, errors.New("Error: Hash verification failed")
	}
	return true, nil
}

// Generate hash_ref and create new salt from crypto.rand
func (s ScryptAuth) Gen(user_password []byte) (hash, salt []byte, err error) {
	salt = make([]byte, KEYLENGTH)
	salt_length, err := rand.Read(salt)
	if salt_length != KEYLENGTH {
		return nil, nil, errors.New("Insufficient random bytes for salt")
	}
	if err != nil {
		return nil, nil, err
	}

	hash, err = s.Hash(s.PwCost, user_password, salt)
	if err != nil {
		return nil, nil, err
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type MangoMiddlewareInterface interface {
	Call(string) string
}

type MangoMiddleware struct {
	upstream MangoMiddlewareInterface
}

func (this *MangoMiddleware) Call(in string) string {
	return this.upstream.Call(in)
}

type Logger struct {
	MangoMiddleware
}

func (this *Logger) Call(in string) string {
	return fmt.Sprintf("Logger\n%s\nLogger", this.upstream.Call(in))
}

type Sessions struct {
	MangoMiddleware
}

func (this *Sessions) Call(in string) string {
	return fmt.Sprintf("Sessions\n%s\nSessions", this.upstream.Call(in))
}

type App struct{}

func (this *App) Call(in string) string {
	return "App"
}

func main() {
	logger := new(Logger)
	logger.upstream = new(App)
	sessions := new(Sessions)
	sessions.upstream = logger
	fmt.Printf(sessions.Call("1"))
}
<commit_msg>trying a functional version of the middleware<commit_after>package main

import (
	"fmt"
)

type MangoApp func(string) string
type MangoMiddleware func(string, []MangoMiddleware) string

func Logger(in string, upstream []MangoMiddleware) string {
	return fmt.Sprintf("Logger\n%s\nLogger", upstream[0](in, upstream[1:]))
}

func Sessions(in string, upstream []MangoMiddleware) string {
	return fmt.Sprintf("Sessions\n%s\nSessions", upstream[0](in, upstream[1:]))
}

func App(in string, upstream []MangoMiddleware) string {
	return "App"
}

func main() {
	fmt.Printf(Sessions("1", []MangoMiddleware{Logger, App}))
}
<|endoftext|>


<commit_before>//go:build integration
// +build integration

/*
Copyright 2020 The Kubernetes Authors All rights reserved.

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 integration

import (
	"context"
	"encoding/json"
	"os/exec"
	"strings"
	"testing"

	"k8s.io/minikube/cmd/minikube/cmd"
	"k8s.io/minikube/pkg/minikube/constants"
)

// TestPause tests minikube pause functionality
func TestPause(t *testing.T) {
	MaybeParallel(t)

	type validateFunc func(context.Context, *testing.T, string)
	profile := UniqueProfileName("pause")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(15))
	defer Cleanup(t, profile, cancel)

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
			{"Start", validateFreshStart},
			{"SecondStartNoReconfiguration", validateStartNoReconfigure},
			{"Pause", validatePause},
			{"VerifyStatus", validateStatus},
			{"Unpause", validateUnpause},
			{"PauseAgain", validatePause},
			{"DeletePaused", validateDelete},
			{"VerifyDeletedResources", validateVerifyDeleted},
		}
		for _, tc := range tests {
			tc := tc

			if ctx.Err() == context.DeadlineExceeded {
				t.Fatalf("Unable to run more tests (deadline exceeded)")
			}
			if t.Failed() {
				t.Fatalf("Previous test failed, not running dependent tests")
			}

			t.Run(tc.name, func(t *testing.T) {
				tc.validator(ctx, t, profile)
				if t.Failed() && *postMortemLogs {
					PostMortemLogs(t, profile)
				}
			})
		}
	})
}

// validateFreshStart just starts a new minikube cluster
func validateFreshStart(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := append([]string{"start", "-p", profile, "--memory=2048", "--install-addons=false", "--wait=all"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateStartNoReconfigure validates that starting a running cluster does not invoke reconfiguration
func validateStartNoReconfigure(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"start", "-p", profile, "--alsologtostderr", "-v=1"}
	args = append(args, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to second start a running minikube with args: %q : %v", rr.Command(), err)
	}

	if !NoneDriver() {
		softLog := "The running cluster does not require reconfiguration"
		if !strings.Contains(rr.Output(), softLog) {
			t.Errorf("expected the second start log output to include %q but got: %s", softLog, rr.Output())
		}
	}
}

// validatePause runs minikube pause
func validatePause(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"pause", "-p", profile, "--alsologtostderr", "-v=5"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to pause minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateUnpause runs minikube unpause
func validateUnpause(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"unpause", "-p", profile, "--alsologtostderr", "-v=5"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to unpause minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateDelete deletes the unpaused cluster
func validateDelete(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"delete", "-p", profile, "--alsologtostderr", "-v=5"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to delete minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateVerifyDeleted makes sure no left over left after deleting a profile such as containers or volumes
func validateVerifyDeleted(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
	if err != nil {
		t.Errorf("failed to list profiles with json format after it was deleted. args %q: %v", rr.Command(), err)
	}

	var jsonObject map[string][]map[string]interface{}
	if err := json.Unmarshal(rr.Stdout.Bytes(), &jsonObject); err != nil {
		t.Errorf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
	}
	validProfiles := jsonObject["valid"]
	profileExists := false
	for _, profileObject := range validProfiles {
		if profileObject["Name"] == profile {
			profileExists = true
			break
		}
	}
	if profileExists {
		t.Errorf("expected the deleted profile %q not to show up in profile list but it does! output: %s . args: %q", profile, rr.Stdout.String(), rr.Command())
	}

	if KicDriver() {
		bin := "docker"
		if PodmanDriver() {
			bin = "podman"
		}
		rr, err := Run(t, exec.CommandContext(ctx, bin, "ps", "-a"))
		if err == nil && strings.Contains(rr.Output(), profile) {
			t.Errorf("expected container %q not to exist in output of %s but it does output: %s.", profile, rr.Command(), rr.Output())
		}

		rr, err = Run(t, exec.CommandContext(ctx, bin, "volume", "inspect", profile))
		if err == nil {
			t.Errorf("expected to see error and volume %q to not exist after deletion but got no error and this output: %s", rr.Command(), rr.Output())
		}

		rr, err = Run(t, exec.CommandContext(ctx, bin, "network", "ls"))
		if err != nil {
			t.Errorf("failed to get list of networks: %v", err)
		}
		if strings.Contains(rr.Output(), profile) {
			t.Errorf("expected network %q to not exist after deletion but contained: %s", profile, rr.Output())
		}
	}

}

// validateStatus makes sure paused clusters show up in minikube status correctly
func validateStatus(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	statusOutput := runStatusCmd(ctx, t, profile, false)
	var cs cmd.ClusterState
	if err := json.Unmarshal(statusOutput, &cs); err != nil {
		t.Fatalf("unmarshalling: %v", err)
	}
	// verify the status looks as we expect
	if cs.StatusCode != cmd.Paused {
		t.Fatalf("incorrect status code: %v", cs.StatusCode)
	}
	if cs.StatusName != "Paused" {
		t.Fatalf("incorrect status name: %v", cs.StatusName)
	}
}
<commit_msg>remove unused import<commit_after>//go:build integration
// +build integration

/*
Copyright 2020 The Kubernetes Authors All rights reserved.

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 integration

import (
	"context"
	"encoding/json"
	"os/exec"
	"strings"
	"testing"

	"k8s.io/minikube/cmd/minikube/cmd"
)

// TestPause tests minikube pause functionality
func TestPause(t *testing.T) {
	MaybeParallel(t)

	type validateFunc func(context.Context, *testing.T, string)
	profile := UniqueProfileName("pause")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(15))
	defer Cleanup(t, profile, cancel)

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
			{"Start", validateFreshStart},
			{"SecondStartNoReconfiguration", validateStartNoReconfigure},
			{"Pause", validatePause},
			{"VerifyStatus", validateStatus},
			{"Unpause", validateUnpause},
			{"PauseAgain", validatePause},
			{"DeletePaused", validateDelete},
			{"VerifyDeletedResources", validateVerifyDeleted},
		}
		for _, tc := range tests {
			tc := tc

			if ctx.Err() == context.DeadlineExceeded {
				t.Fatalf("Unable to run more tests (deadline exceeded)")
			}
			if t.Failed() {
				t.Fatalf("Previous test failed, not running dependent tests")
			}

			t.Run(tc.name, func(t *testing.T) {
				tc.validator(ctx, t, profile)
				if t.Failed() && *postMortemLogs {
					PostMortemLogs(t, profile)
				}
			})
		}
	})
}

// validateFreshStart just starts a new minikube cluster
func validateFreshStart(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := append([]string{"start", "-p", profile, "--memory=2048", "--install-addons=false", "--wait=all"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateStartNoReconfigure validates that starting a running cluster does not invoke reconfiguration
func validateStartNoReconfigure(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"start", "-p", profile, "--alsologtostderr", "-v=1"}
	args = append(args, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to second start a running minikube with args: %q : %v", rr.Command(), err)
	}

	if !NoneDriver() {
		softLog := "The running cluster does not require reconfiguration"
		if !strings.Contains(rr.Output(), softLog) {
			t.Errorf("expected the second start log output to include %q but got: %s", softLog, rr.Output())
		}
	}
}

// validatePause runs minikube pause
func validatePause(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"pause", "-p", profile, "--alsologtostderr", "-v=5"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to pause minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateUnpause runs minikube unpause
func validateUnpause(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"unpause", "-p", profile, "--alsologtostderr", "-v=5"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to unpause minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateDelete deletes the unpaused cluster
func validateDelete(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"delete", "-p", profile, "--alsologtostderr", "-v=5"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to delete minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateVerifyDeleted makes sure no left over left after deleting a profile such as containers or volumes
func validateVerifyDeleted(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
	if err != nil {
		t.Errorf("failed to list profiles with json format after it was deleted. args %q: %v", rr.Command(), err)
	}

	var jsonObject map[string][]map[string]interface{}
	if err := json.Unmarshal(rr.Stdout.Bytes(), &jsonObject); err != nil {
		t.Errorf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
	}
	validProfiles := jsonObject["valid"]
	profileExists := false
	for _, profileObject := range validProfiles {
		if profileObject["Name"] == profile {
			profileExists = true
			break
		}
	}
	if profileExists {
		t.Errorf("expected the deleted profile %q not to show up in profile list but it does! output: %s . args: %q", profile, rr.Stdout.String(), rr.Command())
	}

	if KicDriver() {
		bin := "docker"
		if PodmanDriver() {
			bin = "podman"
		}
		rr, err := Run(t, exec.CommandContext(ctx, bin, "ps", "-a"))
		if err == nil && strings.Contains(rr.Output(), profile) {
			t.Errorf("expected container %q not to exist in output of %s but it does output: %s.", profile, rr.Command(), rr.Output())
		}

		rr, err = Run(t, exec.CommandContext(ctx, bin, "volume", "inspect", profile))
		if err == nil {
			t.Errorf("expected to see error and volume %q to not exist after deletion but got no error and this output: %s", rr.Command(), rr.Output())
		}

		rr, err = Run(t, exec.CommandContext(ctx, bin, "network", "ls"))
		if err != nil {
			t.Errorf("failed to get list of networks: %v", err)
		}
		if strings.Contains(rr.Output(), profile) {
			t.Errorf("expected network %q to not exist after deletion but contained: %s", profile, rr.Output())
		}
	}

}

// validateStatus makes sure paused clusters show up in minikube status correctly
func validateStatus(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	statusOutput := runStatusCmd(ctx, t, profile, false)
	var cs cmd.ClusterState
	if err := json.Unmarshal(statusOutput, &cs); err != nil {
		t.Fatalf("unmarshalling: %v", err)
	}
	// verify the status looks as we expect
	if cs.StatusCode != cmd.Paused {
		t.Fatalf("incorrect status code: %v", cs.StatusCode)
	}
	if cs.StatusName != "Paused" {
		t.Fatalf("incorrect status name: %v", cs.StatusName)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

const mapMaxSize = 1e4

type Binary struct {
	Asset   []byte
	Name    string
	BaseDir string
}

func NewBinary(name string) Binary {
	wd, err := os.Getwd()
	if err != nil {
		wd = "/"
	}
	return Binary{
		Name:    name,
		BaseDir: wd,
	}
}

func (b *Binary) FileName() string {
	return strings.Replace(b.Name, "/", "_", -1)
}

func (b *Binary) FilePath() string {
	return filepath.Join(b.BaseDir, b.FileName())
}

func (b *Binary) TempPath() string {
	// FIXME: ensure using memory instead of disk if p ossible
	return filepath.Join(os.TempDir(), b.FileName())
}

func (b *Binary) Uninstall(filepath string) error {
	logrus.Infof("Uninstalling binary %q (%s)", b.Name, filepath)

	if _, err := os.Stat(filepath); os.IsNotExist(err) {
		logrus.Debugf("Asset %q not installed, nothing to do", filepath)
		return nil
	}

	return os.Remove(filepath)
}

func (b *Binary) Install(filepath string) error {
	logrus.Infof("Installing binary %q (%s)", b.Name, filepath)

	if _, err := os.Stat(filepath); !os.IsNotExist(err) {
		logrus.Debugf("Asset already installed: %q", filepath)
		return nil
	}

	asset, err := Asset(b.Name)
	if err != nil {
		return fmt.Errorf("failed to load the asset %q: %v", b.Name, err)
	}

	length := len(asset)
	size := int(unsafe.Sizeof(0)) * length
	if size > mapMaxSize*int(unsafe.Sizeof(0)) {
		return fmt.Errorf("file too big for current map size: %d > %d", size, mapMaxSize*int(unsafe.Sizeof(0)))
	}

	logrus.Debugf("Creating map file: %q", filepath)
	mapFile, err := os.Create(filepath)
	if err != nil {
		return fmt.Errorf("failed to create map file: %v", err)
	}

	logrus.Debugf("Seeking file")
	if _, err = mapFile.Seek(int64(length-1), 0); err != nil {
		return fmt.Errorf("failed to seek: %v", err)
	}

	logrus.Debugf("Writing to file")
	if _, err = mapFile.Write([]byte(" ")); err != nil {
		return fmt.Errorf("failed to write to file: %v", err)
	}

	logrus.Debugf("MMAPing")
	fd := int(mapFile.Fd())
	mmap, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		return fmt.Errorf("failed to mmap: %v", err)
	}

	logrus.Debugf("Filling array var")
	mapArray := (*[mapMaxSize]byte)(unsafe.Pointer(&mmap[0]))
	for i := 0; i < length; i++ {
		mapArray[i] = asset[i]
	}
	// fmt.Println(*mapArray)

	logrus.Debugf("MUNMAPing")
	if err = syscall.Munmap(mmap); err != nil {
		return fmt.Errorf("failed to munmap: %v", err)
	}

	logrus.Debugf("Closing")
	if err = mapFile.Close(); err != nil {
		return fmt.Errorf("failed to close: %v", err)
	}

	logrus.Debugf("Chmoding binary")
	if err = os.Chmod(filepath, 0777); err != nil {
		return fmt.Errorf("failed to chmod program: %v", err)
	}

	return nil
}

func (b *Binary) Execute(args []string) error {
	filepath := b.TempPath()
	logrus.Infof("Executing binary %q (%s)", b.Name, filepath)
	if len(args) > 0 {
		logrus.Infof("args=%v", args)
	}

	// temporary
	if err := b.Uninstall(filepath); err != nil {
		logrus.Debugf("Failed to uninstall temporary binary %q: %v", filepath, err)
	}

	if err := b.Install(filepath); err != nil {
		return err
	}
	// FIXME: delete early
	defer b.Uninstall(filepath)

	cmd := exec.Command(filepath, args...)
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Start()

	return cmd.Wait()
}

func GetBinaryByName(name string) (*Binary, error) {
	for _, assetName := range AssetNames() {
		if name == assetName {
			bin := NewBinary(assetName)
			return &bin, nil
		}
	}
	for _, assetName := range AssetNames() {
		if name == filepath.Base(assetName) {
			bin := NewBinary(assetName)
			return &bin, nil
		}
	}
	return nil, fmt.Errorf("No match")
}

func ActionExecute(c *cli.Context) {
	bin, err := GetBinaryByName(c.Command.Name)
	if err != nil {
		logrus.Fatalf("No such binary %q: %v", c.Command.Name, err)
	}

	if err := bin.Execute(c.Args()); err != nil {
		logrus.Fatalf("Failed to execute binary: %v", err)
	}
}

func ActionInstall(c *cli.Context) {
	for _, name := range AssetNames() {
		bin := NewBinary(name)
		if err := bin.Install(bin.FilePath()); err != nil {
			logrus.Fatalf("Failed to install binary: %v", err)
		}
	}
}
func ActionUninstall(c *cli.Context) {
	for _, name := range AssetNames() {
		bin := NewBinary(name)
		if err := bin.Uninstall(bin.FilePath()); err != nil {
			logrus.Fatalf("Failed to uninstall binary: %v", err)
		}
	}
}

func hookBefore(c *cli.Context) error {
	if c.Bool("debug") {
		os.Setenv("MB_DEBUG", "1")
	}
	initLogging()
	return nil
}

func initLogging() {
	if os.Getenv("MB_DEBUG") == "1" {
		logrus.SetLevel(logrus.DebugLevel)
	} else {
		logrus.SetLevel(logrus.WarnLevel)
	}
}

func main() {
	// Checking if file is a symlink to mister-bin or mister-bin itself
	fi, err := os.Lstat(os.Args[0])
	if err != nil {
		logrus.Fatalf("Failed to lstat %q: %v", os.Args[0], err)
	}

	// we are a symlink, direct execution
	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		initLogging()
		bin, err := GetBinaryByName(filepath.Base(os.Args[0]))
		if err != nil {
			logrus.Fatalf("No such binary %q: %v", filepath.Base(os.Args[0]), err)
		}

		if err := bin.Execute(os.Args[1:]); err != nil {
			logrus.Fatalf("Failed to execute binary: %v", err)
		}
		return
	}

	// we are not a symlink, standard menu
	app := cli.NewApp()
	app.Name = "Mister Bin"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:   "debug, D",
			EnvVar: "MB_DEBUG",
		},
	}

	app.Before = hookBefore

	app.Commands = []cli.Command{
		{
			Name:   "install",
			Action: ActionInstall,
		},
		{
			Name:   "uninstall",
			Action: ActionUninstall,
		},
	}

	for _, name := range AssetNames() {
		command := cli.Command{
			Name:   filepath.Base(name),
			Action: ActionExecute,
		}
		app.Commands = append(app.Commands, command)
	}

	app.Run(os.Args)
}
<commit_msg>Support of '--basedir' option (fix #3)<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

const mapMaxSize = 1e4

type Binary struct {
	Asset   []byte
	Name    string
	BaseDir string
}

func NewBinary(name string) Binary {
	wd, err := os.Getwd()
	if err != nil {
		wd = "/"
	}
	return Binary{
		Name:    name,
		BaseDir: wd,
	}
}

func (b *Binary) FileName() string {
	return strings.Replace(b.Name, "/", "_", -1)
}

func (b *Binary) FilePath() string {
	return filepath.Join(b.BaseDir, b.FileName())
}

func (b *Binary) TempPath() string {
	// FIXME: ensure using memory instead of disk if p ossible
	return filepath.Join(os.TempDir(), b.FileName())
}

func (b *Binary) Uninstall(filepath string) error {
	logrus.Infof("Uninstalling binary %q (%s)", b.Name, filepath)

	if _, err := os.Stat(filepath); os.IsNotExist(err) {
		logrus.Debugf("Asset %q not installed, nothing to do", filepath)
		return nil
	}

	return os.Remove(filepath)
}

func (b *Binary) Install(filepath string) error {
	logrus.Infof("Installing binary %q (%s)", b.Name, filepath)

	if _, err := os.Stat(filepath); !os.IsNotExist(err) {
		logrus.Debugf("Asset already installed: %q", filepath)
		return nil
	}

	asset, err := Asset(b.Name)
	if err != nil {
		return fmt.Errorf("failed to load the asset %q: %v", b.Name, err)
	}

	length := len(asset)
	size := int(unsafe.Sizeof(0)) * length
	if size > mapMaxSize*int(unsafe.Sizeof(0)) {
		return fmt.Errorf("file too big for current map size: %d > %d", size, mapMaxSize*int(unsafe.Sizeof(0)))
	}

	logrus.Debugf("Creating map file: %q", filepath)
	mapFile, err := os.Create(filepath)
	if err != nil {
		return fmt.Errorf("failed to create map file: %v", err)
	}

	logrus.Debugf("Seeking file")
	if _, err = mapFile.Seek(int64(length-1), 0); err != nil {
		return fmt.Errorf("failed to seek: %v", err)
	}

	logrus.Debugf("Writing to file")
	if _, err = mapFile.Write([]byte(" ")); err != nil {
		return fmt.Errorf("failed to write to file: %v", err)
	}

	logrus.Debugf("MMAPing")
	fd := int(mapFile.Fd())
	mmap, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		return fmt.Errorf("failed to mmap: %v", err)
	}

	logrus.Debugf("Filling array var")
	mapArray := (*[mapMaxSize]byte)(unsafe.Pointer(&mmap[0]))
	for i := 0; i < length; i++ {
		mapArray[i] = asset[i]
	}
	// fmt.Println(*mapArray)

	logrus.Debugf("MUNMAPing")
	if err = syscall.Munmap(mmap); err != nil {
		return fmt.Errorf("failed to munmap: %v", err)
	}

	logrus.Debugf("Closing")
	if err = mapFile.Close(); err != nil {
		return fmt.Errorf("failed to close: %v", err)
	}

	logrus.Debugf("Chmoding binary")
	if err = os.Chmod(filepath, 0777); err != nil {
		return fmt.Errorf("failed to chmod program: %v", err)
	}

	return nil
}

func (b *Binary) Execute(args []string) error {
	filepath := b.TempPath()
	logrus.Infof("Executing binary %q (%s)", b.Name, filepath)
	if len(args) > 0 {
		logrus.Infof("args=%v", args)
	}

	// temporary
	if err := b.Uninstall(filepath); err != nil {
		logrus.Debugf("Failed to uninstall temporary binary %q: %v", filepath, err)
	}

	if err := b.Install(filepath); err != nil {
		return err
	}
	// FIXME: delete early
	defer b.Uninstall(filepath)

	cmd := exec.Command(filepath, args...)
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Start()

	return cmd.Wait()
}

func GetBinaryByName(name string) (*Binary, error) {
	for _, assetName := range AssetNames() {
		if name == assetName {
			bin := NewBinary(assetName)
			return &bin, nil
		}
	}
	for _, assetName := range AssetNames() {
		if name == filepath.Base(assetName) {
			bin := NewBinary(assetName)
			return &bin, nil
		}
	}
	return nil, fmt.Errorf("No match")
}

func ActionExecute(c *cli.Context) {
	bin, err := GetBinaryByName(c.Command.Name)
	if err != nil {
		logrus.Fatalf("No such binary %q: %v", c.Command.Name, err)
	}
	binaryUseContext(&bin, c)

	if err := bin.Execute(c.Args()); err != nil {
		logrus.Fatalf("Failed to execute binary: %v", err)
	}
}

func binaryUseContext(bin *Binary, context *cli.Context) {
	if basedir := context.Parent().String("basedir"); basedir != "" {
		bin.BaseDir = basedir
	}
}

func ActionInstall(c *cli.Context) {
	for _, name := range AssetNames() {
		bin := NewBinary(name)
		binaryUseContext(&bin, c)

		if err := bin.Install(bin.FilePath()); err != nil {
			logrus.Fatalf("Failed to install binary: %v", err)
		}
	}
}
func ActionUninstall(c *cli.Context) {
	for _, name := range AssetNames() {
		bin := NewBinary(name)
		binaryUseContext(&bin, c)

		if err := bin.Uninstall(bin.FilePath()); err != nil {
			logrus.Fatalf("Failed to uninstall binary: %v", err)
		}
	}
}

func hookBefore(c *cli.Context) error {
	if c.Bool("debug") {
		os.Setenv("MB_DEBUG", "1")
	}
	initLogging()
	return nil
}

func initLogging() {
	if os.Getenv("MB_DEBUG") == "1" {
		logrus.SetLevel(logrus.DebugLevel)
	} else {
		logrus.SetLevel(logrus.WarnLevel)
	}
}

func main() {
	// Checking if file is a symlink to mister-bin or mister-bin itself
	fi, err := os.Lstat(os.Args[0])
	if err != nil {
		logrus.Fatalf("Failed to lstat %q: %v", os.Args[0], err)
	}

	// we are a symlink, direct execution
	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		initLogging()
		bin, err := GetBinaryByName(filepath.Base(os.Args[0]))
		if err != nil {
			logrus.Fatalf("No such binary %q: %v", filepath.Base(os.Args[0]), err)
		}

		if err := bin.Execute(os.Args[1:]); err != nil {
			logrus.Fatalf("Failed to execute binary: %v", err)
		}
		return
	}

	// we are not a symlink, standard menu
	app := cli.NewApp()
	app.Name = "Mister Bin"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:   "debug, D",
			EnvVar: "MB_DEBUG",
		},
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "basedir",
		},
	}

	app.Before = hookBefore

	app.Commands = []cli.Command{
		{
			Name:   "install",
			Action: ActionInstall,
		},
		{
			Name:   "uninstall",
			Action: ActionUninstall,
		},
	}

	for _, name := range AssetNames() {
		command := cli.Command{
			Name:   filepath.Base(name),
			Action: ActionExecute,
		}
		app.Commands = append(app.Commands, command)
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"strconv"
	"time"
	"unicode/utf8"

	log "github.com/Sirupsen/logrus"
	serial "go.bug.st/serial.v1"
)

type SerialConfig struct {
	Name string
	Baud int

	// Size     int // 0 get translated to 8
	// Parity   SomeNewTypeToGetCorrectDefaultOf_None
	// StopBits SomeNewTypeToGetCorrectDefaultOf_1

	// RTSFlowControl bool
	// DTRFlowControl bool
	// XONFlowControl bool

	// CRLFTranslate bool
	// TimeoutStuff int
	RtsOn bool
	DtrOn bool
}

type serport struct {
	// The serial port connection.
	portConf *SerialConfig
	portIo   io.ReadWriteCloser

	done chan bool // signals the end of this request

	// Keep track of whether we're being actively closed
	// just so we don't show scary error messages
	isClosing bool

	isClosingDueToError bool

	// counter incremented on queue, decremented on write
	itemsInBuffer int

	// buffered channel containing up to 25600 outbound messages.
	sendBuffered chan string

	// unbuffered channel of outbound messages that bypass internal serial port buffer
	sendNoBuf chan string

	// Do we have an extra channel/thread to watch our buffer?
	BufferType string
	//bufferwatcher *BufferflowDummypause
	bufferwatcher Bufferflow
}

type Cmd struct {
	data                       string
	id                         string
	skippedBuffer              bool
	willHandleCompleteResponse bool
}

type CmdComplete struct {
	Cmd     string
	Id      string
	P       string
	BufSize int    `json:"-"`
	D       string `json:"-"`
}

type qwReport struct {
	Cmd  string
	QCnt int
	Id   string
	D    string `json:"-"`
	Buf  string `json:"-"`
	P    string
}

type SpPortMessage struct {
	// P string // the port, i.e. com22
	D string // the data, i.e. G0 X0 Y0
}

type SpPortMessageRaw struct {
	// P string // the port, i.e. com22
	D []byte // the data, i.e. G0 X0 Y0
}

func (p *serport) reader() {

	//var buf bytes.Buffer
	ch := make([]byte, 1024)
	timeCheckOpen := time.Now()
	var buffered_ch bytes.Buffer

	for {

		n, err := p.portIo.Read(ch)

		if err == nil {
			if n > len(ch) {
				n = len(ch)
			}
			ch = append(buffered_ch.Bytes(), ch[:n-1]...)
			n += len(buffered_ch.Bytes())
			buffered_ch.Reset()
		}

		//if we detect that port is closing, break out o this for{} loop.
		if p.isClosing {
			strmsg := "Shutting down reader on " + p.portConf.Name
			log.Println(strmsg)
			h.broadcastSys <- []byte(strmsg)
			break
		}

		// read can return legitimate bytes as well as an error
		// so process the bytes if n > 0
		if n > 0 {
			//log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(ch))

			data := ""

			for i, w := 0, 0; i < n; i += w {
				runeValue, width := utf8.DecodeRune(ch[i:n])
				if runeValue == utf8.RuneError {
					buffered_ch.Write(append(ch[i:n]))
					break
				}
				if i == n {
					buffered_ch.Reset()
				}
				data += string(runeValue)
				w = width
			}

			//log.Print("The data i will convert to json is:")
			//log.Print(data)

			// give the data to our bufferflow so it can do it's work
			// to read/translate the data to see if it wants to block
			// writes to the serialport. each bufferflow type will decide
			// this on its own based on its logic, i.e. tinyg vs grbl vs others
			//p.b.bufferwatcher..OnIncomingData(data)
			p.bufferwatcher.OnIncomingData(data)

			// see if the OnIncomingData handled the broadcast back
			// to the user. this option was added in case the OnIncomingData wanted
			// to do something fancier or implementation specific, i.e. TinyG Buffer
			// actually sends back data on a perline basis rather than our method
			// where we just send the moment we get it. the reason for this is that
			// the browser was sometimes getting back packets out of order which
			// of course would screw things up when parsing

			if p.bufferwatcher.IsBufferGloballySendingBackIncomingData() == false {
				//m := SpPortMessage{"Alice", "Hello"}
				m := SpPortMessage{data}
				//log.Print("The m obj struct is:")
				//log.Print(m)

				//b, err := json.MarshalIndent(m, "", "\t")
				b, err := json.Marshal(m)
				if err != nil {
					log.Println(err)
					h.broadcastSys <- []byte("Error creating json on " + p.portConf.Name + " " +
						err.Error() + " The data we were trying to convert is: " + string(ch[:n]))
					break
				}
				//log.Print("Printing out json byte data...")
				//log.Print(string(b))
				h.broadcastSys <- b
			}
		}

		// double check that we got characters in the buffer
		// before deciding if an EOF is legitimately a reason
		// to close the port because we're seeing that on some
		// os's like Linux/Ubuntu you get an EOF when you open
		// the port. Perhaps the EOF was buffered from a previous
		// close and the OS doesn't clear out that buffer on a new
		// connect. This means we'll only catch EOF's when there are
		// other characters with it, but that seems to work ok
		if n <= 0 {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				// hit end of file
				log.Println("Hit end of file on serial port")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")

			}

			if err != nil {
				log.Println(err)
				h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
					err.Error() + " Closing port.")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
				p.isClosingDueToError = true
				break
			}

			// Keep track of time difference between two consecutive read with n == 0 and err == nil
			// we get here if the port has been disconnected while open (cpu usage will jump to 100%)
			// let's close the port only if the events are extremely fast (<1ms)
			if err == nil {
				diff := time.Since(timeCheckOpen)
				if diff.Nanoseconds() < 1000000 {
					p.isClosingDueToError = true
					break
				}
				timeCheckOpen = time.Now()
			}
		}
	}
	if p.isClosingDueToError {
		spCloseReal(p)
	}
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerBuffered() {

	// this method can panic if user closes serial port and something is
	// in BlockUntilReady() and then a send occurs on p.sendNoBuf

	defer func() {
		if e := recover(); e != nil {
			log.Println("Got panic: ", e)
		}
	}()

	// this for loop blocks on p.sendBuffered until that channel
	// sees something come in
	for data := range p.sendBuffered {

		// we want to block here if we are being asked to pause.
		goodToGo, _ := p.bufferwatcher.BlockUntilReady(string(data), "")

		if goodToGo == false {
			log.Println("We got back from BlockUntilReady() but apparently we must cancel this cmd")
			// since we won't get a buffer decrement in p.sendNoBuf, we must do it here
			p.itemsInBuffer--
		} else {
			// send to the non-buffered serial port writer
			//log.Println("About to send to p.sendNoBuf channel")
			p.sendNoBuf <- data
		}
	}
	msgstr := "writerBuffered just got closed. make sure you make a new one. port:" + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerNoBuf() {
	// this for loop blocks on p.send until that channel
	// sees something come in
	for data := range p.sendNoBuf {

		// if we get here, we were able to write successfully
		// to the serial port because it blocks until it can write

		// decrement counter
		p.itemsInBuffer--
		log.Printf("itemsInBuffer:%v\n", p.itemsInBuffer)

		// FINALLY, OF ALL THE CODE IN THIS PROJECT
		// WE TRULY/FINALLY GET TO WRITE TO THE SERIAL PORT!
		n2, err := p.portIo.Write([]byte(data))

		log.Print("Just wrote ", n2, " bytes to serial: ", string(data))
		if err != nil {
			errstr := "Error writing to " + p.portConf.Name + " " + err.Error() + " Closing port."
			log.Print(errstr)
			h.broadcastSys <- []byte(errstr)
			break
		}
	}
	msgstr := "Shutting down writer on " + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
	p.portIo.Close()
	spListDual(false)
	spList(false)
}

func spHandlerOpen(portname string, baud int, buftype string) {

	log.Print("Inside spHandler")

	var out bytes.Buffer

	out.WriteString("Opening serial port ")
	out.WriteString(portname)
	out.WriteString(" at ")
	out.WriteString(strconv.Itoa(baud))
	out.WriteString(" baud")
	log.Print(out.String())

	conf := &SerialConfig{Name: portname, Baud: baud, RtsOn: true}

	mode := &serial.Mode{
		BaudRate: baud,
	}

	sp, err := serial.Open(portname, mode)
	log.Print("Just tried to open port")
	if err != nil {
		//log.Fatal(err)
		log.Print("Error opening port " + err.Error())
		//h.broadcastSys <- []byte("Error opening port. " + err.Error())
		h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Error opening port. " + err.Error() + "\",\"Port\":\"" + conf.Name + "\",\"Baud\":" + strconv.Itoa(conf.Baud) + "}")

		return
	}
	log.Print("Opened port successfully")
	//p := &serport{send: make(chan []byte, 256), portConf: conf, portIo: sp}
	// we can go up to 256,000 lines of gcode in the buffer
	p := &serport{sendBuffered: make(chan string, 256000), sendNoBuf: make(chan string), portConf: conf, portIo: sp, BufferType: buftype}

	var bw Bufferflow

	if buftype == "timed" {
		bw = &BufferflowTimed{Name: "timed", Port: portname, Output: h.broadcastSys, Input: make(chan string)}
	} else if buftype == "timedraw" {
		bw = &BufferflowTimedRaw{Name: "timedraw", Port: portname, Output: h.broadcastSys, Input: make(chan string)}
	} else {
		bw = &BufferflowDefault{Port: portname}
	}

	bw.Init()
	p.bufferwatcher = bw

	sh.register <- p
	defer func() { sh.unregister <- p }()

	spListDual(false)
	spList(false)

	// this is internally buffered thread to not send to serial port if blocked
	go p.writerBuffered()
	// this is thread to send to serial port regardless of block
	go p.writerNoBuf()
	p.reader()

	spListDual(false)
	spList(false)

	//go p.reader()
	//p.done = make(chan bool)
	//<-p.done
}

func spHandlerClose(p *serport) {
	p.isClosing = true
	h.broadcastSys <- []byte("Closing serial port " + p.portConf.Name)
	spCloseReal(p)
}

func spCloseReal(p *serport) {
	p.bufferwatcher.Close()
	p.portIo.Close()
	spListDual(false)
	spList(false)
}
<commit_msg>Revert "fix stupid bug on serial buffering routine"<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"strconv"
	"time"
	"unicode/utf8"

	log "github.com/Sirupsen/logrus"
	serial "go.bug.st/serial.v1"
)

type SerialConfig struct {
	Name string
	Baud int

	// Size     int // 0 get translated to 8
	// Parity   SomeNewTypeToGetCorrectDefaultOf_None
	// StopBits SomeNewTypeToGetCorrectDefaultOf_1

	// RTSFlowControl bool
	// DTRFlowControl bool
	// XONFlowControl bool

	// CRLFTranslate bool
	// TimeoutStuff int
	RtsOn bool
	DtrOn bool
}

type serport struct {
	// The serial port connection.
	portConf *SerialConfig
	portIo   io.ReadWriteCloser

	done chan bool // signals the end of this request

	// Keep track of whether we're being actively closed
	// just so we don't show scary error messages
	isClosing bool

	isClosingDueToError bool

	// counter incremented on queue, decremented on write
	itemsInBuffer int

	// buffered channel containing up to 25600 outbound messages.
	sendBuffered chan string

	// unbuffered channel of outbound messages that bypass internal serial port buffer
	sendNoBuf chan string

	// Do we have an extra channel/thread to watch our buffer?
	BufferType string
	//bufferwatcher *BufferflowDummypause
	bufferwatcher Bufferflow
}

type Cmd struct {
	data                       string
	id                         string
	skippedBuffer              bool
	willHandleCompleteResponse bool
}

type CmdComplete struct {
	Cmd     string
	Id      string
	P       string
	BufSize int    `json:"-"`
	D       string `json:"-"`
}

type qwReport struct {
	Cmd  string
	QCnt int
	Id   string
	D    string `json:"-"`
	Buf  string `json:"-"`
	P    string
}

type SpPortMessage struct {
	// P string // the port, i.e. com22
	D string // the data, i.e. G0 X0 Y0
}

type SpPortMessageRaw struct {
	// P string // the port, i.e. com22
	D []byte // the data, i.e. G0 X0 Y0
}

func (p *serport) reader() {

	//var buf bytes.Buffer
	ch := make([]byte, 1024)
	timeCheckOpen := time.Now()
	var buffered_ch bytes.Buffer

	for {

		n, err := p.portIo.Read(ch)

		//if we detect that port is closing, break out o this for{} loop.
		if p.isClosing {
			strmsg := "Shutting down reader on " + p.portConf.Name
			log.Println(strmsg)
			h.broadcastSys <- []byte(strmsg)
			break
		}

		ch = append(buffered_ch.Bytes(), ch[:n]...)
		n += len(buffered_ch.Bytes())
		buffered_ch.Reset()

		// read can return legitimate bytes as well as an error
		// so process the bytes if n > 0
		if n > 0 {
			//log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(ch))

			data := ""

			for i, w := 0, 0; i < n; i += w {
				runeValue, width := utf8.DecodeRune(ch[i:n])
				if runeValue == utf8.RuneError {
					buffered_ch.Write(append(ch[i:n]))
					break
				}
				if i == n {
					buffered_ch.Reset()
				}
				data += string(runeValue)
				w = width
			}

			//log.Print("The data i will convert to json is:")
			//log.Print(data)

			// give the data to our bufferflow so it can do it's work
			// to read/translate the data to see if it wants to block
			// writes to the serialport. each bufferflow type will decide
			// this on its own based on its logic, i.e. tinyg vs grbl vs others
			//p.b.bufferwatcher..OnIncomingData(data)
			p.bufferwatcher.OnIncomingData(data)

			// see if the OnIncomingData handled the broadcast back
			// to the user. this option was added in case the OnIncomingData wanted
			// to do something fancier or implementation specific, i.e. TinyG Buffer
			// actually sends back data on a perline basis rather than our method
			// where we just send the moment we get it. the reason for this is that
			// the browser was sometimes getting back packets out of order which
			// of course would screw things up when parsing

			if p.bufferwatcher.IsBufferGloballySendingBackIncomingData() == false {
				//m := SpPortMessage{"Alice", "Hello"}
				m := SpPortMessage{data}
				//log.Print("The m obj struct is:")
				//log.Print(m)

				//b, err := json.MarshalIndent(m, "", "\t")
				b, err := json.Marshal(m)
				if err != nil {
					log.Println(err)
					h.broadcastSys <- []byte("Error creating json on " + p.portConf.Name + " " +
						err.Error() + " The data we were trying to convert is: " + string(ch[:n]))
					break
				}
				//log.Print("Printing out json byte data...")
				//log.Print(string(b))
				h.broadcastSys <- b
			}
		}

		// double check that we got characters in the buffer
		// before deciding if an EOF is legitimately a reason
		// to close the port because we're seeing that on some
		// os's like Linux/Ubuntu you get an EOF when you open
		// the port. Perhaps the EOF was buffered from a previous
		// close and the OS doesn't clear out that buffer on a new
		// connect. This means we'll only catch EOF's when there are
		// other characters with it, but that seems to work ok
		if n <= 0 {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				// hit end of file
				log.Println("Hit end of file on serial port")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")

			}

			if err != nil {
				log.Println(err)
				h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
					err.Error() + " Closing port.")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
				p.isClosingDueToError = true
				break
			}

			// Keep track of time difference between two consecutive read with n == 0 and err == nil
			// we get here if the port has been disconnected while open (cpu usage will jump to 100%)
			// let's close the port only if the events are extremely fast (<1ms)
			if err == nil {
				diff := time.Since(timeCheckOpen)
				if diff.Nanoseconds() < 1000000 {
					p.isClosingDueToError = true
					break
				}
				timeCheckOpen = time.Now()
			}
		}
	}
	if p.isClosingDueToError {
		spCloseReal(p)
	}
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerBuffered() {

	// this method can panic if user closes serial port and something is
	// in BlockUntilReady() and then a send occurs on p.sendNoBuf

	defer func() {
		if e := recover(); e != nil {
			log.Println("Got panic: ", e)
		}
	}()

	// this for loop blocks on p.sendBuffered until that channel
	// sees something come in
	for data := range p.sendBuffered {

		// we want to block here if we are being asked to pause.
		goodToGo, _ := p.bufferwatcher.BlockUntilReady(string(data), "")

		if goodToGo == false {
			log.Println("We got back from BlockUntilReady() but apparently we must cancel this cmd")
			// since we won't get a buffer decrement in p.sendNoBuf, we must do it here
			p.itemsInBuffer--
		} else {
			// send to the non-buffered serial port writer
			//log.Println("About to send to p.sendNoBuf channel")
			p.sendNoBuf <- data
		}
	}
	msgstr := "writerBuffered just got closed. make sure you make a new one. port:" + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerNoBuf() {
	// this for loop blocks on p.send until that channel
	// sees something come in
	for data := range p.sendNoBuf {

		// if we get here, we were able to write successfully
		// to the serial port because it blocks until it can write

		// decrement counter
		p.itemsInBuffer--
		log.Printf("itemsInBuffer:%v\n", p.itemsInBuffer)

		// FINALLY, OF ALL THE CODE IN THIS PROJECT
		// WE TRULY/FINALLY GET TO WRITE TO THE SERIAL PORT!
		n2, err := p.portIo.Write([]byte(data))

		log.Print("Just wrote ", n2, " bytes to serial: ", string(data))
		if err != nil {
			errstr := "Error writing to " + p.portConf.Name + " " + err.Error() + " Closing port."
			log.Print(errstr)
			h.broadcastSys <- []byte(errstr)
			break
		}
	}
	msgstr := "Shutting down writer on " + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
	p.portIo.Close()
	spListDual(false)
	spList(false)
}

func spHandlerOpen(portname string, baud int, buftype string) {

	log.Print("Inside spHandler")

	var out bytes.Buffer

	out.WriteString("Opening serial port ")
	out.WriteString(portname)
	out.WriteString(" at ")
	out.WriteString(strconv.Itoa(baud))
	out.WriteString(" baud")
	log.Print(out.String())

	conf := &SerialConfig{Name: portname, Baud: baud, RtsOn: true}

	mode := &serial.Mode{
		BaudRate: baud,
	}

	sp, err := serial.Open(portname, mode)
	log.Print("Just tried to open port")
	if err != nil {
		//log.Fatal(err)
		log.Print("Error opening port " + err.Error())
		//h.broadcastSys <- []byte("Error opening port. " + err.Error())
		h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Error opening port. " + err.Error() + "\",\"Port\":\"" + conf.Name + "\",\"Baud\":" + strconv.Itoa(conf.Baud) + "}")

		return
	}
	log.Print("Opened port successfully")
	//p := &serport{send: make(chan []byte, 256), portConf: conf, portIo: sp}
	// we can go up to 256,000 lines of gcode in the buffer
	p := &serport{sendBuffered: make(chan string, 256000), sendNoBuf: make(chan string), portConf: conf, portIo: sp, BufferType: buftype}

	var bw Bufferflow

	if buftype == "timed" {
		bw = &BufferflowTimed{Name: "timed", Port: portname, Output: h.broadcastSys, Input: make(chan string)}
	} else if buftype == "timedraw" {
		bw = &BufferflowTimedRaw{Name: "timedraw", Port: portname, Output: h.broadcastSys, Input: make(chan string)}
	} else {
		bw = &BufferflowDefault{Port: portname}
	}

	bw.Init()
	p.bufferwatcher = bw

	sh.register <- p
	defer func() { sh.unregister <- p }()

	spListDual(false)
	spList(false)

	// this is internally buffered thread to not send to serial port if blocked
	go p.writerBuffered()
	// this is thread to send to serial port regardless of block
	go p.writerNoBuf()
	p.reader()

	spListDual(false)
	spList(false)

	//go p.reader()
	//p.done = make(chan bool)
	//<-p.done
}

func spHandlerClose(p *serport) {
	p.isClosing = true
	h.broadcastSys <- []byte("Closing serial port " + p.portConf.Name)
	spCloseReal(p)
}

func spCloseReal(p *serport) {
	p.bufferwatcher.Close()
	p.portIo.Close()
	spListDual(false)
	spList(false)
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strconv"
)

const ERROR_INVALID_FORMAT = 1
const ERROR_MISSING_PARAMETER = 2
const ERROR_INVALID_FINGERPRINT = 3
const ERROR_INVALID_APIKEY = 4
const ERROR_INTERNAL = 5
const ERROR_INVALID_USER_APIKEY = 6
const ERROR_INVALID_UUID = 7
const ERROR_INVALID_DURATION = 8
const ERROR_INVALID_BITRATE = 9
const ERROR_INVALID_FOREIGNID = 10
const ERROR_INVALID_MAX_DURATION_DIFF = 11
const ERROR_NOT_ALLOWED = 12
const ERROR_SERVICE_UNAVAILABLE = 13
const ERROR_TOO_MANY_REQUESTS = 14
const ERROR_INVALID_MUSICBRAINZ_ACCESS_TOKEN = 15
const ERROR_INSECURE_REQUEST = 16
const ERROR_UNKNOWN_APPLICATION = 17
const ERROR_FINGERPRINT_NOT_FOUND = 18

const MinDuration = 1
const MaxDuration = 32767

type ErrorDetails struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type ErrorResponse struct {
	Status string       `json:"status"`
	Error  ErrorDetails `json:"error"`
}

type WebService struct {
	Mux *http.ServeMux
}

func NewWebService() *WebService {
	ws := &WebService{
		Mux: http.NewServeMux(),
	}
	ws.Mux.HandleFunc("/v2/lookup", ws.HandleV2Lookup)
	return ws
}

func (ws *WebService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ws.Mux.ServeHTTP(w, r)
}

func Error(w http.ResponseWriter, status int, errorCode int, errorMessage string) {
	response := ErrorResponse{
		Status: "error",
		Error: ErrorDetails{
			Code:    errorCode,
			Message: errorMessage,
		},
	}

	contentType := "application/json"
	content, err := json.Marshal(&response)
	if err != nil {
		if errorCode == ERROR_INTERNAL {
			content = []byte("internal error")
			contentType = "text/plain"
		} else {
			Error(w, http.StatusInternalServerError, ERROR_INTERNAL, "internal error")
			return
		}
	}

	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
	w.Write(content)
}

func GetFormat(r *http.Request) (string, error) {
	format := r.FormValue("format")
	if format == "" {
		format = "json"
	}
	if format != "json" && format != "xml" {
		return "", errors.New("invalid format")
	}
	return format, nil
}

func (ws *WebService) HandleV2Lookup(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		Error(w, http.StatusBadRequest, ERROR_INTERNAL, "unable to parse HTTP request data")
		return
	}

	format, err := GetFormat(r)
	if err != nil {
		Error(w, http.StatusBadRequest, ERROR_INVALID_FORMAT, "invalid format")
		return
	}

	durationStr := r.FormValue("duration")
	if durationStr == "" {
		Error(w, http.StatusBadRequest, ERROR_MISSING_PARAMETER, "missing parameter 'duration'")
		return
	}
	duration, err := strconv.ParseFloat(durationStr, 64)
	log.Printf("Duration %v", duration)
	if err != nil || duration < MinDuration || duration >= MaxDuration {
		if err != nil {
			log.Printf("Invalid duration: %s", err)
		}
		Error(w, http.StatusBadRequest, ERROR_INVALID_DURATION, "invalid duration")
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf("{%s}", format)))
}

func RunWebService() error {
	ws := NewWebService()
	return http.ListenAndServe(":8080", ws)
}
<commit_msg>Parse fingerprint<commit_after>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/acoustid/go-acoustid/chromaprint"
	"log"
	"net/http"
	"strconv"
)

const ERROR_INVALID_FORMAT = 1
const ERROR_MISSING_PARAMETER = 2
const ERROR_INVALID_FINGERPRINT = 3
const ERROR_INVALID_APIKEY = 4
const ERROR_INTERNAL = 5
const ERROR_INVALID_USER_APIKEY = 6
const ERROR_INVALID_UUID = 7
const ERROR_INVALID_DURATION = 8
const ERROR_INVALID_BITRATE = 9
const ERROR_INVALID_FOREIGNID = 10
const ERROR_INVALID_MAX_DURATION_DIFF = 11
const ERROR_NOT_ALLOWED = 12
const ERROR_SERVICE_UNAVAILABLE = 13
const ERROR_TOO_MANY_REQUESTS = 14
const ERROR_INVALID_MUSICBRAINZ_ACCESS_TOKEN = 15
const ERROR_INSECURE_REQUEST = 16
const ERROR_UNKNOWN_APPLICATION = 17
const ERROR_FINGERPRINT_NOT_FOUND = 18

const MinDuration = 1
const MaxDuration = 32767

type ErrorDetails struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type ErrorResponse struct {
	Status string       `json:"status"`
	Error  ErrorDetails `json:"error"`
}

type WebService struct {
	Mux *http.ServeMux
}

func NewWebService() *WebService {
	ws := &WebService{
		Mux: http.NewServeMux(),
	}
	ws.Mux.HandleFunc("/v2/lookup", ws.HandleLookup)
	return ws
}

func (ws *WebService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ws.Mux.ServeHTTP(w, r)
}

func Error(w http.ResponseWriter, status int, errorCode int, errorMessage string) {
	response := ErrorResponse{
		Status: "error",
		Error: ErrorDetails{
			Code:    errorCode,
			Message: errorMessage,
		},
	}

	contentType := "application/json"
	content, err := json.Marshal(&response)
	if err != nil {
		if errorCode == ERROR_INTERNAL {
			content = []byte("internal error")
			contentType = "text/plain"
		} else {
			Error(w, http.StatusInternalServerError, ERROR_INTERNAL, "internal error")
			return
		}
	}

	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
	w.Write(content)
}

func GetFormat(r *http.Request) (string, error) {
	format := r.FormValue("format")
	if format == "" {
		format = "json"
	}
	if format != "json" && format != "xml" {
		return "", errors.New("invalid format")
	}
	return format, nil
}

type lookupRequest struct {
	format   string
	duration float64
}

func (req *lookupRequest) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		Error(w, http.StatusBadRequest, ERROR_INTERNAL, "unable to parse HTTP request data")
		return
	}

	format, err := GetFormat(r)
	if err != nil {
		Error(w, http.StatusBadRequest, ERROR_INVALID_FORMAT, "invalid format")
		return
	}

	durationStr := r.FormValue("duration")
	if durationStr == "" {
		Error(w, http.StatusBadRequest, ERROR_MISSING_PARAMETER, "missing parameter 'duration'")
		return
	}
	duration, err := strconv.ParseFloat(durationStr, 64)
	if err != nil || duration < MinDuration || duration >= MaxDuration {
		if err != nil {
			log.Printf("Invalid duration: %s", err)
		}
		Error(w, http.StatusBadRequest, ERROR_INVALID_DURATION, "invalid duration")
		return
	}

	fingerprintStr := r.FormValue("fingerprint")
	if fingerprintStr == "" {
		Error(w, http.StatusBadRequest, ERROR_MISSING_PARAMETER, "missing parameter 'fingerprint'")
		return
	}
	fingerprint, err := chromaprint.ParseFingerprintString(fingerprintStr)
	if err != nil {
		log.Printf("Invalid fingerprint: %s", err)
		Error(w, http.StatusBadRequest, ERROR_INVALID_FINGERPRINT, "invalid fingerprint")
		return
	}

	log.Printf("format=%s duration=%s fingerprint=%s", format, duration, fingerprint)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf("{%s}", format)))
}

func (ws *WebService) HandleLookup(w http.ResponseWriter, r *http.Request) {
	var req lookupRequest
	req.ServeHTTP(w, r)
}

func RunWebService() error {
	ws := NewWebService()
	return http.ListenAndServe(":8080", ws)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"github.com/james4k/rcon"
)

type FactorioServer struct {
	Cmd      *exec.Cmd              `json:"-"`
	Savefile string                 `json:"savefile"`
	Latency  int                    `json:"latency"`
	Port     int                    `json:"port"`
	Running  bool                   `json:"running"`
	StdOut   io.ReadCloser          `json:"-"`
	StdErr   io.ReadCloser          `json:"-"`
	StdIn    io.WriteCloser         `json:"-"`
	Settings map[string]interface{} `json:"-"`
	Rcon     *rcon.RemoteConsole    `json:"-"`
}

func initFactorio() (f *FactorioServer, err error) {
	f = new(FactorioServer)
	f.Settings = make(map[string]interface{})

	if err = os.MkdirAll(config.FactorioConfigDir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create config directory: %v", err)
	}

	settingsPath := filepath.Join(config.FactorioConfigDir, config.SettingsFile)
	var settings *os.File

	if _, err := os.Stat(settingsPath); os.IsNotExist(err) {
		// copy example settings to supplied settings file, if not exists
		log.Printf("Server settings at %s not found, copying example server settings.\n", settingsPath)

		examplePath := filepath.Join(config.FactorioDir, "data", "server-settings.example.json")

		example, err := os.Open(examplePath)
		if err != nil {
			return nil, fmt.Errorf("failed to open example server settings: %v", err)
		}
		defer example.Close()

		settings, err = os.Create(settingsPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create server settings file: %v", err)
		}
		defer settings.Close()

		_, err = io.Copy(settings, example)
		if err != nil {
			return nil, fmt.Errorf("failed to copy example server settings: %v", err)
		}
	} else {
		// otherwise, open file normally
		settings, err = os.Open(settingsPath)
		if err != nil {
			return nil, fmt.Errorf("failed to open server settings file: %v", err)
		}
		defer settings.Close()
	}

	// before reading reset offset
	if _, err = settings.Seek(0, 0); err != nil {
		return nil, fmt.Errorf("error while seeking in settings file: %v", err)
	}

	if err = json.NewDecoder(settings).Decode(&f.Settings); err != nil {
		return nil, fmt.Errorf("error reading %s: %v", settingsPath, err)
	}

	log.Printf("Loaded Factorio settings from %s\n", settingsPath)

	return
}

func (f *FactorioServer) Run() error {
	var err error

	data, err := json.MarshalIndent(f.Settings, "", "  ")
	if err != nil {
		log.Println("Failed to marshal FactorioServerSettings: ", err)
	} else {
		ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.SettingsFile), data, 0644)
	}

	args := []string{
		"--start-server", filepath.Join(config.FactorioSavesDir, f.Savefile),
		"--port", strconv.Itoa(f.Port),
		"--server-settings", filepath.Join(config.FactorioConfigDir, "server-settings.json"),
	}

	log.Println("Starting server with command: ", config.FactorioBinary, args)

	f.Cmd = exec.Command(config.FactorioBinary, args...)

	f.StdOut, err = f.Cmd.StdoutPipe()
	if err != nil {
		log.Printf("Error opening stdout pipe: %s", err)
		return err
	}

	f.StdIn, err = f.Cmd.StdinPipe()
	if err != nil {
		log.Printf("Error opening stdin pipe: %s", err)
		return err
	}

	f.StdErr, err = f.Cmd.StderrPipe()
	if err != nil {
		log.Printf("Error opening stderr pipe: %s", err)
		return err
	}

	go f.parseRunningCommand(f.StdOut)
	go f.parseRunningCommand(f.StdErr)

	err = f.Cmd.Start()
	if err != nil {
		log.Printf("Factorio process failed to start: %s", err)
		return err
	}
	f.Running = true

	err = f.Cmd.Wait()
	if err != nil {
		log.Printf("Factorio process exited with error: %s", err)
		f.Running = false
		return err
	}

	return nil
}

func (f *FactorioServer) parseRunningCommand(std io.ReadCloser) (err error) {
	stdScanner := bufio.NewScanner(std)
	for stdScanner.Scan() {
		log.Printf("Factorio Server: %s", stdScanner.Text())
		line := strings.Fields(stdScanner.Text())
		if line[1] == "Error" {
			log.Printf("Error: %s, %s", line[0], line[1])
		}
	}
	if err := stdScanner.Err(); err != nil {
		log.Printf("Error reading std buffer: %s", err)
		return err
	}
	return nil
}

func (f *FactorioServer) checkLogError(logline string) []string {
	line := strings.Fields(logline)
	log.Println(line)

	return line
}

func (f *FactorioServer) Stop() error {
	// TODO: Find an alternative to os.Kill on Windows. os.Interupt
	// is not implemented. Maps will not be saved.
	if runtime.GOOS == "windows" {
		err := f.Cmd.Process.Signal(os.Kill)
		if err != nil {
			if err.Error() == "os: process already finished" {
				f.Running = false
				return err
			}
			log.Printf("Error sending SIGKILLL to Factorio process: %s", err)
			return err
		}
		f.Running = false
		log.Println("Sent SIGKILL to Factorio process. Factorio forced to exit.")

		return nil
	}

	err := f.Cmd.Process.Signal(os.Interrupt)
	if err != nil {
		if err.Error() == "os: process already finished" {
			f.Running = false
			return err
		}
		log.Printf("Error sending SIGINT to Factorio process: %s", err)
		return err
	}
	f.Running = false
	log.Printf("Sent SIGINT to Factorio process. Factorio shutting down...")

	return nil
}
<commit_msg>add log channel to FactorioServer struct<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"github.com/james4k/rcon"
)

type FactorioServer struct {
	Cmd      *exec.Cmd              `json:"-"`
	Savefile string                 `json:"savefile"`
	Latency  int                    `json:"latency"`
	Port     int                    `json:"port"`
	Running  bool                   `json:"running"`
	StdOut   io.ReadCloser          `json:"-"`
	StdErr   io.ReadCloser          `json:"-"`
	StdIn    io.WriteCloser         `json:"-"`
	Settings map[string]interface{} `json:"-"`
	Rcon     *rcon.RemoteConsole    `json:"-"`
	LogChan  chan []string          `json:"-"`
}

func initFactorio() (f *FactorioServer, err error) {
	f = new(FactorioServer)
	f.Settings = make(map[string]interface{})

	if err = os.MkdirAll(config.FactorioConfigDir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create config directory: %v", err)
	}

	settingsPath := filepath.Join(config.FactorioConfigDir, config.SettingsFile)
	var settings *os.File

	if _, err := os.Stat(settingsPath); os.IsNotExist(err) {
		// copy example settings to supplied settings file, if not exists
		log.Printf("Server settings at %s not found, copying example server settings.\n", settingsPath)

		examplePath := filepath.Join(config.FactorioDir, "data", "server-settings.example.json")

		example, err := os.Open(examplePath)
		if err != nil {
			return nil, fmt.Errorf("failed to open example server settings: %v", err)
		}
		defer example.Close()

		settings, err = os.Create(settingsPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create server settings file: %v", err)
		}
		defer settings.Close()

		_, err = io.Copy(settings, example)
		if err != nil {
			return nil, fmt.Errorf("failed to copy example server settings: %v", err)
		}
	} else {
		// otherwise, open file normally
		settings, err = os.Open(settingsPath)
		if err != nil {
			return nil, fmt.Errorf("failed to open server settings file: %v", err)
		}
		defer settings.Close()
	}

	// before reading reset offset
	if _, err = settings.Seek(0, 0); err != nil {
		return nil, fmt.Errorf("error while seeking in settings file: %v", err)
	}

	if err = json.NewDecoder(settings).Decode(&f.Settings); err != nil {
		return nil, fmt.Errorf("error reading %s: %v", settingsPath, err)
	}

	log.Printf("Loaded Factorio settings from %s\n", settingsPath)

	return
}

func (f *FactorioServer) Run() error {
	var err error

	data, err := json.MarshalIndent(f.Settings, "", "  ")
	if err != nil {
		log.Println("Failed to marshal FactorioServerSettings: ", err)
	} else {
		ioutil.WriteFile(filepath.Join(config.FactorioConfigDir, config.SettingsFile), data, 0644)
	}

	args := []string{
		"--start-server", filepath.Join(config.FactorioSavesDir, f.Savefile),
		"--port", strconv.Itoa(f.Port),
		"--server-settings", filepath.Join(config.FactorioConfigDir, "server-settings.json"),
	}

	log.Println("Starting server with command: ", config.FactorioBinary, args)

	f.Cmd = exec.Command(config.FactorioBinary, args...)

	f.StdOut, err = f.Cmd.StdoutPipe()
	if err != nil {
		log.Printf("Error opening stdout pipe: %s", err)
		return err
	}

	f.StdIn, err = f.Cmd.StdinPipe()
	if err != nil {
		log.Printf("Error opening stdin pipe: %s", err)
		return err
	}

	f.StdErr, err = f.Cmd.StderrPipe()
	if err != nil {
		log.Printf("Error opening stderr pipe: %s", err)
		return err
	}

	go f.parseRunningCommand(f.StdOut)
	go f.parseRunningCommand(f.StdErr)

	err = f.Cmd.Start()
	if err != nil {
		log.Printf("Factorio process failed to start: %s", err)
		return err
	}
	f.Running = true

	err = f.Cmd.Wait()
	if err != nil {
		log.Printf("Factorio process exited with error: %s", err)
		f.Running = false
		return err
	}

	return nil
}

func (f *FactorioServer) parseRunningCommand(std io.ReadCloser) (err error) {
	stdScanner := bufio.NewScanner(std)
	for stdScanner.Scan() {
		log.Printf("Factorio Server: %s", stdScanner.Text())
		line := strings.Fields(stdScanner.Text())
		// Check if Factorio Server reports any errors if so handle it
		if line[1] == "Error" {
			f.checkLogError(line)
		}

		// Send latest console output to log channel
		f.LogChan <- line
	}
	if err := stdScanner.Err(); err != nil {
		log.Printf("Error reading std buffer: %s", err)
		return err
	}
	return nil
}

func (f *FactorioServer) checkLogError(logline string) error {
	line := strings.Fields(logline)
	log.Println(line)

	return nil
}

func (f *FactorioServer) Stop() error {
	// TODO: Find an alternative to os.Kill on Windows. os.Interupt
	// is not implemented. Maps will not be saved.
	if runtime.GOOS == "windows" {
		err := f.Cmd.Process.Signal(os.Kill)
		if err != nil {
			if err.Error() == "os: process already finished" {
				f.Running = false
				return err
			}
			log.Printf("Error sending SIGKILLL to Factorio process: %s", err)
			return err
		}
		f.Running = false
		log.Println("Sent SIGKILL to Factorio process. Factorio forced to exit.")

		return nil
	}

	err := f.Cmd.Process.Signal(os.Interrupt)
	if err != nil {
		if err.Error() == "os: process already finished" {
			f.Running = false
			return err
		}
		log.Printf("Error sending SIGINT to Factorio process: %s", err)
		return err
	}
	f.Running = false
	log.Printf("Sent SIGINT to Factorio process. Factorio shutting down...")

	return nil
}
<|endoftext|>


<commit_before>package vault

import (
	"path"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/concourse/atc/creds"
	vaultapi "github.com/hashicorp/vault/api"
)

type vaultFactory struct {
	vaultClient *vaultapi.Client

	prefix string

	token  string
	tokenL *sync.RWMutex

	loggedIn chan struct{}
}

func NewVaultFactory(logger lager.Logger, client *vaultapi.Client, auth AuthConfig, prefix string) *vaultFactory {
	factory := &vaultFactory{
		vaultClient: client,

		prefix: prefix,

		tokenL:   new(sync.RWMutex),
		loggedIn: make(chan struct{}),
	}

	go factory.authLoop(logger, auth)

	return factory
}

func (factory *vaultFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
	<-factory.loggedIn

	return &Vault{
		VaultClient: factory.clientWith(factory.currentToken()).Logical(),

		PathPrefix:   factory.prefix,
		TeamName:     teamName,
		PipelineName: pipelineName,
	}
}

func (factory *vaultFactory) currentToken() string {
	factory.tokenL.RLock()
	token := factory.token
	factory.tokenL.RUnlock()
	return token
}

func (factory *vaultFactory) authLoop(logger lager.Logger, config AuthConfig) {
	for {
		currentToken := factory.currentToken()

		var token string
		var delay time.Duration
		if currentToken == "" {
			token, delay = factory.login(logger.Session("log-in"), config)
		} else {
			token, delay = factory.renew(logger.Session("renew"), currentToken)
		}

		if token != "" {
			factory.tokenL.Lock()
			factory.token = token
			if currentToken == "" {
				close(factory.loggedIn)
			}
			factory.tokenL.Unlock()
		}

		time.Sleep(delay)
	}
}

func (factory *vaultFactory) login(logger lager.Logger, config AuthConfig) (string, time.Duration) {
	if config.ClientToken != "" {
		return config.ClientToken, 0
	}

	backend := config.Backend

	params := map[string]interface{}{}
	for _, param := range config.Params {
		params[param.Name] = param.Value
	}

	secret, err := factory.vaultClient.Logical().Write(path.Join("auth", backend, "login"), params)
	if err != nil {
		logger.Error("failed", err)
		return "", time.Second
	}

	logger.Info("succeeded", lager.Data{
		"token-accessor": secret.Auth.Accessor,
		"lease-duration": secret.Auth.LeaseDuration,
		"policies":       secret.Auth.Policies,
	})

	return secret.Auth.ClientToken, (time.Duration(secret.Auth.LeaseDuration) * time.Second) / 2
}

func (factory *vaultFactory) renew(logger lager.Logger, token string) (string, time.Duration) {
	secret, err := factory.clientWith(token).Auth().Token().RenewSelf(0)
	if err != nil {
		logger.Error("failed", err)
		return "", time.Second
	}

	logger.Info("succeeded", lager.Data{
		"token-accessor": secret.Auth.Accessor,
		"lease-duration": secret.Auth.LeaseDuration,
		"policies":       secret.Auth.Policies,
	})

	return secret.Auth.ClientToken, (time.Duration(secret.Auth.LeaseDuration) * time.Second) / 2
}

func (factory *vaultFactory) clientWith(token string) *vaultapi.Client {
	clientCopy := *factory.vaultClient
	vaultClient := &clientCopy
	vaultClient.SetToken(token)
	return vaultClient
}
<commit_msg>someone should make a petition for this<commit_after>package vault

import (
	"path"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/concourse/atc/creds"
	vaultapi "github.com/hashicorp/vault/api"
)

type vaultFactory struct {
	vaultClient *vaultapi.Client

	prefix string

	token  string
	tokenL *sync.RWMutex

	loggedIn chan struct{}
}

func NewVaultFactory(logger lager.Logger, client *vaultapi.Client, auth AuthConfig, prefix string) *vaultFactory {
	factory := &vaultFactory{
		vaultClient: client,

		prefix: prefix,

		tokenL:   new(sync.RWMutex),
		loggedIn: make(chan struct{}),
	}

	go factory.authLoop(logger, auth)

	return factory
}

func (factory *vaultFactory) NewVariables(teamName string, pipelineName string) creds.Variables {
	<-factory.loggedIn

	return &Vault{
		VaultClient: factory.clientWith(factory.currentToken()).Logical(),

		PathPrefix:   factory.prefix,
		TeamName:     teamName,
		PipelineName: pipelineName,
	}
}

func (factory *vaultFactory) currentToken() string {
	factory.tokenL.RLock()
	token := factory.token
	factory.tokenL.RUnlock()
	return token
}

func (factory *vaultFactory) authLoop(logger lager.Logger, config AuthConfig) {
	for {
		currentToken := factory.currentToken()

		var token string
		var delay time.Duration
		if currentToken == "" {
			token, delay = factory.login(logger.Session("login"), config)
		} else {
			token, delay = factory.renew(logger.Session("renew"), currentToken)
		}

		if token != "" {
			factory.tokenL.Lock()
			factory.token = token
			if currentToken == "" {
				close(factory.loggedIn)
			}
			factory.tokenL.Unlock()
		}

		time.Sleep(delay)
	}
}

func (factory *vaultFactory) login(logger lager.Logger, config AuthConfig) (string, time.Duration) {
	if config.ClientToken != "" {
		return config.ClientToken, 0
	}

	backend := config.Backend

	params := map[string]interface{}{}
	for _, param := range config.Params {
		params[param.Name] = param.Value
	}

	secret, err := factory.vaultClient.Logical().Write(path.Join("auth", backend, "login"), params)
	if err != nil {
		logger.Error("failed", err)
		return "", time.Second
	}

	logger.Info("succeeded", lager.Data{
		"token-accessor": secret.Auth.Accessor,
		"lease-duration": secret.Auth.LeaseDuration,
		"policies":       secret.Auth.Policies,
	})

	return secret.Auth.ClientToken, (time.Duration(secret.Auth.LeaseDuration) * time.Second) / 2
}

func (factory *vaultFactory) renew(logger lager.Logger, token string) (string, time.Duration) {
	secret, err := factory.clientWith(token).Auth().Token().RenewSelf(0)
	if err != nil {
		logger.Error("failed", err)
		return "", time.Second
	}

	logger.Info("succeeded", lager.Data{
		"token-accessor": secret.Auth.Accessor,
		"lease-duration": secret.Auth.LeaseDuration,
		"policies":       secret.Auth.Policies,
	})

	return secret.Auth.ClientToken, (time.Duration(secret.Auth.LeaseDuration) * time.Second) / 2
}

func (factory *vaultFactory) clientWith(token string) *vaultapi.Client {
	clientCopy := *factory.vaultClient
	vaultClient := &clientCopy
	vaultClient.SetToken(token)
	return vaultClient
}
<|endoftext|>


<commit_before>// Copyright 2014 The Prometheus 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 prometheus

// Collector is the interface implemented by anything that can be used by
// Prometheus to collect metrics. A Collector has to be registered for
// collection. See Registerer.Register.
//
// The stock metrics provided by this package (Gauge, Counter, Summary,
// Histogram, Untyped) are also Collectors (which only ever collect one metric,
// namely itself). An implementer of Collector may, however, collect multiple
// metrics in a coordinated fashion and/or create metrics on the fly. Examples
// for collectors already implemented in this library are the metric vectors
// (i.e. collection of multiple instances of the same Metric but with different
// label values) like GaugeVec or SummaryVec, and the ExpvarCollector.
type Collector interface {
	// Describe sends the super-set of all possible descriptors of metrics
	// collected by this Collector to the provided channel and returns once
	// the last descriptor has been sent. The sent descriptors fulfill the
	// consistency and uniqueness requirements described in the Desc
	// documentation.
	//
	// It is valid if one and the same Collector sends duplicate
	// descriptors. Those duplicates are simply ignored. However, two
	// different Collectors must not send duplicate descriptors.
	//
	// Sending no descriptor at all marks the Collector as “unchecked”,
	// i.e. no checks will be performed at registration time, and the
	// Collector may yield any Metric it sees fit in its Collect method.
	//
	// This method idempotently sends the same descriptors throughout the
	// lifetime of the Collector. It may be called concurrently and
	// therefore must be implemented in a concurrency safe way.
	//
	// If a Collector encounters an error while executing this method, it
	// must send an invalid descriptor (created with NewInvalidDesc) to
	// signal the error to the registry.
	Describe(chan<- *Desc)
	// Collect is called by the Prometheus registry when collecting
	// metrics. The implementation sends each collected metric via the
	// provided channel and returns once the last metric has been sent. The
	// descriptor of each sent metric is one of those returned by Describe
	// (unless the Collector is unchecked, see above). Returned metrics that
	// share the same descriptor must differ in their variable label
	// values.
	//
	// This method may be called concurrently and must therefore be
	// implemented in a concurrency safe way. Blocking occurs at the expense
	// of total performance of rendering all registered metrics. Ideally,
	// Collector implementations support concurrent readers.
	Collect(chan<- Metric)
}

// DescribeByCollect is a helper to implement the Describe method of a custom
// Collector. It collects the metrics from the provided Collector and sends
// their descriptors to the provided channel.
//
// If a Collector collects the same metrics throughout its lifetime, its
// Describe method can simply be implemented as:
//
//   func (c customCollector) Describe(ch chan<- *Desc) {
//   	DescribeByCollect(c, ch)
//   }
//
// However, this will not work if the metrics collected change dynamically over
// the lifetime of the Collector in a way that their combined set of descriptors
// changes as well. The shortcut implementation will then violate the contract
// of the Describe method. If a Collector sometimes collects no metrics at all
// (for example vectors like CounterVec, GaugeVec, etc., which only collect
// metrics after a metric with a fully specified label set has been accessed),
// it might even get registered as an unchecked Collecter (cf. the Register
// method of the Registerer interface). Hence, only use this shortcut
// implementation of Describe if you are certain to fulfill the contract.
//
// The Collector example demonstrates a use of DescribeByCollect.
func DescribeByCollect(c Collector, descs chan<- *Desc) {
	metrics := make(chan Metric)
	go func() {
		c.Collect(metrics)
		close(metrics)
	}()
	for m := range metrics {
		descs <- m.Desc()
	}
}

// selfCollector implements Collector for a single Metric so that the Metric
// collects itself. Add it as an anonymous field to a struct that implements
// Metric, and call init with the Metric itself as an argument.
type selfCollector struct {
	self Metric
}

// init provides the selfCollector with a reference to the metric it is supposed
// to collect. It is usually called within the factory function to create a
// metric. See example.
func (c *selfCollector) init(self Metric) {
	c.self = self
}

// Describe implements Collector.
func (c *selfCollector) Describe(ch chan<- *Desc) {
	ch <- c.self.Desc()
}

// Collect implements Collector.
func (c *selfCollector) Collect(ch chan<- Metric) {
	ch <- c.self
}
<commit_msg>fix typo<commit_after>// Copyright 2014 The Prometheus 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 prometheus

// Collector is the interface implemented by anything that can be used by
// Prometheus to collect metrics. A Collector has to be registered for
// collection. See Registerer.Register.
//
// The stock metrics provided by this package (Gauge, Counter, Summary,
// Histogram, Untyped) are also Collectors (which only ever collect one metric,
// namely itself). An implementer of Collector may, however, collect multiple
// metrics in a coordinated fashion and/or create metrics on the fly. Examples
// for collectors already implemented in this library are the metric vectors
// (i.e. collection of multiple instances of the same Metric but with different
// label values) like GaugeVec or SummaryVec, and the ExpvarCollector.
type Collector interface {
	// Describe sends the super-set of all possible descriptors of metrics
	// collected by this Collector to the provided channel and returns once
	// the last descriptor has been sent. The sent descriptors fulfill the
	// consistency and uniqueness requirements described in the Desc
	// documentation.
	//
	// It is valid if one and the same Collector sends duplicate
	// descriptors. Those duplicates are simply ignored. However, two
	// different Collectors must not send duplicate descriptors.
	//
	// Sending no descriptor at all marks the Collector as “unchecked”,
	// i.e. no checks will be performed at registration time, and the
	// Collector may yield any Metric it sees fit in its Collect method.
	//
	// This method idempotently sends the same descriptors throughout the
	// lifetime of the Collector. It may be called concurrently and
	// therefore must be implemented in a concurrency safe way.
	//
	// If a Collector encounters an error while executing this method, it
	// must send an invalid descriptor (created with NewInvalidDesc) to
	// signal the error to the registry.
	Describe(chan<- *Desc)
	// Collect is called by the Prometheus registry when collecting
	// metrics. The implementation sends each collected metric via the
	// provided channel and returns once the last metric has been sent. The
	// descriptor of each sent metric is one of those returned by Describe
	// (unless the Collector is unchecked, see above). Returned metrics that
	// share the same descriptor must differ in their variable label
	// values.
	//
	// This method may be called concurrently and must therefore be
	// implemented in a concurrency safe way. Blocking occurs at the expense
	// of total performance of rendering all registered metrics. Ideally,
	// Collector implementations support concurrent readers.
	Collect(chan<- Metric)
}

// DescribeByCollect is a helper to implement the Describe method of a custom
// Collector. It collects the metrics from the provided Collector and sends
// their descriptors to the provided channel.
//
// If a Collector collects the same metrics throughout its lifetime, its
// Describe method can simply be implemented as:
//
//   func (c customCollector) Describe(ch chan<- *Desc) {
//   	DescribeByCollect(c, ch)
//   }
//
// However, this will not work if the metrics collected change dynamically over
// the lifetime of the Collector in a way that their combined set of descriptors
// changes as well. The shortcut implementation will then violate the contract
// of the Describe method. If a Collector sometimes collects no metrics at all
// (for example vectors like CounterVec, GaugeVec, etc., which only collect
// metrics after a metric with a fully specified label set has been accessed),
// it might even get registered as an unchecked Collector (cf. the Register
// method of the Registerer interface). Hence, only use this shortcut
// implementation of Describe if you are certain to fulfill the contract.
//
// The Collector example demonstrates a use of DescribeByCollect.
func DescribeByCollect(c Collector, descs chan<- *Desc) {
	metrics := make(chan Metric)
	go func() {
		c.Collect(metrics)
		close(metrics)
	}()
	for m := range metrics {
		descs <- m.Desc()
	}
}

// selfCollector implements Collector for a single Metric so that the Metric
// collects itself. Add it as an anonymous field to a struct that implements
// Metric, and call init with the Metric itself as an argument.
type selfCollector struct {
	self Metric
}

// init provides the selfCollector with a reference to the metric it is supposed
// to collect. It is usually called within the factory function to create a
// metric. See example.
func (c *selfCollector) init(self Metric) {
	c.self = self
}

// Describe implements Collector.
func (c *selfCollector) Describe(ch chan<- *Desc) {
	ch <- c.self.Desc()
}

// Collect implements Collector.
func (c *selfCollector) Collect(ch chan<- Metric) {
	ch <- c.self
}
<|endoftext|>


<commit_before>package neutrino

import (
	"fmt"

	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcwallet/waddrmgr"
	"github.com/lightninglabs/neutrino/headerfs"
)

// mockBlockHeaderStore is an implementation of the BlockHeaderStore backed by
// a simple map.
type mockBlockHeaderStore struct {
	headers map[chainhash.Hash]wire.BlockHeader
}

// A compile-time check to ensure the mockBlockHeaderStore adheres to the
// BlockHeaderStore interface.
var _ headerfs.BlockHeaderStore = (*mockBlockHeaderStore)(nil)

// NewMockBlockHeaderStore returns a version of the BlockHeaderStore that's
// backed by an in-memory map. This instance is meant to be used by callers
// outside the package to unit test components that require a BlockHeaderStore
// interface.
func newMockBlockHeaderStore() headerfs.BlockHeaderStore {
	return &mockBlockHeaderStore{
		headers: make(map[chainhash.Hash]wire.BlockHeader),
	}
}

func (m *mockBlockHeaderStore) ChainTip() (*wire.BlockHeader,
	uint32, error) {
	return nil, 0, nil

}
func (m *mockBlockHeaderStore) LatestBlockLocator() (
	blockchain.BlockLocator, error) {
	return nil, nil
}
func (m *mockBlockHeaderStore) FetchHeaderByHeight(height uint32) (
	*wire.BlockHeader, error) {

	return nil, nil
}
func (m *mockBlockHeaderStore) FetchHeaderAncestors(uint32,
	*chainhash.Hash) ([]wire.BlockHeader, uint32, error) {

	return nil, 0, nil
}
func (m *mockBlockHeaderStore) HeightFromHash(*chainhash.Hash) (uint32, error) {
	return 0, nil

}
func (m *mockBlockHeaderStore) RollbackLastBlock() (*waddrmgr.BlockStamp,
	error) {
	return nil, nil
}

func (m *mockBlockHeaderStore) FetchHeader(h *chainhash.Hash) (
	*wire.BlockHeader, uint32, error) {
	if header, ok := m.headers[*h]; ok {
		return &header, 0, nil
	}
	return nil, 0, fmt.Errorf("not found")
}

func (m *mockBlockHeaderStore) WriteHeaders(headers ...headerfs.BlockHeader) error {
	for _, h := range headers {
		m.headers[h.BlockHash()] = *h.BlockHeader
	}

	return nil
}
<commit_msg>mock_store: implement FetchHeaderByHeight<commit_after>package neutrino

import (
	"fmt"

	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcwallet/waddrmgr"
	"github.com/lightninglabs/neutrino/headerfs"
)

// mockBlockHeaderStore is an implementation of the BlockHeaderStore backed by
// a simple map.
type mockBlockHeaderStore struct {
	headers map[chainhash.Hash]wire.BlockHeader
	heights map[uint32]wire.BlockHeader
}

// A compile-time check to ensure the mockBlockHeaderStore adheres to the
// BlockHeaderStore interface.
var _ headerfs.BlockHeaderStore = (*mockBlockHeaderStore)(nil)

// NewMockBlockHeaderStore returns a version of the BlockHeaderStore that's
// backed by an in-memory map. This instance is meant to be used by callers
// outside the package to unit test components that require a BlockHeaderStore
// interface.
func newMockBlockHeaderStore() *mockBlockHeaderStore {
	return &mockBlockHeaderStore{
		headers: make(map[chainhash.Hash]wire.BlockHeader),
		heights: make(map[uint32]wire.BlockHeader),
	}
}

func (m *mockBlockHeaderStore) ChainTip() (*wire.BlockHeader,
	uint32, error) {
	return nil, 0, nil

}
func (m *mockBlockHeaderStore) LatestBlockLocator() (
	blockchain.BlockLocator, error) {
	return nil, nil
}

func (m *mockBlockHeaderStore) FetchHeaderByHeight(height uint32) (
	*wire.BlockHeader, error) {

	if header, ok := m.heights[height]; ok {
		return &header, nil
	}

	return nil, headerfs.ErrHeightNotFound
}

func (m *mockBlockHeaderStore) FetchHeaderAncestors(uint32,
	*chainhash.Hash) ([]wire.BlockHeader, uint32, error) {

	return nil, 0, nil
}
func (m *mockBlockHeaderStore) HeightFromHash(*chainhash.Hash) (uint32, error) {
	return 0, nil

}
func (m *mockBlockHeaderStore) RollbackLastBlock() (*waddrmgr.BlockStamp,
	error) {
	return nil, nil
}

func (m *mockBlockHeaderStore) FetchHeader(h *chainhash.Hash) (
	*wire.BlockHeader, uint32, error) {
	if header, ok := m.headers[*h]; ok {
		return &header, 0, nil
	}
	return nil, 0, fmt.Errorf("not found")
}

func (m *mockBlockHeaderStore) WriteHeaders(headers ...headerfs.BlockHeader) error {
	for _, h := range headers {
		m.headers[h.BlockHash()] = *h.BlockHeader
	}

	return nil
}
<|endoftext|>