<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cloudsigma

import (
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/Altoros/gosigma"
	"github.com/juju/errors"
	"github.com/juju/juju/agent"
	"github.com/juju/juju/cloudinit/sshinit"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/cloudinit"
	"github.com/juju/juju/juju/arch"
	"github.com/juju/juju/network"
	"github.com/juju/juju/utils/ssh"
	"github.com/juju/juju/worker/localstorage"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	coreCloudinit "github.com/juju/juju/cloudinit"
	"github.com/juju/juju/instance"
)

//
// Imlementation of InstanceBroker: methods for starting and stopping instances.
//

// StartInstance asks for a new instance to be created, associated with
// the provided config in machineConfig. The given config describes the juju
// state for the new instance to connect to. The config MachineNonce, which must be
// unique within an environment, is used by juju to protect against the
// consequences of multiple instances being started with the same machine id.
func (env environ) StartInstance(args environs.StartInstanceParams) (
	instance.Instance, *instance.HardwareCharacteristics, []network.Info, error) {
	logger.Infof("sigmaEnviron.StartInstance...")

	if args.MachineConfig == nil {
		return nil, nil, nil, fmt.Errorf("machine configuration is nil")
	}

	if args.MachineConfig.HasNetworks() {
		return nil, nil, nil, fmt.Errorf("starting instances with networks is not supported yet")
	}

	if len(args.Tools) == 0 {
		return nil, nil, nil, fmt.Errorf("tools not found")
	}

	args.MachineConfig.Tools = args.Tools[0]
	if err := environs.FinishMachineConfig(args.MachineConfig, env.Config(), args.Constraints); err != nil {
		return nil, nil, nil, err
	}

	client := env.client
	server, rootdrive, err := client.newInstance(args)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed start instance: %v", err)
	}

	inst := sigmaInstance{server: server}
	addr := inst.findIPv4()
	if addr == "" {
		return nil, nil, nil, fmt.Errorf("failed obtain instance IP address")
	}

	// prepare new instance: wait up and running, populate nonce file, etc
	if err := env.prepareInstance(addr, args.MachineConfig); err != nil {
		return nil, nil, nil, fmt.Errorf("failed prepare instanse: %v", err)
	}

	// provide additional agent config for localstorage, if any
	if env.storage.tmp && args.MachineConfig.Bootstrap {
		if err := env.prepareStorage(addr, args.MachineConfig); err != nil {
			return nil, nil, nil, fmt.Errorf("failed prepare storage: %v", err)
		}
	}

	// prepare hardware characteristics
	hwch := inst.hardware()

	// populate root drive hardware characteristics
	switch rootdrive.Arch() {
	case "64":
		var a = arch.AMD64
		hwch.Arch = &a
	}

	diskSpace := rootdrive.Size() / gosigma.Megabyte
	if diskSpace > 0 {
		hwch.RootDisk = &diskSpace
	}

	logger.Tracef("hardware: %v", hwch)

	return inst, hwch, nil, nil
}

// AllInstances returns all instances currently known to the broker.
func (env environ) AllInstances() ([]instance.Instance, error) {
	// Please note that this must *not* return instances that have not been
	// allocated as part of this environment -- if it does, juju will see they
	// are not tracked in state, assume they're stale/rogue, and shut them down.

	logger.Tracef("environ.AllInstances...")

	servers, err := env.client.instances()
	if err != nil {
		logger.Tracef("environ.AllInstances failed: %v", err)
		return nil, err
	}

	instances := make([]instance.Instance, 0, len(servers))
	for _, server := range servers {
		instance := sigmaInstance{server: server}
		instances = append(instances, instance)
	}

	if logger.LogLevel() <= loggo.TRACE {
		logger.Tracef("All instances, len = %d:", len(instances))
		for _, instance := range instances {
			logger.Tracef("... id: %q, status: %q", instance.Id(), instance.Status())
		}
	}

	return instances, nil
}

// Instances returns a slice of instances corresponding to the
// given instance ids.  If no instances were found, but there
// was no other error, it will return ErrNoInstances.  If
// some but not all the instances were found, the returned slice
// will have some nil slots, and an ErrPartialInstances error
// will be returned.
func (env environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
	logger.Tracef("environ.Instances %#v", ids)
	// Please note that this must *not* return instances that have not been
	// allocated as part of this environment -- if it does, juju will see they
	// are not tracked in state, assume they're stale/rogue, and shut them down.
	// This advice applies even if an instance id passed in corresponds to a
	// real instance that's not part of the environment -- the Environ should
	// treat that no differently to a request for one that does not exist.

	m, err := env.client.instanceMap()
	if err != nil {
		logger.Tracef("environ.Instances failed: %v", err)
		return nil, err
	}

	var found int
	r := make([]instance.Instance, len(ids))
	for i, id := range ids {
		if s, ok := m[string(id)]; ok {
			r[i] = sigmaInstance{server: s}
			found++
		}
	}

	if found == 0 {
		err = environs.ErrNoInstances
	} else if found != len(ids) {
		err = environs.ErrPartialInstances
	}

	return r, err
}

// StopInstances shuts down the given instances.
func (env environ) StopInstances(instances ...instance.Id) error {
	logger.Infof("stop instances %+v", instances)

	var err error

	for _, instance := range instances {
		if e := env.client.stopInstance(instance); e != nil {
			err = e
		}
	}

	return err
}

func (env environ) prepareInstance(addr string, mcfg *cloudinit.MachineConfig) error {
	host := "ubuntu@" + addr
	logger.Debugf("Running prepare script on %s", host)

	cmds := []string{"#!/bin/bash", "set -e"}
	cmds = append(cmds, fmt.Sprintf("mkdir -p '%s'", mcfg.DataDir))

	nonceFile := path.Join(mcfg.DataDir, cloudinit.NonceFile)
	nonceContent := mcfg.MachineNonce
	cmds = append(cmds, fmt.Sprintf("echo '%s' > %s", nonceContent, nonceFile))

	script := strings.Join(cmds, "\n")

	if !mcfg.Bootstrap {
		cloudcfg := coreCloudinit.New()
		if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
			return err
		}
		configScript, err := sshinit.ConfigureScript(cloudcfg)
		if err != nil {
			return err
		}

		script += "\n\n"
		script += configScript
	}

	logger.Tracef("Script:\n%s", script)

	keyfile := path.Join(mcfg.DataDir, agent.SystemIdentity)
	if _, err := os.Stat(keyfile); err != nil {
		keyfile = ""
	}
	logger.Tracef("System identity file: %q", keyfile)

	var retryAttempts = utils.AttemptStrategy{
		Total: 120 * time.Second,
		Delay: 300 * time.Millisecond,
	}

	var err error
	for attempt := retryAttempts.Start(); attempt.Next(); {
		var options ssh.Options
		if keyfile != "" {
			options.SetIdentities(keyfile)
		}

		cmd := ssh.Command(host, []string{"sudo", "/bin/bash"}, &options)
		cmd.Stdin = strings.NewReader(script)

		var logw = loggerWriter{logger.LogLevel()}
		cmd.Stderr = &logw
		cmd.Stdout = &logw

		if err = cmd.Run(); err == nil {
			break
		}
	}

	if err != nil {
		return fmt.Errorf("failed running prepare script: %v", err)
	}

<<<<<<< HEAD
<<<<<<< HEAD
	logger.Tracef("Bootstrap script done for machine '%s', '%s'", mcfg.MachineId, addr)

=======
>>>>>>> 071fbea... merged implementation of CloudSigma provider from launchpad
=======
>>>>>>> 071fbea... merged implementation of CloudSigma provider from launchpad
	return nil
}

func (env environ) prepareStorage(addr string, mcfg *cloudinit.MachineConfig) error {
	storagePort := env.ecfg.storagePort()
	storageDir := mcfg.DataDir + "/" + storageSubdir

	logger.Debugf("Moving local temporary storage to %s:%d (%s)...", addr, storagePort, storageDir)
	if err := env.storage.MoveToSSH("ubuntu", addr); err != nil {
		return err
	}

	if strings.Contains(mcfg.Tools.URL, "%s") {
		mcfg.Tools.URL = fmt.Sprintf(mcfg.Tools.URL, "file://"+storageDir)
		logger.Tracef("Tools URL patched to %q", mcfg.Tools.URL)
	}

	// prepare configuration for local storage at bootstrap host
	storageConfig := storageConfig{
		ecfg:        env.ecfg,
		storageDir:  storageDir,
		storageAddr: addr,
		storagePort: storagePort,
	}

	agentEnv, err := localstorage.StoreConfig(&storageConfig)
	if err != nil {
		return err
	}

	for k, v := range agentEnv {
		mcfg.AgentEnvironment[k] = v
	}

	return nil
}

// AllocateAddress requests a new address to be allocated for the
// given instance on the given network.
func (env environ) AllocateAddress(instID instance.Id, netID network.Id) (network.Address, error) {
	return network.Address{}, errors.NotSupportedf("AllocateAddress")
}
<<<<<<< HEAD
<<<<<<< HEAD

// ListNetworks returns basic information about all networks known
// by the provider for the environment. They may be unknown to juju
// yet (i.e. when called initially or when a new network was created).
func (env environ) ListNetworks() ([]network.BasicInfo, error) {
	return nil, errors.NotImplementedf("ListNetworks")
}
=======
>>>>>>> 071fbea... merged implementation of CloudSigma provider from launchpad
=======
>>>>>>> 071fbea... merged implementation of CloudSigma provider from launchpad
<commit_msg>Fix conflicts<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cloudsigma

import (
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/Altoros/gosigma"
	"github.com/juju/errors"
	"github.com/juju/juju/agent"
	"github.com/juju/juju/cloudinit/sshinit"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/cloudinit"
	"github.com/juju/juju/juju/arch"
	"github.com/juju/juju/network"
	"github.com/juju/juju/utils/ssh"
	"github.com/juju/juju/worker/localstorage"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	coreCloudinit "github.com/juju/juju/cloudinit"
	"github.com/juju/juju/instance"
)

//
// Imlementation of InstanceBroker: methods for starting and stopping instances.
//

// StartInstance asks for a new instance to be created, associated with
// the provided config in machineConfig. The given config describes the juju
// state for the new instance to connect to. The config MachineNonce, which must be
// unique within an environment, is used by juju to protect against the
// consequences of multiple instances being started with the same machine id.
func (env environ) StartInstance(args environs.StartInstanceParams) (
	instance.Instance, *instance.HardwareCharacteristics, []network.Info, error) {
	logger.Infof("sigmaEnviron.StartInstance...")

	if args.MachineConfig == nil {
		return nil, nil, nil, fmt.Errorf("machine configuration is nil")
	}

	if args.MachineConfig.HasNetworks() {
		return nil, nil, nil, fmt.Errorf("starting instances with networks is not supported yet")
	}

	if len(args.Tools) == 0 {
		return nil, nil, nil, fmt.Errorf("tools not found")
	}

	args.MachineConfig.Tools = args.Tools[0]
	if err := environs.FinishMachineConfig(args.MachineConfig, env.Config(), args.Constraints); err != nil {
		return nil, nil, nil, err
	}

	client := env.client
	server, rootdrive, err := client.newInstance(args)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed start instance: %v", err)
	}

	inst := sigmaInstance{server: server}
	addr := inst.findIPv4()
	if addr == "" {
		return nil, nil, nil, fmt.Errorf("failed obtain instance IP address")
	}

	// prepare new instance: wait up and running, populate nonce file, etc
	if err := env.prepareInstance(addr, args.MachineConfig); err != nil {
		return nil, nil, nil, fmt.Errorf("failed prepare instanse: %v", err)
	}

	// provide additional agent config for localstorage, if any
	if env.storage.tmp && args.MachineConfig.Bootstrap {
		if err := env.prepareStorage(addr, args.MachineConfig); err != nil {
			return nil, nil, nil, fmt.Errorf("failed prepare storage: %v", err)
		}
	}

	// prepare hardware characteristics
	hwch := inst.hardware()

	// populate root drive hardware characteristics
	switch rootdrive.Arch() {
	case "64":
		var a = arch.AMD64
		hwch.Arch = &a
	}

	diskSpace := rootdrive.Size() / gosigma.Megabyte
	if diskSpace > 0 {
		hwch.RootDisk = &diskSpace
	}

	logger.Tracef("hardware: %v", hwch)

	return inst, hwch, nil, nil
}

// AllInstances returns all instances currently known to the broker.
func (env environ) AllInstances() ([]instance.Instance, error) {
	// Please note that this must *not* return instances that have not been
	// allocated as part of this environment -- if it does, juju will see they
	// are not tracked in state, assume they're stale/rogue, and shut them down.

	logger.Tracef("environ.AllInstances...")

	servers, err := env.client.instances()
	if err != nil {
		logger.Tracef("environ.AllInstances failed: %v", err)
		return nil, err
	}

	instances := make([]instance.Instance, 0, len(servers))
	for _, server := range servers {
		instance := sigmaInstance{server: server}
		instances = append(instances, instance)
	}

	if logger.LogLevel() <= loggo.TRACE {
		logger.Tracef("All instances, len = %d:", len(instances))
		for _, instance := range instances {
			logger.Tracef("... id: %q, status: %q", instance.Id(), instance.Status())
		}
	}

	return instances, nil
}

// Instances returns a slice of instances corresponding to the
// given instance ids.  If no instances were found, but there
// was no other error, it will return ErrNoInstances.  If
// some but not all the instances were found, the returned slice
// will have some nil slots, and an ErrPartialInstances error
// will be returned.
func (env environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
	logger.Tracef("environ.Instances %#v", ids)
	// Please note that this must *not* return instances that have not been
	// allocated as part of this environment -- if it does, juju will see they
	// are not tracked in state, assume they're stale/rogue, and shut them down.
	// This advice applies even if an instance id passed in corresponds to a
	// real instance that's not part of the environment -- the Environ should
	// treat that no differently to a request for one that does not exist.

	m, err := env.client.instanceMap()
	if err != nil {
		logger.Tracef("environ.Instances failed: %v", err)
		return nil, err
	}

	var found int
	r := make([]instance.Instance, len(ids))
	for i, id := range ids {
		if s, ok := m[string(id)]; ok {
			r[i] = sigmaInstance{server: s}
			found++
		}
	}

	if found == 0 {
		err = environs.ErrNoInstances
	} else if found != len(ids) {
		err = environs.ErrPartialInstances
	}

	return r, err
}

// StopInstances shuts down the given instances.
func (env environ) StopInstances(instances ...instance.Id) error {
	logger.Infof("stop instances %+v", instances)

	var err error

	for _, instance := range instances {
		if e := env.client.stopInstance(instance); e != nil {
			err = e
		}
	}

	return err
}

func (env environ) prepareInstance(addr string, mcfg *cloudinit.MachineConfig) error {
	host := "ubuntu@" + addr
	logger.Debugf("Running prepare script on %s", host)

	cmds := []string{"#!/bin/bash", "set -e"}
	cmds = append(cmds, fmt.Sprintf("mkdir -p '%s'", mcfg.DataDir))

	nonceFile := path.Join(mcfg.DataDir, cloudinit.NonceFile)
	nonceContent := mcfg.MachineNonce
	cmds = append(cmds, fmt.Sprintf("echo '%s' > %s", nonceContent, nonceFile))

	script := strings.Join(cmds, "\n")

	if !mcfg.Bootstrap {
		cloudcfg := coreCloudinit.New()
		if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil {
			return err
		}
		configScript, err := sshinit.ConfigureScript(cloudcfg)
		if err != nil {
			return err
		}

		script += "\n\n"
		script += configScript
	}

	logger.Tracef("Script:\n%s", script)

	keyfile := path.Join(mcfg.DataDir, agent.SystemIdentity)
	if _, err := os.Stat(keyfile); err != nil {
		keyfile = ""
	}
	logger.Tracef("System identity file: %q", keyfile)

	var retryAttempts = utils.AttemptStrategy{
		Total: 120 * time.Second,
		Delay: 300 * time.Millisecond,
	}

	var err error
	for attempt := retryAttempts.Start(); attempt.Next(); {
		var options ssh.Options
		if keyfile != "" {
			options.SetIdentities(keyfile)
		}

		cmd := ssh.Command(host, []string{"sudo", "/bin/bash"}, &options)
		cmd.Stdin = strings.NewReader(script)

		var logw = loggerWriter{logger.LogLevel()}
		cmd.Stderr = &logw
		cmd.Stdout = &logw

		if err = cmd.Run(); err == nil {
			break
		}
	}

	if err != nil {
		return fmt.Errorf("failed running prepare script: %v", err)
	}

	logger.Tracef("Bootstrap script done for machine '%s', '%s'", mcfg.MachineId, addr)

	return nil
}

func (env environ) prepareStorage(addr string, mcfg *cloudinit.MachineConfig) error {
	storagePort := env.ecfg.storagePort()
	storageDir := mcfg.DataDir + "/" + storageSubdir

	logger.Debugf("Moving local temporary storage to %s:%d (%s)...", addr, storagePort, storageDir)
	if err := env.storage.MoveToSSH("ubuntu", addr); err != nil {
		return err
	}

	if strings.Contains(mcfg.Tools.URL, "%s") {
		mcfg.Tools.URL = fmt.Sprintf(mcfg.Tools.URL, "file://"+storageDir)
		logger.Tracef("Tools URL patched to %q", mcfg.Tools.URL)
	}

	// prepare configuration for local storage at bootstrap host
	storageConfig := storageConfig{
		ecfg:        env.ecfg,
		storageDir:  storageDir,
		storageAddr: addr,
		storagePort: storagePort,
	}

	agentEnv, err := localstorage.StoreConfig(&storageConfig)
	if err != nil {
		return err
	}

	for k, v := range agentEnv {
		mcfg.AgentEnvironment[k] = v
	}

	return nil
}

// AllocateAddress requests a new address to be allocated for the
// given instance on the given network.
func (env environ) AllocateAddress(instID instance.Id, netID network.Id) (network.Address, error) {
	return network.Address{}, errors.NotSupportedf("AllocateAddress")
}

// ListNetworks returns basic information about all networks known
// by the provider for the environment. They may be unknown to juju
// yet (i.e. when called initially or when a new network was created).
func (env environ) ListNetworks() ([]network.BasicInfo, error) {
	return nil, errors.NotImplementedf("ListNetworks")
}
<|endoftext|>


<commit_before>// +build json

package socks_test

import (
	"testing"

	"v2ray.com/core/proxy/registry"
	. "v2ray.com/core/proxy/socks"
	"v2ray.com/core/testing/assert"
)

func TestDefaultIPAddress(t *testing.T) {
	assert := assert.On(t)

	socksConfig, err := registry.CreateInboundConfig("socks", []byte(`{
    "auth": "noauth"
  }`))
	assert.Error(err).IsNil()
	assert.Address(socksConfig.(*ServerConfig).Address.AsAddress()).EqualsString("127.0.0.1")
}
<commit_msg>fix test break<commit_after>// +build json

package socks_test

import (
	"testing"

	"v2ray.com/core/proxy/registry"
	. "v2ray.com/core/proxy/socks"
	"v2ray.com/core/testing/assert"
)

func TestDefaultIPAddress(t *testing.T) {
	assert := assert.On(t)

	socksConfig, err := registry.CreateInboundConfig("socks", []byte(`{
    "auth": "noauth"
  }`))
	assert.Error(err).IsNil()
	assert.Address(socksConfig.(*ServerConfig).GetNetAddress()).EqualsString("127.0.0.1")
}
<|endoftext|>


<commit_before>package conf

import (
	"fmt"
	"github.com/yassu/gnuplot.go/utils"
	"regexp"
	"strconv"
)

func isNum(s string) bool {
	r := regexp.MustCompile(`^[+-]?[0-9]*[\.]?[0-9]+$`)
	return r.MatchString(s)
}

func isIntStr(s string) bool {
	r := regexp.MustCompile(`^[+-]?[0-9]+$`)
	return r.MatchString(s)
}

func isSixHex(s string) bool {
	r := regexp.MustCompile(`^[0-9a-f]{6}$`)
	return r.MatchString(s)
}

func isEightHex(s string) bool {
	r := regexp.MustCompile(`^[0-9a-f]{8}$`)
	return r.MatchString(s)
}

// either s is float of 0 ~ 1 or not
func isSmallFloat(s string) bool {
	if !isNum(s) {
		return false
	}
	f, _ := strconv.ParseFloat(s, 32)
	return 0 <= f && f <= 1
}

// Configures
type Configure struct {
	key               string
	aliasKeys         []string
	vals              []string
	requiredCondition func(vals []string) bool
}

func NewConfigure(keys []string, defaultVals []string, requiredCondition func(vals []string) bool) *Configure {
	conf := new(Configure)
	conf.key = keys[0]
	conf.aliasKeys = keys
	conf.vals = defaultVals
	conf.requiredCondition = requiredCondition
	return conf
}

func (conf *Configure) SetVals(vals []string) {
	if conf.requiredCondition(vals) {
		conf.vals = vals
	} else {
		panic(fmt.Sprintf("%v is illegal values of %v.", vals, conf.key))
	}
}

func (conf *Configure) GetKey() string {
	return conf.key
}

func (conf *Configure) GetVals() []string {
	return conf.vals
}

func (conf *Configure) AliasedKeys() []string {
	return conf.aliasKeys
}

// Function2d or Curve2d options
func WithConf() *Configure {
	return NewConfigure([]string{"with", "w"}, []string{"lines"}, func(vals []string) bool {
		return len(vals) == 1 && utils.InStr(vals[0], []string{
			"lines", "dots", "steps", "errorbars", "xerrorbar",
			"xyerrorlines", "points", "impulses", "fsteps", "errorlines", "xerrorlines",
			"yerrorlines", "surface", "vectors", "parallelaxes"})
	})
}

var COLOR_NAMES = []string{
	"white", "black", "dark-grey", "red", "web-green", "web-blue",
	"dark-magenta", "dark-cyan", "dark-orange", "dark-yellow", "royalblue",
	"goldenrod", "dark-spring-green", "purple", "steelblue", "dark-red",
	"dark-chartreuse", "orchild", "aquamarine", "brown", "yellow",
	"turquoise",
	"grey", "grey0", "grey10", "grey20", "grey30", "grey40", "grey50",
	"grey60", "grey70", "grey80", "grey90", "grey100",
	"light-red", "light-green", "light-blue", "light-magenta", "light-cyan",
	"light-goldenrod", "light-pink", "light-turquoise", "gold", "green",
	"dark-green", "sprint-green", "forest-green", "sea-green", "blue",
	"dark-blue", "midnight-blue", "navy", "medium-blue", "skyblue",
	"cyan", "magenta", "dark-turquoise", "dark-pink", "coral", "light-coral",
	"orange-red", "salmon", "dark-salmon", "khaki", "dark-khaki",
	"dark-goldenrod", "beige", "olive", "orange", "violet", "dark-violet",
	"plum", "dark-plum", "dark-olivegreen", "orangered4", "brown4", "sienna4",
	"orchid4", "mediumpurple3", "slateblue1", "yellow4", "sienna1", "tan1",
	"standybrown", "light-salmon", "pink", "khaki1", "lemonchiffon", "bisque",
	"honeydew", "slategrey", "seagreen", "antiquewhite", "chartreuse",
	"greenyellow", "gray", "light-gray", "light-grey",
	"dark-gray", "slategray",
	"gray0", "gray10", "gray20", "gray30", "gray40", "gray50", "gray60",
	"gray70", "gray80", "gray90", "gray100"}

func LineColorConf() *Configure {
	return NewConfigure([]string{"linecolor", "lc"}, []string{"1"}, func(vals []string) bool {
		if len(vals) == 0 {
			return false
		}
		// in case of linecolor "colorname"
		val := vals[0]
		if utils.InStr(val, COLOR_NAMES) {
			return true
		}

		// in case of linecolor <n>
		if isIntStr(val) {
			return true
		}

		// in case of linecolor <colorspec> and len(vals) == 1
		if utils.InStr(val, []string{"variable", "bgnd", "black"}) {
			return true
		}

		if len(vals) == 2 && vals[0] == "rgbcolor" && utils.InStr(vals[1], COLOR_NAMES) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0:2] == "0x" && isSixHex(vals[1][2:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0:2] == "0x" && isEightHex(vals[1][2:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0] == '#' && isSixHex(vals[1][1:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0] == '#' && isEightHex(vals[1][1:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && isIntStr(vals[1]) {
			return true
		}
		return false
	})
}

func GoXMinConf() *Configure {
	return NewConfigure([]string{"_xMin"}, []string{"-10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func GoXMaxConf() *Configure {
	return NewConfigure([]string{"_xMax"}, []string{"10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func GoTMinConf() *Configure {
	return NewConfigure([]string{"_tMin"}, []string{"-10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func GoTMaxConf() *Configure {
	return NewConfigure([]string{"_tMax"}, []string{"10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func Function2dConfs() []*Configure {
	return []*Configure{WithConf(), GoXMinConf(), GoXMaxConf()}
}

func Curve2dConfs() []*Configure {
	return []*Configure{WithConf(), GoTMinConf(), GoTMaxConf()}
}

// Graph options
func AnglesConf() *Configure {
	return NewConfigure([]string{"angles"}, []string{"radians"}, func(vals []string) bool {
		return len(vals) == 1 && utils.InStr(vals[0], []string{"degrees", "radians", "true", "false"})
	})
}

func Graph2dConfs() []*Configure {
	return []*Configure{AnglesConf()}
}
<commit_msg>arrow conf without validation<commit_after>package conf

import (
	"fmt"
	"github.com/yassu/gnuplot.go/utils"
	"regexp"
	"strconv"
)

func isNum(s string) bool {
	r := regexp.MustCompile(`^[+-]?[0-9]*[\.]?[0-9]+$`)
	return r.MatchString(s)
}

func isIntStr(s string) bool {
	r := regexp.MustCompile(`^[+-]?[0-9]+$`)
	return r.MatchString(s)
}

func isSixHex(s string) bool {
	r := regexp.MustCompile(`^[0-9a-f]{6}$`)
	return r.MatchString(s)
}

func isEightHex(s string) bool {
	r := regexp.MustCompile(`^[0-9a-f]{8}$`)
	return r.MatchString(s)
}

// either s is float of 0 ~ 1 or not
func isSmallFloat(s string) bool {
	if !isNum(s) {
		return false
	}
	f, _ := strconv.ParseFloat(s, 32)
	return 0 <= f && f <= 1
}

// Configures
type Configure struct {
	key               string
	aliasKeys         []string
	vals              []string
	requiredCondition func(vals []string) bool
}

func NewConfigure(keys []string, defaultVals []string, requiredCondition func(vals []string) bool) *Configure {
	conf := new(Configure)
	conf.key = keys[0]
	conf.aliasKeys = keys
	conf.vals = defaultVals
	conf.requiredCondition = requiredCondition
	return conf
}

func (conf *Configure) SetVals(vals []string) {
	if conf.requiredCondition(vals) {
		conf.vals = vals
	} else {
		panic(fmt.Sprintf("%v is illegal values of %v.", vals, conf.key))
	}
}

func (conf *Configure) GetKey() string {
	return conf.key
}

func (conf *Configure) GetVals() []string {
	return conf.vals
}

func (conf *Configure) AliasedKeys() []string {
	return conf.aliasKeys
}

var COLOR_NAMES = []string{
	"white", "black", "dark-grey", "red", "web-green", "web-blue",
	"dark-magenta", "dark-cyan", "dark-orange", "dark-yellow", "royalblue",
	"goldenrod", "dark-spring-green", "purple", "steelblue", "dark-red",
	"dark-chartreuse", "orchild", "aquamarine", "brown", "yellow",
	"turquoise",
	"grey", "grey0", "grey10", "grey20", "grey30", "grey40", "grey50",
	"grey60", "grey70", "grey80", "grey90", "grey100",
	"light-red", "light-green", "light-blue", "light-magenta", "light-cyan",
	"light-goldenrod", "light-pink", "light-turquoise", "gold", "green",
	"dark-green", "sprint-green", "forest-green", "sea-green", "blue",
	"dark-blue", "midnight-blue", "navy", "medium-blue", "skyblue",
	"cyan", "magenta", "dark-turquoise", "dark-pink", "coral", "light-coral",
	"orange-red", "salmon", "dark-salmon", "khaki", "dark-khaki",
	"dark-goldenrod", "beige", "olive", "orange", "violet", "dark-violet",
	"plum", "dark-plum", "dark-olivegreen", "orangered4", "brown4", "sienna4",
	"orchid4", "mediumpurple3", "slateblue1", "yellow4", "sienna1", "tan1",
	"standybrown", "light-salmon", "pink", "khaki1", "lemonchiffon", "bisque",
	"honeydew", "slategrey", "seagreen", "antiquewhite", "chartreuse",
	"greenyellow", "gray", "light-gray", "light-grey",
	"dark-gray", "slategray",
	"gray0", "gray10", "gray20", "gray30", "gray40", "gray50", "gray60",
	"gray70", "gray80", "gray90", "gray100"}

var POSITIONS = []string{"x", "y", "first", "second", "graph", "screen", "character"}

// Function2d or Curve2d options
func WithConf() *Configure {
	return NewConfigure([]string{"with", "w"}, []string{"lines"}, func(vals []string) bool {
		return len(vals) == 1 && utils.InStr(vals[0], []string{
			"lines", "dots", "steps", "errorbars", "xerrorbar",
			"xyerrorlines", "points", "impulses", "fsteps", "errorlines", "xerrorlines",
			"yerrorlines", "surface", "vectors", "parallelaxes"})
	})
}

func LineColorConf() *Configure {
	return NewConfigure([]string{"linecolor", "lc"}, []string{"1"}, func(vals []string) bool {
		if len(vals) == 0 {
			return false
		}
		// in case of linecolor "colorname"
		val := vals[0]
		if utils.InStr(val, COLOR_NAMES) {
			return true
		}

		// in case of linecolor <n>
		if isIntStr(val) {
			return true
		}

		// in case of linecolor <colorspec> and len(vals) == 1
		if utils.InStr(val, []string{"variable", "bgnd", "black"}) {
			return true
		}

		if len(vals) == 2 && vals[0] == "rgbcolor" && utils.InStr(vals[1], COLOR_NAMES) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0:2] == "0x" && isSixHex(vals[1][2:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0:2] == "0x" && isEightHex(vals[1][2:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0] == '#' && isSixHex(vals[1][1:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && vals[1][0] == '#' && isEightHex(vals[1][1:]) {
			return true
		}
		if len(vals) == 2 && vals[0] == "rgbcolor" && isIntStr(vals[1]) {
			return true
		}
		return false
	})
}

func ArrowConf() *Configure {
	return NewConfigure([]string{"arrow"}, []string{}, func(vals []string) bool {
		if len(vals) == 0 {
			return true
		}

		// set arrow <tag> from <position> to <position>
		if len(vals) == 4 && isNum(vals[0]) &&
			vals[1] == "from" && vals[3] == "to" &&
			utils.InStr(vals[2], POSITIONS) &&
			utils.InStr(vals[4], POSITIONS) {
			return true
		}

		// set arrow from <position> to <position>
		if len(vals) == 3 &&
			vals[0] == "from" && vals[2] == "to" &&
			utils.InStr(vals[1], POSITIONS) &&
			utils.InStr(vals[3], POSITIONS) {
			return true
		}

		// set arrow <tag> from <position> rto <position>
		if len(vals) == 4 && isNum(vals[0]) &&
			vals[1] == "from" && vals[3] == "rto" &&
			utils.InStr(vals[2], POSITIONS) &&
			utils.InStr(vals[4], POSITIONS) {
			return true
		}

		// set arrow from <position> rto <position>
		if len(vals) == 3 &&
			vals[0] == "from" && vals[2] == "rto" &&
			utils.InStr(vals[1], POSITIONS) &&
			utils.InStr(vals[3], POSITIONS) {
			return true
		}

		// set arrow <tag> from <position> length <coord> angle <ang> (until)
		if len(vals) == 7 &&
			vals[1] == "from" && vals[3] == "length" && vals[5] == "angle" &&
			isIntStr(vals[0]) {
			return true
		}
		// TODO: until

		return false
	})
}

func GoXMinConf() *Configure {
	return NewConfigure([]string{"_xMin"}, []string{"-10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func GoXMaxConf() *Configure {
	return NewConfigure([]string{"_xMax"}, []string{"10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func GoTMinConf() *Configure {
	return NewConfigure([]string{"_tMin"}, []string{"-10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func GoTMaxConf() *Configure {
	return NewConfigure([]string{"_tMax"}, []string{"10.0"}, func(vals []string) bool {
		return len(vals) == 1 && isNum(vals[0])
	})
}

func Function2dConfs() []*Configure {
	return []*Configure{WithConf(), GoXMinConf(), GoXMaxConf()}
}

func Curve2dConfs() []*Configure {
	return []*Configure{WithConf(), GoTMinConf(), GoTMaxConf()}
}

// Graph options
func AnglesConf() *Configure {
	return NewConfigure([]string{"angles"}, []string{"radians"}, func(vals []string) bool {
		return len(vals) == 1 && utils.InStr(vals[0], []string{"degrees", "radians", "true", "false"})
	})
}

func Graph2dConfs() []*Configure {
	return []*Configure{AnglesConf()}
}
<|endoftext|>


<commit_before><commit_msg>cmd/godoc: when redirecting don't clear query string so that http://golang.org/pkg/runtime?m=all works.<commit_after><|endoftext|>


<commit_before>package device

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
	"unicode"
	"unicode/utf8"

	"github.com/gorilla/websocket"
	"github.com/jochenvg/go-udev"
	"github.com/tarm/serial"
)

var modemCommands = struct {
	IMEI, IMSI string
}{
	"AT+GSN", "AT+CIMI",
}
var upgrader = websocket.Upgrader{}

type message struct {
	Type string                 `json:"type"`
	Data map[string]interface{} `json:"data"`
}

// Manager manages devices that are plugged into the system. It supports auto
// detection of devices.
//
// Serial ports are opened each for a device, and a clean API for communicating
// is provided via Read, Write and Flush methods.
//
// The devices are monitored via udev, and any changes that requires reloading
// of the  ports are handled by reloading the ports to the devices.
//
// This is safe to use concurrently in multiple goroutines
type Manager struct {
	devices map[string]serial.Config
	modems  map[string]*Modem
	mu      sync.RWMutex
	monitor *udev.Monitor
	done    chan struct{}
	stop    chan struct{}
	events  chan struct{}
}

// New returns a new Manager instance
func New() *Manager {
	return &Manager{
		devices: make(map[string]serial.Config),
		modems:  make(map[string]*Modem),
		done:    make(chan struct{}),
		stop:    make(chan struct{}),
	}
}

// Init initializes the manager. This involves creating a new goroutine to watch
// over the changes detected by udev for any device interaction with the system.
//
// The only interesting device actions are add and reomove for adding and
// removing devices respctively.
func (m *Manager) Init() {
	u := udev.Udev{}
	monitor := u.NewMonitorFromNetlink("udev")
	monitor.FilterAddMatchTag("systemd")
	devCh, err := monitor.DeviceChan(m.done)
	if err != nil {
		panic(err)
	}
	m.monitor = monitor
	go func() {
	stop:
		for {
			select {
			case d := <-devCh:
				dpath := filepath.Join("/dev", filepath.Base(d.Devpath()))
				switch d.Action() {
				case "add":
					fmt.Println(" add ", dpath)
					m.AddDevice(d)
					fmt.Println(" done adding ", dpath)
				case "remove":
					m.RemoveDevice(dpath)
				}
			case quit := <-m.stop:
				m.done <- quit
				break stop
			}
		}
	}()

}

// AddDevice adds device name to the manager
//
// WARNING: The way modems are picked is a hack. It asserts that the modem with
// the lowest tty number is the control modem( Which I'm not so sure is always
// correct).
//
// TODO: comeup with a proper way to identify modems
func (m *Manager) AddDevice(d *udev.Device) error {
	err := m.addDevice(d)
	if err != nil {
		return err
	}
	return m.Symlink()
}
func (m *Manager) addDevice(d *udev.Device) error {
	name := filepath.Join("/dev", filepath.Base(d.Devpath()))
	cfg := serial.Config{Name: name, Baud: 9600, ReadTimeout: 10 * time.Second}
	conn := &Conn{device: cfg}
	if strings.Contains(name, "ttyUSB") {
		fmt.Println("checking modem")
		modem, err := newModem(conn)
		if err != nil {
			return err
		}
		if mm, ok := m.getModem(modem.IMEI); ok {
			n1, err := getttyNum(mm.Path)
			if err != nil {
				return err
			}
			n2, err := getttyNum(modem.Path)
			if err != nil {
				return err
			}
			if n1 > n2 {
				m.setModem(modem)
			}
			return nil
		}
		m.setModem(modem)
	}
	return nil
}

func (m *Manager) Symlink() error {
	for _, v := range m.modems {
		err := v.Symlink()
		if err != nil {
			fmt.Printf(" ERROR %v\n", err)
			continue
		}
	}
	return nil
}

func (m *Manager) setModem(mod *Modem) {
	m.mu.Lock()
	m.modems[mod.IMEI] = mod
	m.mu.Unlock()
}

func (m *Manager) getModem(imei string) (*Modem, bool) {
	m.mu.RLock()
	mod, ok := m.modems[imei]
	m.mu.RUnlock()
	return mod, ok
}

func getttyNum(tty string) (int, error) {
	b := filepath.Base(tty)
	b = strings.TrimPrefix(b, "ttyUSB")
	return strconv.Atoi(b)
}

// List serves the list of current devices. The list wont cover all devices ,
// only the significant ones( modems for now)
func (m *Manager) List(w http.ResponseWriter, r *http.Request) {
	m.mu.RLock()
	json.NewEncoder(w).Encode(m.modems)
	m.mu.RUnlock()
	w.Header().Set("Content-Type", "application/json")
}

// RunComand runs commands to the exposed devices over serial ports
func (m *Manager) RunCommand(w http.ResponseWriter, r *http.Request) {
}

// RemoveDevice removes device name from the manager
func (m *Manager) RemoveDevice(name string) error {
	return nil
}

type Modem struct {
	IMEI         string `json:"imei"`
	IMSI         string `json:"imsi"`
	Manufacturer string `json:"manufacturer"`
	Path         string `json:"tty"`
	conn         *Conn
}

// Symlink adds symlink to the  modem. The symlink links the tty to the IMEI
// number of the modem.
func (m *Modem) Symlink() error {
	newLink := (fmt.Sprintf("/dev/%s", m.IMEI))
	err := syscall.Unlink(newLink)
	if err != nil {
		fmt.Printf(" ERROR %v \n", err)
	}
	return os.Symlink(m.Path, newLink)
}

func newModem(c *Conn) (*Modem, error) {
	m := &Modem{}
	ich := time.After(10 * time.Second)
STOP:
	for {
		select {
		case <-ich:
			break STOP
		default:
			imei, err := c.Run(modemCommands.IMEI)
			if err != nil {
				continue
			}
			i, err := cleanResult(imei)
			if err != nil {
				continue
			}
			im := string(i)
			if !isNumber(im) {
				continue
			}
			m.IMEI = im
			break STOP
		}
	}
	if m.IMEI == "" {
		return nil, errors.New("no imei")
	}
	// we make sure we obtain the sim card information.
	var wg sync.WaitGroup
	done := time.After(20 * time.Second)
	wg.Add(1)
	go func() {
		defer wg.Done()
	END:
		for {
			select {
			case <-done:
				fmt.Println("Timed out")
				break END
			default:
				imsi, err := c.Run("AT+CIMI")
				if err != nil {
					continue
				}
				s, err := cleanResult(imsi)
				if err != nil {
					continue
				}
				ss := string(s)
				if !isNumber(ss) {
					continue
				}
				if m.IMEI == ss {
					continue
				}
				fmt.Println(string(s))
				m.IMSI = ss
				break END
			}
		}
	}()
	wg.Wait()
	if m.IMSI == "" {
		return nil, errors.New(" can't find IMSI")
	}
	m.conn = c
	m.Path = c.device.Name
	return m, nil
}

func isNumber(src string) bool {
	for _, v := range src {
		if !unicode.IsDigit(v) {
			return false
		}
	}
	return true
}

func (m *Manager) reload() {
	for _, v := range m.devices {
		conn := &Conn{device: v}
		modem, err := newModem(conn)
		if err != nil {
			continue
		}
		fmt.Println(*modem)
		m.modems[modem.IMEI] = modem
	}
}

func cleanResult(src []byte) ([]byte, error) {
	i := bytes.Index(src, []byte("OK"))
	if i == -1 {
		return nil, errors.New("not okay")
	}
	ns := bytes.TrimSpace(src[:i])
	ch, _ := utf8.DecodeRune(ns)
	if unicode.IsLetter(ch) {
		at := bytes.Index(ns, []byte("AT"))
		if at != -1 {
			i := bytes.IndexRune(ns[at:], '\r')
			if i > 0 {
				return bytes.TrimSpace(ns[at+i:]), nil
			}
		}
	}
	return ns, nil
}

//Close shuts down the device manager. This makes sure the udev monitor is
//closed and all goroutines are properly exited.
func (m *Manager) Close() {
	m.stop <- struct{}{}
}

func reader(ws *websocket.Conn) {
	defer ws.Close()
	for {
		_, _, err := ws.ReadMessage()
		if err != nil {
			log.Println(err)
		}
	}
}

// Updates is websocket http handler for sending updates about the devices
// plugged into the system in real time.
func (m *Manager) Updates(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		if _, ok := err.(websocket.HandshakeError); !ok {
			log.Println(err)
		}
		return
	}
	go func() {
		for {
			select {
			case ev := <-m.events:
				_ = ws.WriteJSON(ev)
			}
		}
	}()
	reader(ws)
}

// Conn is a device serial connection
type Conn struct {
	device serial.Config
	imei   string
	port   *serial.Port
	isOpen bool
}

// Open opens a serial port to the undelying device
func (c *Conn) Open() error {
	p, err := serial.OpenPort(&c.device)
	if err != nil {
		return err
	}
	c.port = p
	c.isOpen = true
	return nil
}

// Close closes the port helt by *Conn.
func (c *Conn) Close() error {
	if c.isOpen {
		return c.port.Close()
	}
	return nil
}

// Write wites b to the serieal port
func (c *Conn) Write(b []byte) (int, error) {
	return c.port.Write(b)
}

// Read reads from serial port
func (c *Conn) Read(b []byte) (int, error) {
	return c.port.Read(b)
}

// Exec sends the command over serial port and rrturns the response. If the port
// is closed it is opened  before sending the command.
func (c *Conn) Exec(cmd string) ([]byte, error) {
	if !c.isOpen {
		err := c.Open()
		if err != nil {
			return nil, err
		}
	}
	_, err := c.Write([]byte(cmd))
	if err != nil {
		return nil, err
	}
	buf := make([]byte, 128)
	_, err = c.Read(buf)
	if err != nil {
		return nil, err
	}
	if !bytes.Contains(buf, []byte("OK")) {
		return nil, errors.New("command " + string(cmd) + " xeite without OK" + " got " + string(buf))
	}
	_ = c.port.Flush()
	_ = c.port.Close()
	c.isOpen = false
	return buf, nil
}

// Run helper for Exec that adds \r to the command
func (c *Conn) Run(cmd string) ([]byte, error) {
	return c.Exec(fmt.Sprintf("%s \r", cmd))
}
<commit_msg>Send add_dongle event over websocket<commit_after>package device

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
	"unicode"
	"unicode/utf8"

	"github.com/gorilla/websocket"
	"github.com/jochenvg/go-udev"
	"github.com/tarm/serial"
)

var modemCommands = struct {
	IMEI, IMSI string
}{
	"AT+GSN", "AT+CIMI",
}
var upgrader = websocket.Upgrader{}

type message struct {
	Type string                 `json:"type"`
	Data map[string]interface{} `json:"data"`
}

// Manager manages devices that are plugged into the system. It supports auto
// detection of devices.
//
// Serial ports are opened each for a device, and a clean API for communicating
// is provided via Read, Write and Flush methods.
//
// The devices are monitored via udev, and any changes that requires reloading
// of the  ports are handled by reloading the ports to the devices.
//
// This is safe to use concurrently in multiple goroutines
type Manager struct {
	devices map[string]serial.Config
	modems  map[string]*Modem
	mu      sync.RWMutex
	monitor *udev.Monitor
	done    chan struct{}
	stop    chan struct{}
	events  chan *message
}

// New returns a new Manager instance
func New() *Manager {
	return &Manager{
		devices: make(map[string]serial.Config),
		modems:  make(map[string]*Modem),
		done:    make(chan struct{}),
		stop:    make(chan struct{}),
		events:  make(chan *message),
	}
}

// Init initializes the manager. This involves creating a new goroutine to watch
// over the changes detected by udev for any device interaction with the system.
//
// The only interesting device actions are add and reomove for adding and
// removing devices respctively.
func (m *Manager) Init() {
	u := udev.Udev{}
	monitor := u.NewMonitorFromNetlink("udev")
	monitor.FilterAddMatchTag("systemd")
	devCh, err := monitor.DeviceChan(m.done)
	if err != nil {
		panic(err)
	}
	m.monitor = monitor
	go func() {
	stop:
		for {
			select {
			case d := <-devCh:
				dpath := filepath.Join("/dev", filepath.Base(d.Devpath()))
				switch d.Action() {
				case "add":
					fmt.Println(" add ", dpath)
					m.AddDevice(d)
					fmt.Println(" done adding ", dpath)
				case "remove":
					m.RemoveDevice(dpath)
				}
			case quit := <-m.stop:
				m.done <- quit
				break stop
			}
		}
	}()

}

// AddDevice adds device name to the manager
//
// WARNING: The way modems are picked is a hack. It asserts that the modem with
// the lowest tty number is the control modem( Which I'm not so sure is always
// correct).
//
// TODO: comeup with a proper way to identify modems
func (m *Manager) AddDevice(d *udev.Device) error {
	err := m.addDevice(d)
	if err != nil {
		return err
	}
	return m.Symlink()
}
func (m *Manager) addDevice(d *udev.Device) error {
	name := filepath.Join("/dev", filepath.Base(d.Devpath()))
	cfg := serial.Config{Name: name, Baud: 9600, ReadTimeout: 10 * time.Second}
	conn := &Conn{device: cfg}
	if strings.Contains(name, "ttyUSB") {
		fmt.Println("checking modem")
		modem, err := newModem(conn)
		if err != nil {
			return err
		}
		msg := &message{Type: "add_dongle", Data: make(map[string]interface{})}
		msg.Data["imei"] = modem.IMEI
		msg.Data["imsi"] = modem.IMSI
		m.events <- msg
		if mm, ok := m.getModem(modem.IMEI); ok {
			n1, err := getttyNum(mm.Path)
			if err != nil {
				return err
			}
			n2, err := getttyNum(modem.Path)
			if err != nil {
				return err
			}
			if n1 > n2 {
				m.setModem(modem)
			}
			return nil
		}
		m.setModem(modem)
	}
	return nil
}

func (m *Manager) Symlink() error {
	for _, v := range m.modems {
		err := v.Symlink()
		if err != nil {
			fmt.Printf(" ERROR %v\n", err)
			continue
		}
	}
	return nil
}

func (m *Manager) setModem(mod *Modem) {
	m.mu.Lock()
	m.modems[mod.IMEI] = mod
	m.mu.Unlock()
}

func (m *Manager) getModem(imei string) (*Modem, bool) {
	m.mu.RLock()
	mod, ok := m.modems[imei]
	m.mu.RUnlock()
	return mod, ok
}

func getttyNum(tty string) (int, error) {
	b := filepath.Base(tty)
	b = strings.TrimPrefix(b, "ttyUSB")
	return strconv.Atoi(b)
}

// List serves the list of current devices. The list wont cover all devices ,
// only the significant ones( modems for now)
func (m *Manager) List(w http.ResponseWriter, r *http.Request) {
	m.mu.RLock()
	json.NewEncoder(w).Encode(m.modems)
	m.mu.RUnlock()
	w.Header().Set("Content-Type", "application/json")
}

// RunComand runs commands to the exposed devices over serial ports
func (m *Manager) RunCommand(w http.ResponseWriter, r *http.Request) {
}

// RemoveDevice removes device name from the manager
func (m *Manager) RemoveDevice(name string) error {
	return nil
}

type Modem struct {
	IMEI         string `json:"imei"`
	IMSI         string `json:"imsi"`
	Manufacturer string `json:"manufacturer"`
	Path         string `json:"tty"`
	conn         *Conn
}

// Symlink adds symlink to the  modem. The symlink links the tty to the IMEI
// number of the modem.
func (m *Modem) Symlink() error {
	newLink := (fmt.Sprintf("/dev/%s", m.IMEI))
	err := syscall.Unlink(newLink)
	if err != nil {
		fmt.Printf(" ERROR %v \n", err)
	}
	return os.Symlink(m.Path, newLink)
}

func newModem(c *Conn) (*Modem, error) {
	m := &Modem{}
	ich := time.After(10 * time.Second)
STOP:
	for {
		select {
		case <-ich:
			break STOP
		default:
			imei, err := c.Run(modemCommands.IMEI)
			if err != nil {
				continue
			}
			i, err := cleanResult(imei)
			if err != nil {
				continue
			}
			im := string(i)
			if !isNumber(im) {
				continue
			}
			m.IMEI = im
			break STOP
		}
	}
	if m.IMEI == "" {
		return nil, errors.New("no imei")
	}
	// we make sure we obtain the sim card information.
	var wg sync.WaitGroup
	done := time.After(20 * time.Second)
	wg.Add(1)
	go func() {
		defer wg.Done()
	END:
		for {
			select {
			case <-done:
				fmt.Println("Timed out")
				break END
			default:
				imsi, err := c.Run("AT+CIMI")
				if err != nil {
					continue
				}
				s, err := cleanResult(imsi)
				if err != nil {
					continue
				}
				ss := string(s)
				if !isNumber(ss) {
					continue
				}
				if m.IMEI == ss {
					continue
				}
				fmt.Println(string(s))
				m.IMSI = ss
				break END
			}
		}
	}()
	wg.Wait()
	if m.IMSI == "" {
		return nil, errors.New(" can't find IMSI")
	}
	m.conn = c
	m.Path = c.device.Name
	return m, nil
}

func isNumber(src string) bool {
	for _, v := range src {
		if !unicode.IsDigit(v) {
			return false
		}
	}
	return true
}

func (m *Manager) reload() {
	for _, v := range m.devices {
		conn := &Conn{device: v}
		modem, err := newModem(conn)
		if err != nil {
			continue
		}
		fmt.Println(*modem)
		m.modems[modem.IMEI] = modem
	}
}

func cleanResult(src []byte) ([]byte, error) {
	i := bytes.Index(src, []byte("OK"))
	if i == -1 {
		return nil, errors.New("not okay")
	}
	ns := bytes.TrimSpace(src[:i])
	ch, _ := utf8.DecodeRune(ns)
	if unicode.IsLetter(ch) {
		at := bytes.Index(ns, []byte("AT"))
		if at != -1 {
			i := bytes.IndexRune(ns[at:], '\r')
			if i > 0 {
				return bytes.TrimSpace(ns[at+i:]), nil
			}
		}
	}
	return ns, nil
}

//Close shuts down the device manager. This makes sure the udev monitor is
//closed and all goroutines are properly exited.
func (m *Manager) Close() {
	m.stop <- struct{}{}
}

func reader(ws *websocket.Conn) {
	defer ws.Close()
	for {
		_, _, err := ws.ReadMessage()
		if err != nil {
			log.Println(err)
		}
	}
}

// Updates is websocket http handler for sending updates about the devices
// plugged into the system in real time.
func (m *Manager) Updates(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		if _, ok := err.(websocket.HandshakeError); !ok {
			log.Println(err)
		}
		return
	}
	go func() {
		for {
			select {
			case ev := <-m.events:
				_ = ws.WriteJSON(ev)
			}
		}
	}()
	reader(ws)
}

// Conn is a device serial connection
type Conn struct {
	device serial.Config
	imei   string
	port   *serial.Port
	isOpen bool
}

// Open opens a serial port to the undelying device
func (c *Conn) Open() error {
	p, err := serial.OpenPort(&c.device)
	if err != nil {
		return err
	}
	c.port = p
	c.isOpen = true
	return nil
}

// Close closes the port helt by *Conn.
func (c *Conn) Close() error {
	if c.isOpen {
		return c.port.Close()
	}
	return nil
}

// Write wites b to the serieal port
func (c *Conn) Write(b []byte) (int, error) {
	return c.port.Write(b)
}

// Read reads from serial port
func (c *Conn) Read(b []byte) (int, error) {
	return c.port.Read(b)
}

// Exec sends the command over serial port and rrturns the response. If the port
// is closed it is opened  before sending the command.
func (c *Conn) Exec(cmd string) ([]byte, error) {
	if !c.isOpen {
		err := c.Open()
		if err != nil {
			return nil, err
		}
	}
	_, err := c.Write([]byte(cmd))
	if err != nil {
		return nil, err
	}
	buf := make([]byte, 128)
	_, err = c.Read(buf)
	if err != nil {
		return nil, err
	}
	if !bytes.Contains(buf, []byte("OK")) {
		return nil, errors.New("command " + string(cmd) + " xeite without OK" + " got " + string(buf))
	}
	_ = c.port.Flush()
	_ = c.port.Close()
	c.isOpen = false
	return buf, nil
}

// Run helper for Exec that adds \r to the command
func (c *Conn) Run(cmd string) ([]byte, error) {
	return c.Exec(fmt.Sprintf("%s \r", cmd))
}
<|endoftext|>


<commit_before><commit_msg>Add Triangle type<commit_after><|endoftext|>


<commit_before>// checkpoint is a package for checking version information and alerts
// for a HashiCorp product.
package checkpoint

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strings"
)

// CheckParams are the parameters for configuring a check request.
type CheckParams struct {
	// Product and version are used to lookup the correct product and
	// alerts for the proper version. The version is also used to perform
	// a version check.
	Product string
	Version string

	// Arch and OS are used to filter alerts potentially only to things
	// affecting a specific os/arch combination. If these aren't specified,
	// they'll be automatically filled in.
	Arch string
	OS   string

	// Signature is some random signature that should be stored and used
	// as a cookie-like value. This ensures that alerts aren't repeated.
	// If the signature is changed, repeat alerts may be sent down. The
	// signature should NOT be anything identifiable to a user (such as
	// a MAC address). It should be random.
	//
	// If SignatureFile is given, then the signature will be read from this
	// file. If the file doesn't exist, then a random signature will
	// automatically be generated and stored here. SignatureFile will be
	// ignored if Signature is given.
	Signature     string
	SignatureFile string
}

// CheckResponse is the response for a check request.
type CheckResponse struct {
	Product             string
	CurrentVersion      string `json:"current_version"`
	CurrentReleaseDate  int    `json:"current_release_date"`
	CurrentDownloadURL  string `json:"current_download_url"`
	CurrentChangelogURL string `json:"current_changelog_url"`
	ProjectWebsite      string `json:"project_website"`
	Outdated            bool   `json:"outdated"`
	Alerts              []*CheckAlert
}

// CheckAlert is a single alert message from a check request.
//
// These never have to be manually constructed, and are typically populated
// into a CheckResponse as a result of the Check request.
type CheckAlert struct {
	ID      int
	Date    int
	Message string
	URL     string
	Level   string
}

// Check checks for alerts and new version information.
func Check(p *CheckParams) (*CheckResponse, error) {
	var u url.URL

	if p.Arch == "" {
		p.Arch = runtime.GOARCH
	}
	if p.OS == "" {
		p.OS = runtime.GOOS
	}

	// If we're given a SignatureFile, then attempt to read that.
	signature := p.Signature
	if p.Signature == "" && p.SignatureFile != "" {
		if _, err := os.Stat(p.SignatureFile); err != nil {
			// If this isn't a non-exist error, then return that.
			if !os.IsNotExist(err) {
				return nil, err
			}

			// The file doesn't exist, so create a signature.
			var b [16]byte
			n := 0
			for n < 16 {
				n2, err := rand.Read(b[n:])
				if err != nil {
					return nil, err
				}

				n += n2
			}
			signature = fmt.Sprintf(
				"%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

			// Write the signature
			err := ioutil.WriteFile(p.SignatureFile, []byte(signature+"\n"), 0644)
			if err != nil {
				return nil, err
			}
		} else {
			// The file exists, read it out
			sigBytes, err := ioutil.ReadFile(p.SignatureFile)
			if err != nil {
				return nil, err
			}

			signature = strings.TrimSpace(string(sigBytes))
		}
	}

	v := u.Query()
	v.Set("version", p.Version)
	v.Set("arch", p.Arch)
	v.Set("os", p.OS)
	v.Set("signature", signature)

	u.Scheme = "http"
	u.Host = "api.checkpoint.hashicorp.com"
	u.Path = fmt.Sprintf("/v1/check/%s", p.Product)
	u.RawQuery = v.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Unknown status: %d", resp.StatusCode)
	}

	var result CheckResponse
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&result); err != nil {
		return nil, err
	}

	return &result, nil
}
<commit_msg>clean up SignatureFile logic<commit_after>// checkpoint is a package for checking version information and alerts
// for a HashiCorp product.
package checkpoint

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strings"
)

// CheckParams are the parameters for configuring a check request.
type CheckParams struct {
	// Product and version are used to lookup the correct product and
	// alerts for the proper version. The version is also used to perform
	// a version check.
	Product string
	Version string

	// Arch and OS are used to filter alerts potentially only to things
	// affecting a specific os/arch combination. If these aren't specified,
	// they'll be automatically filled in.
	Arch string
	OS   string

	// Signature is some random signature that should be stored and used
	// as a cookie-like value. This ensures that alerts aren't repeated.
	// If the signature is changed, repeat alerts may be sent down. The
	// signature should NOT be anything identifiable to a user (such as
	// a MAC address). It should be random.
	//
	// If SignatureFile is given, then the signature will be read from this
	// file. If the file doesn't exist, then a random signature will
	// automatically be generated and stored here. SignatureFile will be
	// ignored if Signature is given.
	Signature     string
	SignatureFile string
}

// CheckResponse is the response for a check request.
type CheckResponse struct {
	Product             string
	CurrentVersion      string `json:"current_version"`
	CurrentReleaseDate  int    `json:"current_release_date"`
	CurrentDownloadURL  string `json:"current_download_url"`
	CurrentChangelogURL string `json:"current_changelog_url"`
	ProjectWebsite      string `json:"project_website"`
	Outdated            bool   `json:"outdated"`
	Alerts              []*CheckAlert
}

// CheckAlert is a single alert message from a check request.
//
// These never have to be manually constructed, and are typically populated
// into a CheckResponse as a result of the Check request.
type CheckAlert struct {
	ID      int
	Date    int
	Message string
	URL     string
	Level   string
}

// Check checks for alerts and new version information.
func Check(p *CheckParams) (*CheckResponse, error) {
	var u url.URL

	if p.Arch == "" {
		p.Arch = runtime.GOARCH
	}
	if p.OS == "" {
		p.OS = runtime.GOOS
	}

	// If we're given a SignatureFile, then attempt to read that.
	signature := p.Signature
	if p.Signature == "" && p.SignatureFile != "" {
		var err error
		signature, err = checkSignature(p.SignatureFile)
		if err != nil {
			return nil, err
		}
	}

	v := u.Query()
	v.Set("version", p.Version)
	v.Set("arch", p.Arch)
	v.Set("os", p.OS)
	v.Set("signature", signature)

	u.Scheme = "http"
	u.Host = "api.checkpoint.hashicorp.com"
	u.Path = fmt.Sprintf("/v1/check/%s", p.Product)
	u.RawQuery = v.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Unknown status: %d", resp.StatusCode)
	}

	var result CheckResponse
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&result); err != nil {
		return nil, err
	}

	return &result, nil
}

func checkSignature(path string) (string, error) {
	_, err := os.Stat(path)
	if err == nil {
		// The file exists, read it out
		sigBytes, err := ioutil.ReadFile(path)
		if err != nil {
			return "", err
		}

		return strings.TrimSpace(string(sigBytes)), nil
	}

	// If this isn't a non-exist error, then return that.
	if !os.IsNotExist(err) {
		return "", err
	}

	// The file doesn't exist, so create a signature.
	var b [16]byte
	n := 0
	for n < 16 {
		n2, err := rand.Read(b[n:])
		if err != nil {
			return "", err
		}

		n += n2
	}
	signature := fmt.Sprintf(
		"%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

	// Write the signature
	if err := ioutil.WriteFile(path, []byte(signature+"\n"), 0644); err != nil {
		return "", err
	}

	return signature, nil
}
<|endoftext|>


<commit_before>package invdapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/Invoiced/invoiced-go/invdendpoint"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

const requestURL = "https://api.invoiced.com"
const devRequestURL = "https://api.sandbox.invoiced.com"
const requestType = "application/json"
const InvoicedTokenString = "invoicedToken"

const version = "0.1"

func Version() string {
	return version
}

type Connection struct {
	key             string
	itemsPerRequest int
	client          *http.Client
	url             string
}

type InvoicedToken struct {
	Key string `json:"invoicedApiKey"`
}

func NewConnection(key string, devMode bool) *Connection {
	c := new(Connection)
	c.key = key
	c.client = new(http.Client)
	if devMode {
		c.url = devRequestURL
	} else {
		c.url = requestURL
	}

	return c
}

func checkStatusForError(status int, r io.Reader) error {
	if status < 400 {
		return nil
	}

	body, err := ioutil.ReadAll(r)
	if err != nil {
		panic(err)
	}

	apiError := new(APIError)

	err = json.Unmarshal(body, apiError)

	if err != nil {
		apiError.Type = string(body)

	}

	return errors.New(apiError.Error())

}

func pushDataIntoStruct(endPointData interface{}, respBody io.Reader) {

	body, err := ioutil.ReadAll(respBody)

	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(body, endPointData)

	if err != nil {
		panic(err)
	}

}

func parseURLLinksFromString(s string) map[string]string {

	urlAndLinkMap := make(map[string]string)

	rawURLLinksAndRelations := strings.Split(s, ",")

	for _, rawURLLinkRelation := range rawURLLinksAndRelations {
		parsedRawURLAndRelation := strings.Split(rawURLLinkRelation, ";")
		url := parseRawURL(parsedRawURLAndRelation[0])
		relation := parseRawRelation(parsedRawURLAndRelation[1])

		urlAndLinkMap[relation] = url
	}

	return urlAndLinkMap
}

func parseRawRelation(s string) string {
	//parse rel="last" => last

	first := strings.Index(s, "\"")
	last := strings.LastIndex(s, "\"")

	trimmed := s[first+1 : last]

	trimmed = strings.TrimSpace(trimmed)

	return trimmed

}

func parseRawURL(s string) string {
	//<https://api.invoiced.com/invoices?page=1>

	trimmed := strings.Trim(s, " < ")

	trimmed = strings.Trim(trimmed, " > ")

	trimmed = strings.TrimSpace(trimmed)

	return trimmed

}

func addFilterSortToEndPoint(endpoint string, filter *invdendpoint.Filter, sort *invdendpoint.Sort) string {

	emptyFilter := true
	emptySort := true

	if filter != nil && filter.String() != "" {
		emptyFilter = false
	}

	if sort != nil && sort.String() != "" {
		emptySort = false
	}

	if !emptyFilter && !emptySort {
		return endpoint + "?" + filter.String() + "&" + sort.String()
	} else if !emptyFilter && emptySort {
		return endpoint + "?" + filter.String()
	} else if emptyFilter && !emptySort {
		return endpoint + "?" + sort.String()
	}

	return endpoint

}

func makeEndPointSingular(endpoint string, id int64) string {
	return endpoint + "/" + strconv.FormatInt(id, 10)
}

func (c *Connection) makeEndPointURL(endPoint string) string {

	return c.url + endPoint
}

func (c *Connection) setItemsPerRequest(items int) {
	c.itemsPerRequest = items
}

func (c *Connection) get(endPoint string) *http.Response {

	req, err := http.NewRequest("GET", endPoint, nil)
	req.SetBasicAuth(c.key, "")

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) post(endPoint string, body io.Reader) *http.Response {

	req, err := http.NewRequest("POST", endPoint, body)
	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) patch(endPoint string, body io.Reader) *http.Response {

	req, err := http.NewRequest("PATCH", endPoint, body)
	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) deleteRequest(endPoint string) *http.Response {

	req, err := http.NewRequest("DELETE", endPoint, nil)
	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) create(endPoint string, requestData interface{}, responseData interface{}) error {

	b, err := json.Marshal(requestData)

	if err != nil {
		panic(err)
	}

	body := bytes.NewBuffer(b)

	resp := c.post(endPoint, body)

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	pushDataIntoStruct(responseData, resp.Body)

	return nil

}

func (c *Connection) delete(endPoint string) error {

	resp := c.deleteRequest(endPoint)

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	return nil

}

func (c *Connection) update(endPoint string, requestData interface{}, responseData interface{}) error {

	b, err := json.Marshal(requestData)

	if err != nil {
		panic(err)
	}

	body := bytes.NewBuffer(b)

	resp := c.patch(endPoint, body)

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	pushDataIntoStruct(responseData, resp.Body)

	return nil

}

func (c *Connection) count(endPoint string) (int64, error) {
	resp := c.get(endPoint)

	defer resp.Body.Close()

	apiErr := checkStatusForError(resp.StatusCode, resp.Body)

	if apiErr != nil {
		return 0, apiErr
	}

	s := resp.Header.Get("X-Total-Count")

	i, err := strconv.ParseInt(s, 10, 64)

	if err != nil {
		panic(err)
	}

	return i, nil

}

func (c *Connection) retrieveDataFromAPI(endPoint string, endPointData interface{}) (string, error) {

	nextURL := ""

	resp := c.get(endPoint)

	defer resp.Body.Close()

	link := resp.Header.Get("Link")

	if link != "" {
		nextMap := parseURLLinksFromString(link)

		if nextMap["self"] != nextMap["next"] {
			nextURL = nextMap["next"]
		}
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return "", apiError
	}

	pushDataIntoStruct(endPointData, resp.Body)

	return nextURL, nil

}
<commit_msg>debugging invoiced client<commit_after>package invdapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/Invoiced/invoiced-go/invdendpoint"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

const requestURL = "https://api.invoiced.com"
const devRequestURL = "https://api.sandbox.invoiced.com"
const requestType = "application/json"
const InvoicedTokenString = "invoicedToken"

const version = "0.1"

func Version() string {
	return version
}

type Connection struct {
	key             string
	itemsPerRequest int
	client          *http.Client
	url             string
}

type InvoicedToken struct {
	Key string `json:"invoicedApiKey"`
}

func NewConnection(key string, devMode bool) *Connection {
	c := new(Connection)
	c.key = key
	c.client = new(http.Client)
	if devMode {
		c.url = devRequestURL
	} else {
		c.url = requestURL
	}

	return c
}

func checkStatusForError(status int, r io.Reader) error {
	if status < 400 {
		return nil
	}

	body, err := ioutil.ReadAll(r)
	if err != nil {
		panic(err)
	}

	apiError := new(APIError)

	log.Printlns("json body => ", string(body))
	err = json.Unmarshal(body, apiError)

	if err != nil {
		apiError.Type = string(body)

	}

	return errors.New(apiError.Error())

}

func pushDataIntoStruct(endPointData interface{}, respBody io.Reader) {

	body, err := ioutil.ReadAll(respBody)

	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(body, endPointData)

	if err != nil {
		panic(err)
	}

}

func parseURLLinksFromString(s string) map[string]string {

	urlAndLinkMap := make(map[string]string)

	rawURLLinksAndRelations := strings.Split(s, ",")

	for _, rawURLLinkRelation := range rawURLLinksAndRelations {
		parsedRawURLAndRelation := strings.Split(rawURLLinkRelation, ";")
		url := parseRawURL(parsedRawURLAndRelation[0])
		relation := parseRawRelation(parsedRawURLAndRelation[1])

		urlAndLinkMap[relation] = url
	}

	return urlAndLinkMap
}

func parseRawRelation(s string) string {
	//parse rel="last" => last

	first := strings.Index(s, "\"")
	last := strings.LastIndex(s, "\"")

	trimmed := s[first+1 : last]

	trimmed = strings.TrimSpace(trimmed)

	return trimmed

}

func parseRawURL(s string) string {
	//<https://api.invoiced.com/invoices?page=1>

	trimmed := strings.Trim(s, " < ")

	trimmed = strings.Trim(trimmed, " > ")

	trimmed = strings.TrimSpace(trimmed)

	return trimmed

}

func addFilterSortToEndPoint(endpoint string, filter *invdendpoint.Filter, sort *invdendpoint.Sort) string {

	emptyFilter := true
	emptySort := true

	if filter != nil && filter.String() != "" {
		emptyFilter = false
	}

	if sort != nil && sort.String() != "" {
		emptySort = false
	}

	if !emptyFilter && !emptySort {
		return endpoint + "?" + filter.String() + "&" + sort.String()
	} else if !emptyFilter && emptySort {
		return endpoint + "?" + filter.String()
	} else if emptyFilter && !emptySort {
		return endpoint + "?" + sort.String()
	}

	return endpoint

}

func makeEndPointSingular(endpoint string, id int64) string {
	return endpoint + "/" + strconv.FormatInt(id, 10)
}

func (c *Connection) makeEndPointURL(endPoint string) string {

	return c.url + endPoint
}

func (c *Connection) setItemsPerRequest(items int) {
	c.itemsPerRequest = items
}

func (c *Connection) get(endPoint string) *http.Response {

	req, err := http.NewRequest("GET", endPoint, nil)
	req.SetBasicAuth(c.key, "")

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) post(endPoint string, body io.Reader) *http.Response {

	req, err := http.NewRequest("POST", endPoint, body)
	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) patch(endPoint string, body io.Reader) *http.Response {

	req, err := http.NewRequest("PATCH", endPoint, body)
	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) deleteRequest(endPoint string) *http.Response {

	req, err := http.NewRequest("DELETE", endPoint, nil)
	req.SetBasicAuth(c.key, "")
	req.Header.Set("Content-Type", requestType)

	resp, err := c.client.Do(req)

	if err != nil {
		panic(err)
	}

	return resp

}

func (c *Connection) create(endPoint string, requestData interface{}, responseData interface{}) error {

	b, err := json.Marshal(requestData)

	if err != nil {
		panic(err)
	}

	body := bytes.NewBuffer(b)

	resp := c.post(endPoint, body)

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	pushDataIntoStruct(responseData, resp.Body)

	return nil

}

func (c *Connection) delete(endPoint string) error {

	resp := c.deleteRequest(endPoint)

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	return nil

}

func (c *Connection) update(endPoint string, requestData interface{}, responseData interface{}) error {

	b, err := json.Marshal(requestData)

	if err != nil {
		panic(err)
	}

	body := bytes.NewBuffer(b)

	resp := c.patch(endPoint, body)

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return apiError
	}

	pushDataIntoStruct(responseData, resp.Body)

	return nil

}

func (c *Connection) count(endPoint string) (int64, error) {
	resp := c.get(endPoint)

	defer resp.Body.Close()

	apiErr := checkStatusForError(resp.StatusCode, resp.Body)

	if apiErr != nil {
		return 0, apiErr
	}

	s := resp.Header.Get("X-Total-Count")

	i, err := strconv.ParseInt(s, 10, 64)

	if err != nil {
		panic(err)
	}

	return i, nil

}

func (c *Connection) retrieveDataFromAPI(endPoint string, endPointData interface{}) (string, error) {

	nextURL := ""

	resp := c.get(endPoint)

	defer resp.Body.Close()

	link := resp.Header.Get("Link")

	if link != "" {
		nextMap := parseURLLinksFromString(link)

		if nextMap["self"] != nextMap["next"] {
			nextURL = nextMap["next"]
		}
	}

	apiError := checkStatusForError(resp.StatusCode, resp.Body)

	if apiError != nil {
		return "", apiError
	}

	pushDataIntoStruct(endPointData, resp.Body)

	return nextURL, nil

}
<|endoftext|>


<commit_before>/*
 * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc.
 *
 * 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 minio

/// Multipart upload defaults.

// minimumPartSize - minimum part size 5MiB per object after which
// putObject behaves internally as multipart.
var minimumPartSize int64 = 1024 * 1024 * 5

// maxParts - maximum parts for a single multipart session.
var maxParts = int64(10000)

// maxPartSize - maximum part size 5GiB for a single multipart upload operation.
var maxPartSize int64 = 1024 * 1024 * 1024 * 5

// maxSinglePutObjectSize - maximum size 5GiB of object per PUT operation.
var maxSinglePutObjectSize = 1024 * 1024 * 1024 * 5

// maxMultipartPutObjectSize - maximum size 5TiB of object for Multipart operation.
var maxMultipartPutObjectSize = 1024 * 1024 * 1024 * 1024 * 5

// optimalReadAtBufferSize - optimal buffer 5MiB used for reading through ReadAt operation.
var optimalReadAtBufferSize = 1024 * 1024 * 5
<commit_msg>Fix constants on 32 bit machines<commit_after>/*
 * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc.
 *
 * 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 minio

/// Multipart upload defaults.

// minimumPartSize - minimum part size 5MiB per object after which
// putObject behaves internally as multipart.
const minimumPartSize = 1024 * 1024 * 5

// maxParts - maximum parts for a single multipart session.
const maxParts = 10000

// maxPartSize - maximum part size 5GiB for a single multipart upload operation.
const maxPartSize = 1024 * 1024 * 1024 * 5

// maxSinglePutObjectSize - maximum size 5GiB of object per PUT operation.
const maxSinglePutObjectSize = 1024 * 1024 * 1024 * 5

// maxMultipartPutObjectSize - maximum size 5TiB of object for Multipart operation.
const maxMultipartPutObjectSize = 1024 * 1024 * 1024 * 1024 * 5

// optimalReadAtBufferSize - optimal buffer 5MiB used for reading through ReadAt operation.
const optimalReadAtBufferSize = 1024 * 1024 * 5
<|endoftext|>


<commit_before>package docker

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/kr/pty"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"
	"time"
)

type Container struct {
	Id   string
	Root string

	Created time.Time

	Path string
	Args []string

	Config     *Config
	Filesystem *Filesystem
	State      *State

	lxcConfigPath string
	cmd           *exec.Cmd
	stdout        *writeBroadcaster
	stderr        *writeBroadcaster
	stdin         io.ReadCloser
	stdinPipe     io.WriteCloser

	stdoutLog *bytes.Buffer
	stderrLog *bytes.Buffer
}

type Config struct {
	Hostname  string
	Ram       int64
	Tty       bool // Attach standard streams to a tty, including stdin if it is not closed.
	OpenStdin bool // Open stdin
}

func createContainer(id string, root string, command string, args []string, layers []string, config *Config) (*Container, error) {
	container := &Container{
		Id:         id,
		Root:       root,
		Created:    time.Now(),
		Path:       command,
		Args:       args,
		Config:     config,
		Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers),
		State:      newState(),

		lxcConfigPath: path.Join(root, "config.lxc"),
		stdout:        newWriteBroadcaster(),
		stderr:        newWriteBroadcaster(),
		stdoutLog:     new(bytes.Buffer),
		stderrLog:     new(bytes.Buffer),
	}
	if container.Config.OpenStdin {
		container.stdin, container.stdinPipe = io.Pipe()
	} else {
		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
	}
	container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
	container.stderr.AddWriter(NopWriteCloser(container.stderrLog))

	if err := os.Mkdir(root, 0700); err != nil {
		return nil, err
	}
	if err := container.Filesystem.createMountPoints(); err != nil {
		return nil, err
	}
	if err := container.save(); err != nil {
		return nil, err
	}
	if err := container.generateLXCConfig(); err != nil {
		return nil, err
	}
	return container, nil
}

func loadContainer(containerPath string) (*Container, error) {
	data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
	if err != nil {
		return nil, err
	}
	container := &Container{
		stdout:    newWriteBroadcaster(),
		stderr:    newWriteBroadcaster(),
		stdoutLog: new(bytes.Buffer),
		stderrLog: new(bytes.Buffer),
	}
	if err := json.Unmarshal(data, container); err != nil {
		return nil, err
	}
	if err := container.Filesystem.createMountPoints(); err != nil {
		return nil, err
	}
	if container.Config.OpenStdin {
		container.stdin, container.stdinPipe = io.Pipe()
	} else {
		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
	}
	container.State = newState()
	return container, nil
}

func (container *Container) Cmd() *exec.Cmd {
	return container.cmd
}

func (container *Container) When() time.Time {
	return container.Created
}

func (container *Container) loadUserData() (map[string]string, error) {
	jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
	if err != nil {
		if os.IsNotExist(err) {
			return make(map[string]string), nil
		}
		return nil, err
	}
	data := make(map[string]string)
	if err := json.Unmarshal(jsonData, &data); err != nil {
		return nil, err
	}
	return data, nil
}

func (container *Container) saveUserData(data map[string]string) error {
	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
}

func (container *Container) SetUserData(key, value string) error {
	data, err := container.loadUserData()
	if err != nil {
		return err
	}
	data[key] = value
	return container.saveUserData(data)
}

func (container *Container) GetUserData(key string) string {
	data, err := container.loadUserData()
	if err != nil {
		return ""
	}
	if value, exists := data[key]; exists {
		return value
	}
	return ""
}

func (container *Container) save() (err error) {
	data, err := json.Marshal(container)
	if err != nil {
		return
	}
	return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0700)
}

func (container *Container) generateLXCConfig() error {
	fo, err := os.Create(container.lxcConfigPath)
	if err != nil {
		return err
	}
	defer fo.Close()

	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
		return err
	}
	return nil
}

func (container *Container) Start() error {
	if err := container.Filesystem.EnsureMounted(); err != nil {
		return err
	}

	params := []string{
		"-n", container.Id,
		"-f", container.lxcConfigPath,
		"--",
		container.Path,
	}
	params = append(params, container.Args...)

	container.cmd = exec.Command("/usr/bin/lxc-start", params...)

	if container.Config.Tty {
		Pty, tty, err := pty.Open()
		if err != nil {
			return err
		}
		container.cmd.Stdout = tty
		container.cmd.Stderr = tty
		if container.stdin != nil {
			container.cmd.Stdin = tty
		}
		container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
		if err := container.cmd.Start(); err != nil {
			Pty.Close()
			return err
		}
		tty.Close()
		// Attach Pty to stdout
		go func() {
			defer container.stdout.Close()
			for {
				data := make([]byte, 1024)
				n, err := Pty.Read(data)
				if err != nil {
					return
				}
				log.Printf("STDOUT <%s>\n", data)
				if _, err = container.stdout.Write(data[:n]); err != nil {
					return
				}
				log.Printf("STDOUT SENT\n")
			}
			//io.Copy(container.stdout, Pty)
			//container.stdout.Close()
		}()
		// Attach Pty to stderr
		go func() {
			io.Copy(container.stderr, Pty)
			container.stderr.Close()
		}()
		// Attach Pty to stdin
		if container.stdin != nil {
			go func() {
				defer Pty.Close()
				io.Copy(Pty, container.stdin)
			}()
		}
	} else {
		container.cmd.Stdout = container.stdout
		container.cmd.Stderr = container.stderr
		if container.stdin != nil {
			stdin, err := container.cmd.StdinPipe()
			if err != nil {
				return err
			}
			go func() {
				defer stdin.Close()
				io.Copy(stdin, container.stdin)
			}()
		}
	}
	if err := container.cmd.Start(); err != nil {
		return err
	}
	container.State.setRunning(container.cmd.Process.Pid)
	container.save()
	go container.monitor()
	return nil
}

func (container *Container) Run() error {
	if err := container.Start(); err != nil {
		return err
	}
	container.Wait()
	return nil
}

func (container *Container) Output() (output []byte, err error) {
	pipe, err := container.StdoutPipe()
	if err != nil {
		return nil, err
	}
	defer pipe.Close()
	if err := container.Start(); err != nil {
		return nil, err
	}
	output, err = ioutil.ReadAll(pipe)
	container.Wait()
	return output, err
}

// StdinPipe() returns a pipe connected to the standard input of the container's
// active process.
//
func (container *Container) StdinPipe() (io.WriteCloser, error) {
	return container.stdinPipe, nil
}

func (container *Container) StdoutPipe() (io.ReadCloser, error) {
	reader, writer := io.Pipe()
	container.stdout.AddWriter(writer)
	return newBufReader(reader), nil
}

func (container *Container) StdoutLog() io.Reader {
	return strings.NewReader(container.stdoutLog.String())
}

func (container *Container) StderrPipe() (io.ReadCloser, error) {
	reader, writer := io.Pipe()
	container.stderr.AddWriter(writer)
	return newBufReader(reader), nil
}

func (container *Container) StderrLog() io.Reader {
	return strings.NewReader(container.stderrLog.String())
}

func (container *Container) monitor() {
	// Wait for the program to exit
	container.cmd.Wait()
	exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()

	// Cleanup
	container.stdout.Close()
	container.stderr.Close()
	if err := container.Filesystem.Umount(); err != nil {
		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
	}

	// Report status back
	container.State.setStopped(exitCode)
	container.save()
}

func (container *Container) kill() error {
	if err := container.cmd.Process.Kill(); err != nil {
		return err
	}
	// Wait for the container to be actually stopped
	container.Wait()
	return nil
}

func (container *Container) Kill() error {
	if !container.State.Running {
		return nil
	}
	return container.kill()
}

func (container *Container) Stop() error {
	if !container.State.Running {
		return nil
	}

	// 1. Send a SIGTERM
	if output, err := exec.Command("/usr/bin/lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
		log.Printf(string(output))
		log.Printf("Failed to send SIGTERM to the process, force killing")
		if err := container.Kill(); err != nil {
			return err
		}
	}

	// 2. Wait for the process to exit on its own
	if err := container.WaitTimeout(10 * time.Second); err != nil {
		log.Printf("Container %v failed to exit within 10 seconds of SIGTERM - using the force", container.Id)
		if err := container.Kill(); err != nil {
			return err
		}
	}
	return nil
}

func (container *Container) Restart() error {
	if err := container.Stop(); err != nil {
		return err
	}
	if err := container.Start(); err != nil {
		return err
	}
	return nil
}

func (container *Container) Wait() {

	for container.State.Running {
		container.State.wait()
	}
}

func (container *Container) WaitTimeout(timeout time.Duration) error {
	done := make(chan bool)
	go func() {
		container.Wait()
		done <- true
	}()

	select {
	case <-time.After(timeout):
		return errors.New("Timed Out")
	case <-done:
		return nil
	}
	return nil
}
<commit_msg>Container: Improved pseudo-terminal support<commit_after>package docker

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/kr/pty"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"
	"time"
)

type Container struct {
	Id   string
	Root string

	Created time.Time

	Path string
	Args []string

	Config     *Config
	Filesystem *Filesystem
	State      *State

	lxcConfigPath string
	cmd           *exec.Cmd
	stdout        *writeBroadcaster
	stderr        *writeBroadcaster
	stdin         io.ReadCloser
	stdinPipe     io.WriteCloser

	stdoutLog *bytes.Buffer
	stderrLog *bytes.Buffer
}

type Config struct {
	Hostname  string
	Ram       int64
	Tty       bool // Attach standard streams to a tty, including stdin if it is not closed.
	OpenStdin bool // Open stdin
}

func createContainer(id string, root string, command string, args []string, layers []string, config *Config) (*Container, error) {
	container := &Container{
		Id:         id,
		Root:       root,
		Created:    time.Now(),
		Path:       command,
		Args:       args,
		Config:     config,
		Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers),
		State:      newState(),

		lxcConfigPath: path.Join(root, "config.lxc"),
		stdout:        newWriteBroadcaster(),
		stderr:        newWriteBroadcaster(),
		stdoutLog:     new(bytes.Buffer),
		stderrLog:     new(bytes.Buffer),
	}
	if container.Config.OpenStdin {
		container.stdin, container.stdinPipe = io.Pipe()
	} else {
		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
	}
	container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
	container.stderr.AddWriter(NopWriteCloser(container.stderrLog))

	if err := os.Mkdir(root, 0700); err != nil {
		return nil, err
	}
	if err := container.Filesystem.createMountPoints(); err != nil {
		return nil, err
	}
	if err := container.save(); err != nil {
		return nil, err
	}
	if err := container.generateLXCConfig(); err != nil {
		return nil, err
	}
	return container, nil
}

func loadContainer(containerPath string) (*Container, error) {
	data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
	if err != nil {
		return nil, err
	}
	container := &Container{
		stdout:    newWriteBroadcaster(),
		stderr:    newWriteBroadcaster(),
		stdoutLog: new(bytes.Buffer),
		stderrLog: new(bytes.Buffer),
	}
	if err := json.Unmarshal(data, container); err != nil {
		return nil, err
	}
	if err := container.Filesystem.createMountPoints(); err != nil {
		return nil, err
	}
	if container.Config.OpenStdin {
		container.stdin, container.stdinPipe = io.Pipe()
	} else {
		container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
	}
	container.State = newState()
	return container, nil
}

func (container *Container) Cmd() *exec.Cmd {
	return container.cmd
}

func (container *Container) When() time.Time {
	return container.Created
}

func (container *Container) loadUserData() (map[string]string, error) {
	jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
	if err != nil {
		if os.IsNotExist(err) {
			return make(map[string]string), nil
		}
		return nil, err
	}
	data := make(map[string]string)
	if err := json.Unmarshal(jsonData, &data); err != nil {
		return nil, err
	}
	return data, nil
}

func (container *Container) saveUserData(data map[string]string) error {
	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
}

func (container *Container) SetUserData(key, value string) error {
	data, err := container.loadUserData()
	if err != nil {
		return err
	}
	data[key] = value
	return container.saveUserData(data)
}

func (container *Container) GetUserData(key string) string {
	data, err := container.loadUserData()
	if err != nil {
		return ""
	}
	if value, exists := data[key]; exists {
		return value
	}
	return ""
}

func (container *Container) save() (err error) {
	data, err := json.Marshal(container)
	if err != nil {
		return
	}
	return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0700)
}

func (container *Container) generateLXCConfig() error {
	fo, err := os.Create(container.lxcConfigPath)
	if err != nil {
		return err
	}
	defer fo.Close()

	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
		return err
	}
	return nil
}

func (container *Container) startPty() error {
	stdout_master, stdout_slave, err := pty.Open()
	if err != nil {
		return err
	}
	container.cmd.Stdout = stdout_slave

	stderr_master, stderr_slave, err := pty.Open()
	if err != nil {
		return err
	}
	container.cmd.Stderr = stderr_slave

	// Copy the PTYs to our broadcasters
	go func() {
		defer container.stdout.Close()
		io.Copy(container.stdout, stdout_master)
	}()

	go func() {
		defer container.stderr.Close()
		io.Copy(container.stderr, stderr_master)
	}()

	// stdin
	var stdin_slave io.ReadCloser
	if container.Config.OpenStdin {
		stdin_master, stdin_slave, err := pty.Open()
		if err != nil {
			return err
		}
		container.cmd.Stdin = stdin_slave
		// FIXME: The following appears to be broken.
		// "cannot set terminal process group (-1): Inappropriate ioctl for device"
		// container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
		go func() {
			defer container.stdin.Close()
			io.Copy(stdin_master, container.stdin)
		}()
	}
	if err := container.cmd.Start(); err != nil {
		return err
	}
	stdout_slave.Close()
	stderr_slave.Close()
	if stdin_slave != nil {
		stdin_slave.Close()
	}
	return nil
}

func (container *Container) start() error {
	container.cmd.Stdout = container.stdout
	container.cmd.Stderr = container.stderr
	if container.Config.OpenStdin {
		stdin, err := container.cmd.StdinPipe()
		if err != nil {
			return err
		}
		go func() {
			defer stdin.Close()
			io.Copy(stdin, container.stdin)
		}()
	}
	return container.cmd.Start()
}

func (container *Container) Start() error {
	if err := container.Filesystem.EnsureMounted(); err != nil {
		return err
	}

	params := []string{
		"-n", container.Id,
		"-f", container.lxcConfigPath,
		"--",
		container.Path,
	}
	params = append(params, container.Args...)

	container.cmd = exec.Command("/usr/bin/lxc-start", params...)

	var err error
	if container.Config.Tty {
		err = container.startPty()
	} else {
		err = container.start()
	}
	if err != nil {
		return err
	}
	container.State.setRunning(container.cmd.Process.Pid)
	container.save()
	go container.monitor()
	return nil
}

func (container *Container) Run() error {
	if err := container.Start(); err != nil {
		return err
	}
	container.Wait()
	return nil
}

func (container *Container) Output() (output []byte, err error) {
	pipe, err := container.StdoutPipe()
	if err != nil {
		return nil, err
	}
	defer pipe.Close()
	if err := container.Start(); err != nil {
		return nil, err
	}
	output, err = ioutil.ReadAll(pipe)
	container.Wait()
	return output, err
}

// StdinPipe() returns a pipe connected to the standard input of the container's
// active process.
//
func (container *Container) StdinPipe() (io.WriteCloser, error) {
	return container.stdinPipe, nil
}

func (container *Container) StdoutPipe() (io.ReadCloser, error) {
	reader, writer := io.Pipe()
	container.stdout.AddWriter(writer)
	return newBufReader(reader), nil
}

func (container *Container) StdoutLog() io.Reader {
	return strings.NewReader(container.stdoutLog.String())
}

func (container *Container) StderrPipe() (io.ReadCloser, error) {
	reader, writer := io.Pipe()
	container.stderr.AddWriter(writer)
	return newBufReader(reader), nil
}

func (container *Container) StderrLog() io.Reader {
	return strings.NewReader(container.stderrLog.String())
}

func (container *Container) monitor() {
	// Wait for the program to exit
	container.cmd.Wait()
	exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()

	// Cleanup
	container.stdout.Close()
	container.stderr.Close()
	if err := container.Filesystem.Umount(); err != nil {
		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
	}

	// Report status back
	container.State.setStopped(exitCode)
	container.save()
}

func (container *Container) kill() error {
	if err := container.cmd.Process.Kill(); err != nil {
		return err
	}
	// Wait for the container to be actually stopped
	container.Wait()
	return nil
}

func (container *Container) Kill() error {
	if !container.State.Running {
		return nil
	}
	return container.kill()
}

func (container *Container) Stop() error {
	if !container.State.Running {
		return nil
	}

	// 1. Send a SIGTERM
	if output, err := exec.Command("/usr/bin/lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
		log.Printf(string(output))
		log.Printf("Failed to send SIGTERM to the process, force killing")
		if err := container.Kill(); err != nil {
			return err
		}
	}

	// 2. Wait for the process to exit on its own
	if err := container.WaitTimeout(10 * time.Second); err != nil {
		log.Printf("Container %v failed to exit within 10 seconds of SIGTERM - using the force", container.Id)
		if err := container.Kill(); err != nil {
			return err
		}
	}
	return nil
}

func (container *Container) Restart() error {
	if err := container.Stop(); err != nil {
		return err
	}
	if err := container.Start(); err != nil {
		return err
	}
	return nil
}

func (container *Container) Wait() {

	for container.State.Running {
		container.State.wait()
	}
}

func (container *Container) WaitTimeout(timeout time.Duration) error {
	done := make(chan bool)
	go func() {
		container.Wait()
		done <- true
	}()

	select {
	case <-time.After(timeout):
		return errors.New("Timed Out")
	case <-done:
		return nil
	}
	return nil
}
<|endoftext|>