<commit_before>// Copyright 2017 Pilosa Corp.
//
// 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 pilosa

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// Default version check URL.
const (
	defaultVersionCheckURL = "https://diagnostics.pilosa.com/v0/version"
)

type versionResponse struct {
	Version string `json:"version"`
	Message string `json:"message"`
}

// diagnosticsCollector represents a collector/sender of diagnostics data.
type diagnosticsCollector struct {
	mu          sync.Mutex
	host        string
	VersionURL  string
	version     string
	lastVersion string
	startTime   int64
	start       time.Time

	metrics map[string]interface{}

	client *http.Client

	Logger Logger

	server *Server
}

// newDiagnosticsCollector returns a new DiagnosticsCollector given an addr in the format "hostname:port".
func newDiagnosticsCollector(host string) *diagnosticsCollector {
	return &diagnosticsCollector{
		host:       host,
		VersionURL: defaultVersionCheckURL,
		startTime:  time.Now().Unix(),
		start:      time.Now(),
		client:     &http.Client{Timeout: 10 * time.Second},
		metrics:    make(map[string]interface{}),
		Logger:     NopLogger,
	}
}

// SetVersion of locally running Pilosa Cluster to check against master.
func (d *diagnosticsCollector) SetVersion(v string) {
	d.version = v
	d.Set("Version", v)
}

// Flush sends the current metrics.
func (d *diagnosticsCollector) Flush() error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.metrics["Uptime"] = (time.Now().Unix() - d.startTime)
	buf, err := d.encode()
	if err != nil {
		return errors.Wrap(err, "encoding")
	}
	req, err := http.NewRequest("POST", d.host, bytes.NewReader(buf))
	if err != nil {
		return errors.Wrap(err, "making new request")
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := d.client.Do(req)
	if err != nil {
		return errors.Wrap(err, "posting")
	}
	// Intentionally ignoring response body, as user does not need to be notified of error.
	defer resp.Body.Close()
	return nil
}

// CheckVersion of the local build against Pilosa master.
func (d *diagnosticsCollector) CheckVersion() error {
	var rsp versionResponse
	req, err := http.NewRequest("GET", d.VersionURL, nil)
	if err != nil {
		return errors.Wrap(err, "making request")
	}
	resp, err := d.client.Do(req)
	if err != nil {
		return errors.Wrap(err, "getting version")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("http: status=%d", resp.StatusCode)
	} else if err := json.NewDecoder(resp.Body).Decode(&rsp); err != nil {
		return fmt.Errorf("json decode: %s", err)
	}

	// If version has not changed since the last check, return
	if rsp.Version == d.lastVersion {
		return nil
	}

	d.lastVersion = rsp.Version
	if err := d.compareVersion(rsp.Version); err != nil {
		d.Logger.Printf("%s\n", err.Error())
	}

	return nil
}

// compareVersion check version strings.
func (d *diagnosticsCollector) compareVersion(value string) error {
	currentVersion := versionSegments(value)
	localVersion := versionSegments(d.version)

	if localVersion[0] < currentVersion[0] { //Major
		return fmt.Errorf("Warning: You are running Pilosa %s. A newer version (%s) is available: https://github.com/pilosa/pilosa/releases", d.version, value)
	} else if localVersion[1] < currentVersion[1] && localVersion[0] == currentVersion[0] { // Minor
		return fmt.Errorf("Warning: You are running Pilosa %s. The latest Minor release is %s: https://github.com/pilosa/pilosa/releases", d.version, value)
	} else if localVersion[2] < currentVersion[2] && localVersion[0] == currentVersion[0] && localVersion[1] == currentVersion[1] { // Patch
		return fmt.Errorf("There is a new patch release of Pilosa available: %s: https://github.com/pilosa/pilosa/releases", value)
	}

	return nil
}

// Encode metrics maps into the json message format.
func (d *diagnosticsCollector) encode() ([]byte, error) {
	return json.Marshal(d.metrics)
}

// Set adds a key value metric.
func (d *diagnosticsCollector) Set(name string, value interface{}) {
	switch v := value.(type) {
	case string:
		if v == "" {
			// Do not set empty string
			return
		}
	}
	d.mu.Lock()
	defer d.mu.Unlock()
	d.metrics[name] = value
}

// logErr logs the error and returns true if an error exists
func (d *diagnosticsCollector) logErr(err error) bool {
	if err != nil {
		d.Logger.Printf("%v", err)
		return true
	}
	return false
}

// EnrichWithOSInfo adds OS information to the diagnostics payload.
func (d *diagnosticsCollector) EnrichWithOSInfo() {
	uptime, err := d.server.systemInfo.Uptime()
	if !d.logErr(err) {
		d.Set("HostUptime", uptime)
	}
	platform, err := d.server.systemInfo.Platform()
	if !d.logErr(err) {
		d.Set("OSPlatform", platform)
	}
	family, err := d.server.systemInfo.Family()
	if !d.logErr(err) {
		d.Set("OSFamily", family)
	}
	version, err := d.server.systemInfo.OSVersion()
	if !d.logErr(err) {
		d.Set("OSVersion", version)
	}
	kernelVersion, err := d.server.systemInfo.KernelVersion()
	if !d.logErr(err) {
		d.Set("OSKernelVersion", kernelVersion)
	}
}

// EnrichWithMemoryInfo adds memory information to the diagnostics payload.
func (d *diagnosticsCollector) EnrichWithMemoryInfo() {
	memFree, err := d.server.systemInfo.MemFree()
	if !d.logErr(err) {
		d.Set("MemFree", memFree)
	}
	memTotal, err := d.server.systemInfo.MemTotal()
	if !d.logErr(err) {
		d.Set("MemTotal", memTotal)
	}
	memUsed, err := d.server.systemInfo.MemUsed()
	if !d.logErr(err) {
		d.Set("MemUsed", memUsed)
	}
}

// EnrichWithSchemaProperties adds schema info to the diagnostics payload.
func (d *diagnosticsCollector) EnrichWithSchemaProperties() {
	var numShards uint64
	numFields := 0
	numIndexes := 0
	bsiFieldCount := 0
	timeQuantumEnabled := false

	for _, index := range d.server.holder.Indexes() {
		numShards += index.maxShard() + 1
		numIndexes += 1
		for _, field := range index.Fields() {
			numFields += 1
			if field.Type() == FieldTypeInt {
				bsiFieldCount += 1
			}
			if field.TimeQuantum() != "" {
				timeQuantumEnabled = true
			}
		}
	}

	d.Set("NumIndexes", numIndexes)
	d.Set("NumFields", numFields)
	d.Set("NumShards", numShards)
	d.Set("BSIFieldCount", bsiFieldCount)
	d.Set("TimeQuantumEnabled", timeQuantumEnabled)
}

// versionSegments returns the numeric segments of the version as a slice of ints.
func versionSegments(segments string) []int {
	segments = strings.Trim(segments, "v")
	segments = strings.Split(segments, "-")[0]
	s := strings.Split(segments, ".")
	segmentSlice := make([]int, len(s))
	for i, v := range s {
		segmentSlice[i], _ = strconv.Atoi(v)
	}
	return segmentSlice
}

// SystemInfo collects information about the host OS.
type SystemInfo interface {
	Uptime() (uint64, error)
	Platform() (string, error)
	Family() (string, error)
	OSVersion() (string, error)
	KernelVersion() (string, error)
	MemFree() (uint64, error)
	MemTotal() (uint64, error)
	MemUsed() (uint64, error)
}

// newNopSystemInfo creates a no-op implementation of SystemInfo.
func newNopSystemInfo() *NopSystemInfo {
	return &NopSystemInfo{}
}

// NopSystemInfo is a no-op implementation of SystemInfo.
type NopSystemInfo struct {
}

// Uptime is a no-op implementation of SystemInfo.Uptime.
func (n *NopSystemInfo) Uptime() (uint64, error) {
	return 0, nil
}

// Platform is a no-op implementation of SystemInfo.Platform.
func (n *NopSystemInfo) Platform() (string, error) {
	return "", nil
}

// Family is a no-op implementation of SystemInfo.Family.
func (n *NopSystemInfo) Family() (string, error) {
	return "", nil
}

// OSVersion is a no-op implementation of SystemInfo.OSVersion.
func (n *NopSystemInfo) OSVersion() (string, error) {
	return "", nil
}

// KernelVersion is a no-op implementation of SystemInfo.KernelVersion.
func (n *NopSystemInfo) KernelVersion() (string, error) {
	return "", nil
}

// MemFree is a no-op implementation of SystemInfo.MemFree.
func (n *NopSystemInfo) MemFree() (uint64, error) {
	return 0, nil
}

// MemTotal is a no-op implementation of SystemInfo.MemTotal.
func (n *NopSystemInfo) MemTotal() (uint64, error) {
	return 0, nil
}

// MemUsed is a no-op implementation of SystemInfo.MemUsed.
func (n *NopSystemInfo) MemUsed() (uint64, error) {
	return 0, nil
}
<commit_msg>Unexport NopSystemInfo<commit_after>// Copyright 2017 Pilosa Corp.
//
// 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 pilosa

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// Default version check URL.
const (
	defaultVersionCheckURL = "https://diagnostics.pilosa.com/v0/version"
)

type versionResponse struct {
	Version string `json:"version"`
	Message string `json:"message"`
}

// diagnosticsCollector represents a collector/sender of diagnostics data.
type diagnosticsCollector struct {
	mu          sync.Mutex
	host        string
	VersionURL  string
	version     string
	lastVersion string
	startTime   int64
	start       time.Time

	metrics map[string]interface{}

	client *http.Client

	Logger Logger

	server *Server
}

// newDiagnosticsCollector returns a new DiagnosticsCollector given an addr in the format "hostname:port".
func newDiagnosticsCollector(host string) *diagnosticsCollector {
	return &diagnosticsCollector{
		host:       host,
		VersionURL: defaultVersionCheckURL,
		startTime:  time.Now().Unix(),
		start:      time.Now(),
		client:     &http.Client{Timeout: 10 * time.Second},
		metrics:    make(map[string]interface{}),
		Logger:     NopLogger,
	}
}

// SetVersion of locally running Pilosa Cluster to check against master.
func (d *diagnosticsCollector) SetVersion(v string) {
	d.version = v
	d.Set("Version", v)
}

// Flush sends the current metrics.
func (d *diagnosticsCollector) Flush() error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.metrics["Uptime"] = (time.Now().Unix() - d.startTime)
	buf, err := d.encode()
	if err != nil {
		return errors.Wrap(err, "encoding")
	}
	req, err := http.NewRequest("POST", d.host, bytes.NewReader(buf))
	if err != nil {
		return errors.Wrap(err, "making new request")
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := d.client.Do(req)
	if err != nil {
		return errors.Wrap(err, "posting")
	}
	// Intentionally ignoring response body, as user does not need to be notified of error.
	defer resp.Body.Close()
	return nil
}

// CheckVersion of the local build against Pilosa master.
func (d *diagnosticsCollector) CheckVersion() error {
	var rsp versionResponse
	req, err := http.NewRequest("GET", d.VersionURL, nil)
	if err != nil {
		return errors.Wrap(err, "making request")
	}
	resp, err := d.client.Do(req)
	if err != nil {
		return errors.Wrap(err, "getting version")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("http: status=%d", resp.StatusCode)
	} else if err := json.NewDecoder(resp.Body).Decode(&rsp); err != nil {
		return fmt.Errorf("json decode: %s", err)
	}

	// If version has not changed since the last check, return
	if rsp.Version == d.lastVersion {
		return nil
	}

	d.lastVersion = rsp.Version
	if err := d.compareVersion(rsp.Version); err != nil {
		d.Logger.Printf("%s\n", err.Error())
	}

	return nil
}

// compareVersion check version strings.
func (d *diagnosticsCollector) compareVersion(value string) error {
	currentVersion := versionSegments(value)
	localVersion := versionSegments(d.version)

	if localVersion[0] < currentVersion[0] { //Major
		return fmt.Errorf("Warning: You are running Pilosa %s. A newer version (%s) is available: https://github.com/pilosa/pilosa/releases", d.version, value)
	} else if localVersion[1] < currentVersion[1] && localVersion[0] == currentVersion[0] { // Minor
		return fmt.Errorf("Warning: You are running Pilosa %s. The latest Minor release is %s: https://github.com/pilosa/pilosa/releases", d.version, value)
	} else if localVersion[2] < currentVersion[2] && localVersion[0] == currentVersion[0] && localVersion[1] == currentVersion[1] { // Patch
		return fmt.Errorf("There is a new patch release of Pilosa available: %s: https://github.com/pilosa/pilosa/releases", value)
	}

	return nil
}

// Encode metrics maps into the json message format.
func (d *diagnosticsCollector) encode() ([]byte, error) {
	return json.Marshal(d.metrics)
}

// Set adds a key value metric.
func (d *diagnosticsCollector) Set(name string, value interface{}) {
	switch v := value.(type) {
	case string:
		if v == "" {
			// Do not set empty string
			return
		}
	}
	d.mu.Lock()
	defer d.mu.Unlock()
	d.metrics[name] = value
}

// logErr logs the error and returns true if an error exists
func (d *diagnosticsCollector) logErr(err error) bool {
	if err != nil {
		d.Logger.Printf("%v", err)
		return true
	}
	return false
}

// EnrichWithOSInfo adds OS information to the diagnostics payload.
func (d *diagnosticsCollector) EnrichWithOSInfo() {
	uptime, err := d.server.systemInfo.Uptime()
	if !d.logErr(err) {
		d.Set("HostUptime", uptime)
	}
	platform, err := d.server.systemInfo.Platform()
	if !d.logErr(err) {
		d.Set("OSPlatform", platform)
	}
	family, err := d.server.systemInfo.Family()
	if !d.logErr(err) {
		d.Set("OSFamily", family)
	}
	version, err := d.server.systemInfo.OSVersion()
	if !d.logErr(err) {
		d.Set("OSVersion", version)
	}
	kernelVersion, err := d.server.systemInfo.KernelVersion()
	if !d.logErr(err) {
		d.Set("OSKernelVersion", kernelVersion)
	}
}

// EnrichWithMemoryInfo adds memory information to the diagnostics payload.
func (d *diagnosticsCollector) EnrichWithMemoryInfo() {
	memFree, err := d.server.systemInfo.MemFree()
	if !d.logErr(err) {
		d.Set("MemFree", memFree)
	}
	memTotal, err := d.server.systemInfo.MemTotal()
	if !d.logErr(err) {
		d.Set("MemTotal", memTotal)
	}
	memUsed, err := d.server.systemInfo.MemUsed()
	if !d.logErr(err) {
		d.Set("MemUsed", memUsed)
	}
}

// EnrichWithSchemaProperties adds schema info to the diagnostics payload.
func (d *diagnosticsCollector) EnrichWithSchemaProperties() {
	var numShards uint64
	numFields := 0
	numIndexes := 0
	bsiFieldCount := 0
	timeQuantumEnabled := false

	for _, index := range d.server.holder.Indexes() {
		numShards += index.maxShard() + 1
		numIndexes += 1
		for _, field := range index.Fields() {
			numFields += 1
			if field.Type() == FieldTypeInt {
				bsiFieldCount += 1
			}
			if field.TimeQuantum() != "" {
				timeQuantumEnabled = true
			}
		}
	}

	d.Set("NumIndexes", numIndexes)
	d.Set("NumFields", numFields)
	d.Set("NumShards", numShards)
	d.Set("BSIFieldCount", bsiFieldCount)
	d.Set("TimeQuantumEnabled", timeQuantumEnabled)
}

// versionSegments returns the numeric segments of the version as a slice of ints.
func versionSegments(segments string) []int {
	segments = strings.Trim(segments, "v")
	segments = strings.Split(segments, "-")[0]
	s := strings.Split(segments, ".")
	segmentSlice := make([]int, len(s))
	for i, v := range s {
		segmentSlice[i], _ = strconv.Atoi(v)
	}
	return segmentSlice
}

// SystemInfo collects information about the host OS.
type SystemInfo interface {
	Uptime() (uint64, error)
	Platform() (string, error)
	Family() (string, error)
	OSVersion() (string, error)
	KernelVersion() (string, error)
	MemFree() (uint64, error)
	MemTotal() (uint64, error)
	MemUsed() (uint64, error)
}

// newNopSystemInfo creates a no-op implementation of SystemInfo.
func newNopSystemInfo() *nopSystemInfo {
	return &nopSystemInfo{}
}

// nopSystemInfo is a no-op implementation of SystemInfo.
type nopSystemInfo struct {
}

// Uptime is a no-op implementation of SystemInfo.Uptime.
func (n *nopSystemInfo) Uptime() (uint64, error) {
	return 0, nil
}

// Platform is a no-op implementation of SystemInfo.Platform.
func (n *nopSystemInfo) Platform() (string, error) {
	return "", nil
}

// Family is a no-op implementation of SystemInfo.Family.
func (n *nopSystemInfo) Family() (string, error) {
	return "", nil
}

// OSVersion is a no-op implementation of SystemInfo.OSVersion.
func (n *nopSystemInfo) OSVersion() (string, error) {
	return "", nil
}

// KernelVersion is a no-op implementation of SystemInfo.KernelVersion.
func (n *nopSystemInfo) KernelVersion() (string, error) {
	return "", nil
}

// MemFree is a no-op implementation of SystemInfo.MemFree.
func (n *nopSystemInfo) MemFree() (uint64, error) {
	return 0, nil
}

// MemTotal is a no-op implementation of SystemInfo.MemTotal.
func (n *nopSystemInfo) MemTotal() (uint64, error) {
	return 0, nil
}

// MemUsed is a no-op implementation of SystemInfo.MemUsed.
func (n *nopSystemInfo) MemUsed() (uint64, error) {
	return 0, nil
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	"github.com/ryanuber/columnize"
	"strings"
)

type KeyCommand struct {
	Ui cli.Ui
}

func (c *KeyCommand) Help() string {
	helpText := `
Usage: serf keys [options]...

  Manipulates the internal encryption keyring used by Serf.

  To facilitate key rotation, Serf allows for multiple encryption keys to be in
  use simultaneously. Only one key, the "primary" key, will be used for
  encrypting messages. All other keys are used for decryption only.

  WARNING: Running with multiple encryption keys enabled is recommended as a
  transition state only. Performance may be impacted by using multiple keys.

Options:

  -install=<key>            Install a new key onto Serf's internal keyring.
  -use=<key>                Change the primary key used for encrypting messages.
  -remove=<key>             Remove a key from Serf's internal keyring.
  -rpc-addr=127.0.0.1:7373  RPC address of the Serf agent.
  -rpc-auth=""              RPC auth token of the Serf agent.
`
	return strings.TrimSpace(helpText)
}

func (c *KeyCommand) Run(args []string) int {
	var installKey, useKey, removeKey string
	var lines []string

	cmdFlags := flag.NewFlagSet("install-key", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.StringVar(&installKey, "install", "", "install a new key")
	cmdFlags.StringVar(&useKey, "use", "", "change primary encryption key")
	cmdFlags.StringVar(&removeKey, "remove", "", "remove a key")
	rpcAddr := RPCAddrFlag(cmdFlags)
	rpcAuth := RPCAuthFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if fmt.Sprintf("%s%s%s", installKey, useKey, removeKey) == "" {
		c.Ui.Error(c.Help())
		return 1
	}

	client, err := RPCClient(*rpcAddr, *rpcAuth)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Serf agent: %s", err))
		return 1
	}
	defer client.Close()

	if installKey != "" {
		if failedNodes, err := client.InstallKey(installKey); err != nil {
			for node, message := range failedNodes {
				lines = append(lines, fmt.Sprintf("failed: | %s | %s", node, message))
			}
			out, _ := columnize.SimpleFormat(lines)
			c.Ui.Error(out)
			c.Ui.Error("")
			c.Ui.Error(fmt.Sprintf("Error installing key: %s", err))
			return 1
		}
		c.Ui.Output("Successfully installed key")
	}

	if useKey != "" {
		if failedNodes, err := client.UseKey(useKey); err != nil {
			for node, message := range failedNodes {
				lines = append(lines, fmt.Sprintf("failed: | %s | %s", node, message))
			}
			out, _ := columnize.SimpleFormat(lines)
			c.Ui.Error(out)
			c.Ui.Error("")
			c.Ui.Error(fmt.Sprintf("Error installing key: %s", err))
			return 1
		}
		c.Ui.Output("Successfully changed primary key")
	}

	if removeKey != "" {
		if failedNodes, err := client.RemoveKey(removeKey); err != nil {
			for node, message := range failedNodes {
				lines = append(lines, fmt.Sprintf("failed: | %s | %s", node, message))
			}
			out, _ := columnize.SimpleFormat(lines)
			c.Ui.Error(out)
			c.Ui.Error("")
			c.Ui.Error(fmt.Sprintf("Error installing key: %s", err))
			return 1
		}
		c.Ui.Output("Successfully removed key")
	}

	return 0
}

func (c *KeyCommand) Synopsis() string {
	return "Manipulate the internal encryption keyring used by Serf"
}
<commit_msg>command: fixed key command name in metadata and help<commit_after>package command

import (
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	"github.com/ryanuber/columnize"
	"strings"
)

type KeyCommand struct {
	Ui cli.Ui
}

func (c *KeyCommand) Help() string {
	helpText := `
Usage: serf key [options]...

  Manipulates the internal encryption keyring used by Serf.

  To facilitate key rotation, Serf allows for multiple encryption keys to be in
  use simultaneously. Only one key, the "primary" key, will be used for
  encrypting messages. All other keys are used for decryption only.

  WARNING: Running with multiple encryption keys enabled is recommended as a
  transition state only. Performance may be impacted by using multiple keys.

Options:

  -install=<key>            Install a new key onto Serf's internal keyring.
  -use=<key>                Change the primary key used for encrypting messages.
  -remove=<key>             Remove a key from Serf's internal keyring.
  -rpc-addr=127.0.0.1:7373  RPC address of the Serf agent.
  -rpc-auth=""              RPC auth token of the Serf agent.
`
	return strings.TrimSpace(helpText)
}

func (c *KeyCommand) Run(args []string) int {
	var installKey, useKey, removeKey string
	var lines []string

	cmdFlags := flag.NewFlagSet("key", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.StringVar(&installKey, "install", "", "install a new key")
	cmdFlags.StringVar(&useKey, "use", "", "change primary encryption key")
	cmdFlags.StringVar(&removeKey, "remove", "", "remove a key")
	rpcAddr := RPCAddrFlag(cmdFlags)
	rpcAuth := RPCAuthFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if fmt.Sprintf("%s%s%s", installKey, useKey, removeKey) == "" {
		c.Ui.Error(c.Help())
		return 1
	}

	client, err := RPCClient(*rpcAddr, *rpcAuth)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Serf agent: %s", err))
		return 1
	}
	defer client.Close()

	if installKey != "" {
		if failedNodes, err := client.InstallKey(installKey); err != nil {
			for node, message := range failedNodes {
				lines = append(lines, fmt.Sprintf("failed: | %s | %s", node, message))
			}
			out, _ := columnize.SimpleFormat(lines)
			c.Ui.Error(out)
			c.Ui.Error("")
			c.Ui.Error(fmt.Sprintf("Error installing key: %s", err))
			return 1
		}
		c.Ui.Output("Successfully installed key")
	}

	if useKey != "" {
		if failedNodes, err := client.UseKey(useKey); err != nil {
			for node, message := range failedNodes {
				lines = append(lines, fmt.Sprintf("failed: | %s | %s", node, message))
			}
			out, _ := columnize.SimpleFormat(lines)
			c.Ui.Error(out)
			c.Ui.Error("")
			c.Ui.Error(fmt.Sprintf("Error installing key: %s", err))
			return 1
		}
		c.Ui.Output("Successfully changed primary key")
	}

	if removeKey != "" {
		if failedNodes, err := client.RemoveKey(removeKey); err != nil {
			for node, message := range failedNodes {
				lines = append(lines, fmt.Sprintf("failed: | %s | %s", node, message))
			}
			out, _ := columnize.SimpleFormat(lines)
			c.Ui.Error(out)
			c.Ui.Error("")
			c.Ui.Error(fmt.Sprintf("Error installing key: %s", err))
			return 1
		}
		c.Ui.Output("Successfully removed key")
	}

	return 0
}

func (c *KeyCommand) Synopsis() string {
	return "Manipulate the internal encryption keyring used by Serf"
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/tcnksm/gcli/skeleton"
	"github.com/tcnksm/go-gitconfig"
)

// NewCommand is a Command that generates a new cli project
type NewCommand struct {
	Meta
}

// Run generates a new cli project. It returns exit code
func (c *NewCommand) Run(args []string) int {

	var (
		commands     []*skeleton.Command
		flags        []*skeleton.Flag
		frameworkStr string
		owner        string
		staticDir    string
		vcsHost      string
		current      bool
		skipTest     bool
		verbose      bool
	)

	uflag := c.Meta.NewFlagSet("new", c.Help())

	uflag.Var((*CommandFlag)(&commands), "command", "command")
	uflag.Var((*CommandFlag)(&commands), "c", "command (short)")

	uflag.Var((*FlagFlag)(&flags), "flag", "flag")
	uflag.Var((*FlagFlag)(&flags), "f", "flag (short)")

	uflag.StringVar(&frameworkStr, "framework", defaultFrameworkString, "framework")
	uflag.StringVar(&frameworkStr, "F", defaultFrameworkString, "framework (short)")

	uflag.StringVar(&owner, "owner", "", "owner")
	uflag.StringVar(&owner, "o", "", "owner (short)")

	uflag.StringVar(&staticDir, "static-dir", "", "")

	uflag.StringVar(&vcsHost, "vcs", DefaultVCSHost, "")

	uflag.BoolVar(&current, "current", false, "current")
	uflag.BoolVar(&current, "C", false, "current")

	uflag.BoolVar(&skipTest, "skip-test", false, "skip-test")
	uflag.BoolVar(&skipTest, "T", false, "skip-test (short)")

	uflag.BoolVar(&verbose, "verbose", false, "verbose")
	uflag.BoolVar(&verbose, "V", false, "verbose (short)")

	if err := uflag.Parse(args); err != nil {
		return 1
	}

	parsedArgs := uflag.Args()
	if len(parsedArgs) != 1 {
		msg := fmt.Sprintf("Invalid arguments: %s", strings.Join(parsedArgs, " "))
		c.UI.Error(msg)
		return 1
	}

	name := parsedArgs[0]

	// If owner is not provided, use .gitconfig value.
	if owner == "" {
		var err error
		owner, err = gitconfig.GithubUser()
		if err != nil {
			owner, err = gitconfig.Username()
			if err != nil {
				msg := "Cannot find owner name\n" +
					"By default, owener name is retrieved from `~/.gitcofig` file.\n" +
					"Please set one via -owner option or `~/.gitconfig` file."
				c.UI.Error(msg)
				return 1
			}
		}
	}

	currentDir, err := os.Getwd()
	if err != nil {
		c.UI.Error(fmt.Sprintf(
			"Failed to get current directroy: %s", err))
		return ExitCodeFailed
	}

	gopath := os.Getenv(EnvGoPath)
	if gopath == "" {
		c.UI.Error(fmt.Sprintf(
			"Failed to read GOPATH: it should not be empty"))
		return ExitCodeFailed
	}
	idealDir := filepath.Join(gopath, "src", vcsHost, owner)

	output := name
	if currentDir != idealDir && !current {
		c.UI.Output("")
		c.UI.Output(fmt.Sprintf("====> WARNING: You are not in the directory gcli expects."))
		c.UI.Output(fmt.Sprintf("      The codes will be generated be in $GOPATH/src/%s/%s.", vcsHost, owner))
		c.UI.Output(fmt.Sprintf("      Not in the current directory. This is because the output"))
		c.UI.Output(fmt.Sprintf("      codes use import path based on that path."))
		c.UI.Output("")
		output = filepath.Join(idealDir, name)
	}

	if _, err := os.Stat(output); !os.IsNotExist(err) {
		msg := fmt.Sprintf("Cannot create directory %s: file exists", output)
		c.UI.Error(msg)
		return 1
	}

	framework, err := skeleton.FrameworkByName(frameworkStr)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Failed to generate %q: %s", name, err.Error()))
		return 1
	}

	if staticDir == "" {
		localDir, err := c.LocalDir()
		if err != nil {
			c.UI.Error(err.Error())
			return ExitCodeFailed
		}
		staticDir = filepath.Join(localDir, DefaultLocalStaticDir)
	}

	// Define Executable
	executable := &skeleton.Executable{
		Name:        name,
		Owner:       owner,
		VCSHost:     vcsHost,
		Commands:    commands,
		Flags:       flags,
		Version:     skeleton.DefaultVersion,
		Description: skeleton.DefaultDescription,
	}

	// Channels to receive artifact path (result) and error
	artifactCh, errCh := make(chan string), make(chan error)

	// Define Skeleton
	skeleton := &skeleton.Skeleton{
		Path:       output,
		StaticDir:  staticDir,
		Framework:  framework,
		SkipTest:   skipTest,
		Executable: executable,
		ArtifactCh: artifactCh,
		ErrCh:      errCh,
		Verbose:    verbose,
		LogWriter:  os.Stdout,
	}

	// Create project directory
	doneCh := skeleton.Generate()

	for {
		select {
		case artifact := <-artifactCh:
			c.UI.Output(fmt.Sprintf("  Created %s", artifact))
		case err := <-errCh:
			c.UI.Error(fmt.Sprintf("Failed to generate %s: %s", output, err.Error()))

			// If some file are created before error happend
			// Should be cleanuped
			if _, err := os.Stat(output); !os.IsNotExist(err) {
				c.UI.Output(fmt.Sprintf("Cleanup %s", output))
				os.RemoveAll(output)
			}
			return ExitCodeFailed
		case <-doneCh:
			c.UI.Info(fmt.Sprintf("====> Successfully generated %s", name))
			return ExitCodeOK
		}
	}
}

// Synopsis is a one-line, short synopsis of the command.
func (c *NewCommand) Synopsis() string {
	return "Generate new cli project"
}

// Help is a long-form help text that includes the command-line
// usage, a brief few sentences explaining the function of the command,
// and the complete list of flags the command accepts.
func (c *NewCommand) Help() string {
	helpText := `
Generate new cli skeleton project. At least, you must provide executable
name. You can select cli package and set commands via command line option.
See more about that on Options section. By default, gcli use codegangsta/cli.
To check cli framework you can use, run 'gcli list'.

Usage:

    gcli new [option] NAME

Options:

  -command=name, -c           Command name which you want to add.
                              This is valid only when cli pacakge support commands.
                              This can be specified multiple times. Synopsis can be
                              set after ":". Namely, you can specify command by
                              -command=NAME:SYNOPSYS. Only NAME is required.
                              You can set multiple variables at same time with ","
                              separator.

  -flag=name, -f              Global flag option name which you want to add.
                              This can be specified multiple times. By default, flag type
                              is string and its description is empty. You can set them,
                              with ":" separator. Namaly, you can specify flag by
                              -flag=NAME:TYPE:DESCIRPTION. Order must be flow  this and
                              TYPE must be string, bool or int. Only NAME is required.
                              You can set multiple variables at same time with ","
                              separator.

   -framework=name, -F        Cli framework name. By default, gcli use "codegangsta/cli"
                              To check cli framework you can use, run 'gcli list'.
                              If you set invalid framework, it will be failed.

   -owner=name, -o            Command owner (author) name. This value is also used for
                              import path name. By default, owner name is extracted from
                              ~/.gitconfig variable.

   -vcs=name                  Version Control Host name. By default, gcli use 'github.com'.

   -skip-test, -T             Skip generating *_test.go file. By default, gcli generates
                              test file If you specify this flag, gcli will not generate
                              test files.

Examples:

To create todo command application skeleton which has 'add' and 'delete' command,

   $ gcli new -command=add:"Add new task" -commnad=delete:"delete task" todo
`
	return strings.TrimSpace(helpText)
}
<commit_msg>parse appropriate gopath if defined multiple paths<commit_after>package command

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/tcnksm/gcli/skeleton"
	"github.com/tcnksm/go-gitconfig"
)

// NewCommand is a Command that generates a new cli project
type NewCommand struct {
	Meta
}

// Run generates a new cli project. It returns exit code
func (c *NewCommand) Run(args []string) int {

	var (
		commands     []*skeleton.Command
		flags        []*skeleton.Flag
		frameworkStr string
		owner        string
		staticDir    string
		vcsHost      string
		current      bool
		skipTest     bool
		verbose      bool
	)

	uflag := c.Meta.NewFlagSet("new", c.Help())

	uflag.Var((*CommandFlag)(&commands), "command", "command")
	uflag.Var((*CommandFlag)(&commands), "c", "command (short)")

	uflag.Var((*FlagFlag)(&flags), "flag", "flag")
	uflag.Var((*FlagFlag)(&flags), "f", "flag (short)")

	uflag.StringVar(&frameworkStr, "framework", defaultFrameworkString, "framework")
	uflag.StringVar(&frameworkStr, "F", defaultFrameworkString, "framework (short)")

	uflag.StringVar(&owner, "owner", "", "owner")
	uflag.StringVar(&owner, "o", "", "owner (short)")

	uflag.StringVar(&staticDir, "static-dir", "", "")

	uflag.StringVar(&vcsHost, "vcs", DefaultVCSHost, "")

	uflag.BoolVar(&current, "current", false, "current")
	uflag.BoolVar(&current, "C", false, "current")

	uflag.BoolVar(&skipTest, "skip-test", false, "skip-test")
	uflag.BoolVar(&skipTest, "T", false, "skip-test (short)")

	uflag.BoolVar(&verbose, "verbose", false, "verbose")
	uflag.BoolVar(&verbose, "V", false, "verbose (short)")

	if err := uflag.Parse(args); err != nil {
		return 1
	}

	parsedArgs := uflag.Args()
	if len(parsedArgs) != 1 {
		msg := fmt.Sprintf("Invalid arguments: %s", strings.Join(parsedArgs, " "))
		c.UI.Error(msg)
		return 1
	}

	name := parsedArgs[0]

	// If owner is not provided, use .gitconfig value.
	if owner == "" {
		var err error
		owner, err = gitconfig.GithubUser()
		if err != nil {
			owner, err = gitconfig.Username()
			if err != nil {
				msg := "Cannot find owner name\n" +
					"By default, owener name is retrieved from `~/.gitcofig` file.\n" +
					"Please set one via -owner option or `~/.gitconfig` file."
				c.UI.Error(msg)
				return 1
			}
		}
	}

	currentDir, err := os.Getwd()
	if err != nil {
		c.UI.Error(fmt.Sprintf(
			"Failed to get current directroy: %s", err))
		return ExitCodeFailed
	}

	gopaths := filepath.SplitList(os.Getenv(EnvGoPath))
	gopath := ""
	if len(gopaths) == 0 {
		c.UI.Error(fmt.Sprintf(
			"Failed to read GOPATH: it should not be empty"))
		return ExitCodeFailed
	} else {
		for _, path := range gopaths {
			absPath, err := filepath.Abs(path)
			if err != nil {
				c.UI.Error(fmt.Sprintf(
					"Cannot parse GOPATH"))
				continue
			}
			if strings.HasPrefix(currentDir, absPath) {
				gopath = absPath
				break
			}
		}
	}
	if gopath == "" {
		c.UI.Output("")
		c.UI.Output(fmt.Sprintf("===> WARNING: You are not in the directories defined in $GOPATH."))
		c.UI.Output(fmt.Sprintf("     Uses first location in $GOPATH."))
		c.UI.Output("")
		gopath, err = filepath.Abs(gopaths[0])
		if err != nil {
			c.UI.Error(fmt.Sprintf("Cannot parse GOPATH"))
			return ExitCodeFailed
		}
	}

	idealDir := filepath.Join(gopath, "src", vcsHost, owner)

	output := name
	if currentDir != idealDir && !current {
		c.UI.Output("")
		c.UI.Output(fmt.Sprintf("====> WARNING: You are not in the directory gcli expects."))
		c.UI.Output(fmt.Sprintf("      The codes will be generated be in $GOPATH/src/%s/%s.", vcsHost, owner))
		c.UI.Output(fmt.Sprintf("      Not in the current directory. This is because the output"))
		c.UI.Output(fmt.Sprintf("      codes use import path based on that path."))
		c.UI.Output("")
		output = filepath.Join(idealDir, name)
	}

	if _, err := os.Stat(output); !os.IsNotExist(err) {
		msg := fmt.Sprintf("Cannot create directory %s: file exists", output)
		c.UI.Error(msg)
		return 1
	}

	framework, err := skeleton.FrameworkByName(frameworkStr)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Failed to generate %q: %s", name, err.Error()))
		return 1
	}

	if staticDir == "" {
		localDir, err := c.LocalDir()
		if err != nil {
			c.UI.Error(err.Error())
			return ExitCodeFailed
		}
		staticDir = filepath.Join(localDir, DefaultLocalStaticDir)
	}

	// Define Executable
	executable := &skeleton.Executable{
		Name:        name,
		Owner:       owner,
		VCSHost:     vcsHost,
		Commands:    commands,
		Flags:       flags,
		Version:     skeleton.DefaultVersion,
		Description: skeleton.DefaultDescription,
	}

	// Channels to receive artifact path (result) and error
	artifactCh, errCh := make(chan string), make(chan error)

	// Define Skeleton
	skeleton := &skeleton.Skeleton{
		Path:       output,
		StaticDir:  staticDir,
		Framework:  framework,
		SkipTest:   skipTest,
		Executable: executable,
		ArtifactCh: artifactCh,
		ErrCh:      errCh,
		Verbose:    verbose,
		LogWriter:  os.Stdout,
	}

	// Create project directory
	doneCh := skeleton.Generate()

	for {
		select {
		case artifact := <-artifactCh:
			c.UI.Output(fmt.Sprintf("  Created %s", artifact))
		case err := <-errCh:
			c.UI.Error(fmt.Sprintf("Failed to generate %s: %s", output, err.Error()))

			// If some file are created before error happend
			// Should be cleanuped
			if _, err := os.Stat(output); !os.IsNotExist(err) {
				c.UI.Output(fmt.Sprintf("Cleanup %s", output))
				os.RemoveAll(output)
			}
			return ExitCodeFailed
		case <-doneCh:
			c.UI.Info(fmt.Sprintf("====> Successfully generated %s", name))
			return ExitCodeOK
		}
	}
}

// Synopsis is a one-line, short synopsis of the command.
func (c *NewCommand) Synopsis() string {
	return "Generate new cli project"
}

// Help is a long-form help text that includes the command-line
// usage, a brief few sentences explaining the function of the command,
// and the complete list of flags the command accepts.
func (c *NewCommand) Help() string {
	helpText := `
Generate new cli skeleton project. At least, you must provide executable
name. You can select cli package and set commands via command line option.
See more about that on Options section. By default, gcli use codegangsta/cli.
To check cli framework you can use, run 'gcli list'.

Usage:

    gcli new [option] NAME

Options:

  -command=name, -c           Command name which you want to add.
                              This is valid only when cli pacakge support commands.
                              This can be specified multiple times. Synopsis can be
                              set after ":". Namely, you can specify command by
                              -command=NAME:SYNOPSYS. Only NAME is required.
                              You can set multiple variables at same time with ","
                              separator.

  -flag=name, -f              Global flag option name which you want to add.
                              This can be specified multiple times. By default, flag type
                              is string and its description is empty. You can set them,
                              with ":" separator. Namaly, you can specify flag by
                              -flag=NAME:TYPE:DESCIRPTION. Order must be flow  this and
                              TYPE must be string, bool or int. Only NAME is required.
                              You can set multiple variables at same time with ","
                              separator.

   -framework=name, -F        Cli framework name. By default, gcli use "codegangsta/cli"
                              To check cli framework you can use, run 'gcli list'.
                              If you set invalid framework, it will be failed.

   -owner=name, -o            Command owner (author) name. This value is also used for
                              import path name. By default, owner name is extracted from
                              ~/.gitconfig variable.

   -vcs=name                  Version Control Host name. By default, gcli use 'github.com'.

   -skip-test, -T             Skip generating *_test.go file. By default, gcli generates
                              test file If you specify this flag, gcli will not generate
                              test files.

Examples:

To create todo command application skeleton which has 'add' and 'delete' command,

   $ gcli new -command=add:"Add new task" -commnad=delete:"delete task" todo
`
	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"errors"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/zetamatta/nyagos/shell"
)

var rxElse = regexp.MustCompile(`(?i)^\s*else`)

func cmd_if(ctx context.Context, cmd *shell.Cmd) (int, error) {
	// if "xxx" == "yyy"
	args := cmd.Args
	not := false
	start := 1

	option := map[string]struct{}{}

	for len(args) >= 2 && strings.HasPrefix(args[1], "/") {
		option[strings.ToLower(args[1])] = struct{}{}
		args = args[1:]
		start++
	}

	if len(args) >= 2 && strings.EqualFold(args[1], "not") {
		not = true
		args = args[1:]
		start++
	}
	status := false
	if len(args) >= 4 && args[2] == "==" {
		if _, ok := option["/i"]; ok {
			status = strings.EqualFold(args[1], args[3])
		} else {
			status = (args[1] == args[3])
		}
		args = args[4:]
		start += 3
	} else if len(args) >= 3 && strings.EqualFold(args[1], "exist") {
		_, err := os.Stat(args[2])
		status = (err == nil)
		args = args[3:]
		start += 2
	} else if len(args) >= 3 && strings.EqualFold(args[1], "errorlevel") {
		num, num_err := strconv.Atoi(args[2])
		if num_err == nil {
			status = (shell.LastErrorLevel >= num)
		}
		args = args[2:]
		start += 2
	}

	if not {
		status = !status
	}

	if len(args) > 0 {
		if status {
			subCmd, err := cmd.Clone()
			if err != nil {
				return 0, err
			}
			subCmd.Args = cmd.Args[start:]
			subCmd.RawArgs = cmd.RawArgs[start:]
			return subCmd.SpawnvpContext(ctx)
		}
	} else {
		stream, ok := ctx.Value("stream").(shell.Stream)
		if !ok {
			return 1, errors.New("not found stream")
		}
		thenBuffer := BufStream{}
		elseBuffer := BufStream{}
		elsePart := false

		save_prompt := os.Getenv("PROMPT")
		os.Setenv("PROMPT", "if>")
		nest := 1
		for {
			_, line, err := cmd.ReadCommand(ctx, stream)
			if err != nil {
				break
			}
			args := shell.SplitQ(line)
			name := strings.ToLower(args[0])
			if _, ok := start_list[name]; ok {
				nest++
			} else if name == "end" {
				nest--
				if nest == 0 {
					break
				}
			} else if name == "else" {
				if nest == 1 {
					elsePart = true
					os.Setenv("PROMPT", "else>")
					line = rxElse.ReplaceAllString(line, "")
				}
			}
			if elsePart {
				elseBuffer.Add(line)
			} else {
				thenBuffer.Add(line)
			}
		}
		os.Setenv("PROMPT", save_prompt)

		if status {
			cmd.Loop(&thenBuffer)
		} else {
			cmd.Loop(&elseBuffer)
		}
	}
	return 0, nil
}
<commit_msg>endif support<commit_after>package commands

import (
	"context"
	"errors"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/zetamatta/nyagos/shell"
)

var rxElse = regexp.MustCompile(`(?i)^\s*else`)

func cmd_if(ctx context.Context, cmd *shell.Cmd) (int, error) {
	// if "xxx" == "yyy"
	args := cmd.Args
	not := false
	start := 1

	option := map[string]struct{}{}

	for len(args) >= 2 && strings.HasPrefix(args[1], "/") {
		option[strings.ToLower(args[1])] = struct{}{}
		args = args[1:]
		start++
	}

	if len(args) >= 2 && strings.EqualFold(args[1], "not") {
		not = true
		args = args[1:]
		start++
	}
	status := false
	if len(args) >= 4 && args[2] == "==" {
		if _, ok := option["/i"]; ok {
			status = strings.EqualFold(args[1], args[3])
		} else {
			status = (args[1] == args[3])
		}
		args = args[4:]
		start += 3
	} else if len(args) >= 3 && strings.EqualFold(args[1], "exist") {
		_, err := os.Stat(args[2])
		status = (err == nil)
		args = args[3:]
		start += 2
	} else if len(args) >= 3 && strings.EqualFold(args[1], "errorlevel") {
		num, num_err := strconv.Atoi(args[2])
		if num_err == nil {
			status = (shell.LastErrorLevel >= num)
		}
		args = args[2:]
		start += 2
	}

	if not {
		status = !status
	}

	if len(args) > 0 {
		if status {
			subCmd, err := cmd.Clone()
			if err != nil {
				return 0, err
			}
			subCmd.Args = cmd.Args[start:]
			subCmd.RawArgs = cmd.RawArgs[start:]
			return subCmd.SpawnvpContext(ctx)
		}
	} else {
		stream, ok := ctx.Value("stream").(shell.Stream)
		if !ok {
			return 1, errors.New("not found stream")
		}
		thenBuffer := BufStream{}
		elseBuffer := BufStream{}
		elsePart := false

		save_prompt := os.Getenv("PROMPT")
		os.Setenv("PROMPT", "if>")
		nest := 1
		for {
			_, line, err := cmd.ReadCommand(ctx, stream)
			if err != nil {
				break
			}
			args := shell.SplitQ(line)
			name := strings.ToLower(args[0])
			if _, ok := start_list[name]; ok {
				nest++
			} else if name == "end" || name == "endif" {
				nest--
				if nest == 0 {
					break
				}
			} else if name == "else" {
				if nest == 1 {
					elsePart = true
					os.Setenv("PROMPT", "else>")
					line = rxElse.ReplaceAllString(line, "")
				}
			}
			if elsePart {
				elseBuffer.Add(line)
			} else {
				thenBuffer.Add(line)
			}
		}
		os.Setenv("PROMPT", save_prompt)

		if status {
			cmd.Loop(&thenBuffer)
		} else {
			cmd.Loop(&elseBuffer)
		}
	}
	return 0, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tiff

type TiffType uint16

const (
	TiffType_ClassicTIFF TiffType = 42
	TiffType_BigTIFF     TiffType = 43
)

func (p TiffType) Valid() bool {
	return p == TiffType_ClassicTIFF || p == TiffType_BigTIFF
}

type ImageType uint16

const (
	ImageType_Nil ImageType = iota
	ImageType_Bilevel
	ImageType_BilevelInvert
	ImageType_Paletted
	ImageType_Gray
	ImageType_GrayInvert
	ImageType_RGB
	ImageType_RGBA
	ImageType_NRGBA
)

type CompressType uint16

// Compression types (defined in various places in the spec and supplements).
const (
	CompressType_Nil        CompressType = 0     //
	CompressType_None       CompressType = 1     //
	CompressType_CCITT      CompressType = 2     //
	CompressType_G3         CompressType = 3     // Group 3 Fax.
	CompressType_G4         CompressType = 4     // Group 4 Fax.
	CompressType_LZW        CompressType = 5     //
	CompressType_JPEGOld    CompressType = 6     // Superseded by cJPEG.
	CompressType_JPEG       CompressType = 7     //
	CompressType_Deflate    CompressType = 8     // zlib compression.
	CompressType_PackBits   CompressType = 32773 //
	CompressType_DeflateOld CompressType = 32946 // Superseded by cDeflate.
)

type DataType uint16

// Data types (p. 14-16 of the spec).
const (
	DataType_Nil       DataType = iota //  0, invalid
	DataType_Byte                      //  1
	DataType_ASCII                     //  2
	DataType_Short                     //  3
	DataType_Long                      //  4
	DataType_Rational                  //  5
	DataType_SByte                     //  6
	DataType_Undefined                 //  7
	DataType_SShort                    //  8
	DataType_SLong                     //  9
	DataType_SRational                 // 10
	DataType_Float                     // 11
	DataType_Double                    // 12
	DataType_IFD                       // 13
	DataType_Unicode                   // 14
	DataType_Complex                   // 15
	DataType_Long8                     // 16
	DataType_SLong8                    // 17
	DataType_IFD8                      // 18
)

func (d DataType) Valid() bool {
	if d <= DataType_Nil || d > DataType_IFD8 {
		return false
	}
	return true
}

func (d DataType) IsIntType() bool {
	switch d {
	case DataType_Byte, DataType_Short, DataType_Long:
		return true
	case DataType_SByte, DataType_SShort, DataType_SLong:
		return true
	}
	return false
}
func (d DataType) IsFloatType() bool {
	switch d {
	case DataType_Float, DataType_Double:
		return true
	}
	return false
}
func (d DataType) IsRationalType() bool {
	switch d {
	case DataType_Rational, DataType_SRational:
		return true
	}
	return false
}
func (d DataType) IsStringType() bool {
	switch d {
	case DataType_ASCII, DataType_Unicode:
		return true
	}
	return false
}

func (d DataType) ByteSize() int {
	switch d {
	case DataType_Byte:
		return 1
	case DataType_ASCII:
		return 1
	case DataType_Short:
		return 2
	case DataType_Long:
		return 4
	case DataType_Rational:
		return 8
	case DataType_SByte:
		return 1
	case DataType_Undefined:
		return 1
	case DataType_SShort:
		return 2
	case DataType_SLong:
		return 4
	case DataType_SRational:
		return 8
	case DataType_Float:
		return 4
	case DataType_Double:
		return 8
	case DataType_IFD:
		return 4 // LONG
	case DataType_Unicode:
		return 2 // UTF16 ?
	case DataType_Complex:
		return 8
	case DataType_Long8:
		return 8
	case DataType_SLong8:
		return 8
	case DataType_IFD8:
		return 8 // LONG8
	}
	return 0
}

type TagType uint16

// Tags (see p. 28-41 of the spec).
const (
	_                                  TagType = 0     // Type(A/B/C/*), Num(1/*), Required, # comment
	TagType_NewSubfileType             TagType = 254   // LONG , 1, # Default=0
	TagType_SubfileType                TagType = 255   // SHORT, 1,
	TagType_ImageWidth                 TagType = 256   // SHORT/LONG/LONG8, 1, # Required
	TagType_ImageLength                TagType = 257   // SHORT/LONG/LONG8, 1, # Required
	TagType_BitsPerSample              TagType = 258   // SHORT, *, # Default=1. See SamplesPerPixel
	TagType_Compression                TagType = 259   // SHORT, 1, # Default=1
	TagType_PhotometricInterpretation  TagType = 262   // SHORT, 1,
	TagType_Threshholding              TagType = 263   // SHORT, 1, # Default=1
	TagType_CellWidth                  TagType = 264   // SHORT, 1,
	TagType_CellLenght                 TagType = 265   // SHORT, 1,
	TagType_FillOrder                  TagType = 266   // SHORT, 1, # Default=1
	TagType_DocumentName               TagType = 269   // ASCII
	TagType_ImageDescription           TagType = 270   // ASCII
	TagType_Make                       TagType = 271   // ASCII
	TagType_Model                      TagType = 272   // ASCII
	TagType_StripOffsets               TagType = 273   // SHORT/LONG/LONG8, *, # StripsPerImage
	TagType_Orientation                TagType = 274   // SHORT, 1, # Default=1
	TagType_SamplesPerPixel            TagType = 277   // SHORT, 1, # Default=1
	TagType_RowsPerStrip               TagType = 278   // SHORT/LONG/LONG8, 1,
	TagType_StripByteCounts            TagType = 279   // SHORT/LONG/LONG8, *, # StripsPerImage
	TagType_MinSampleValue             TagType = 280   // SHORT,    *, # Default=0
	TagType_MaxSampleValue             TagType = 281   // SHORT,    *, # Default=2^BitsPerSample-1
	TagType_XResolution                TagType = 282   // RATIONAL, 1, # Required?
	TagType_YResolution                TagType = 283   // RATIONAL, 1, # Required?
	TagType_PlanarConfiguration        TagType = 284   // SHORT,    1, # Defaule=1
	TagType_PageName                   TagType = 285   // ASCII
	TagType_XPosition                  TagType = 286   // RATIONAL,   1
	TagType_YPosition                  TagType = 287   // RATIONAL,   1
	TagType_FreeOffsets                TagType = 288   // LONG/LONG8, *
	TagType_FreeByteCounts             TagType = 289   // LONG/LONG8, *
	TagType_GrayResponseUnit           TagType = 290   // SHORT, 1,
	TagType_GrayResponseCurve          TagType = 291   // SHORT, *, # 2**BitPerSample
	TagType_T4Options                  TagType = 292   // LONG,  1, # Default=0
	TagType_T6Options                  TagType = 293   // LONG,  1, # Default=0
	TagType_ResolutionUnit             TagType = 296   // SHORT, 1, # Default=2
	TagType_PageNumber                 TagType = 297   // SHORT, 2,
	TagType_TransferFunction           TagType = 301   // SHORT, *, # {1 or SamplesPerPixel}*2**BitPerSample
	TagType_Software                   TagType = 305   // ASCII
	TagType_DateTime                   TagType = 306   // ASCII, 20, # YYYY:MM:DD HH:MM:SS, include NULL
	TagType_Artist                     TagType = 315   // ASCII
	TagType_HostComputer               TagType = 316   // ASCII
	TagType_Predictor                  TagType = 317   // SHORT, 1, # Default=1
	TagType_WhitePoint                 TagType = 318   // RATIONAL, 2
	TagType_PrimaryChromaticities      TagType = 319   // RATIONAL, 6
	TagType_ColorMap                   TagType = 320   // SHORT, *, # 3*(2**BitPerSample)
	TagType_HalftoneHints              TagType = 321   // SHORT, 2
	TagType_TileWidth                  TagType = 322   // SHORT/LONG, 1
	TagType_TileLength                 TagType = 323   // SHORT/LONG, 1
	TagType_TileOffsets                TagType = 324   // LONG/LONG8, *, # TilesPerImage
	TagType_TileByteCounts             TagType = 325   // SHORT/LONG, *, # TilesPerImage
	TagType_SubIFD                     TagType = 330   // LONG,  *
	TagType_InkSet                     TagType = 332   // SHORT, 1, # Default=1
	TagType_InkNames                   TagType = 333   // ASCII
	TagType_NumberOfInks               TagType = 334   // SHORT, 1, # Default=4
	TagType_DotRange                   TagType = 336   // BYTE/SHORT, # Default=[0,2^BitsPerSample-1]
	TagType_TargetPrinter              TagType = 337   // ASCII
	TagType_ExtraSamples               TagType = 338   // BYTE,  1,
	TagType_SampleFormat               TagType = 339   // SHORT, *, # SamplesPerPixel. Default=1
	TagType_SMinSampleValue            TagType = 340   // *,     *, # SamplesPerPixel, try double
	TagType_SMaxSampleValue            TagType = 341   // *,     *, # SamplesPerPixel, try double
	TagType_TransferRange              TagType = 342   // SHORT, 6,
	TagType_JPEGProc                   TagType = 512   // SHORT, 1,
	TagType_JPEGInterchangeFormat      TagType = 513   // LONG,  1,
	TagType_JPEGInterchangeFormatLngth TagType = 514   // LONG,  1,
	TagType_JPEGRestartInterval        TagType = 515   // SHORT, 1,
	TagType_JPEGLosslessPredictors     TagType = 517   // SHORT, *, # SamplesPerPixel
	TagType_JPEGPointTransforms        TagType = 518   // SHORT, *, # SamplesPerPixel
	TagType_JPEGQTables                TagType = 519   // LONG,  *, # SamplesPerPixel
	TagType_JPEGDCTables               TagType = 520   // LONG,  *, # SamplesPerPixel
	TagType_JPEGACTables               TagType = 521   // LONG,  *, # SamplesPerPixel
	TagType_YCbCrCoefficients          TagType = 529   // RATIONAL, 3
	TagType_YCbCrSubSampling           TagType = 530   // SHORT, 2, # Default=[2,2]
	TagType_YCbCrPositioning           TagType = 531   // SHORT, 1, # Default=1
	TagType_ReferenceBlackWhite        TagType = 532   // LONG , *, # 2*SamplesPerPixel
	TagType_Copyright                  TagType = 33432 // ASCII
)

const (
	TagType_GeoKeyDirectoryTag     TagType = 34735 // SHORT, *, # >= 4
	TagType_GeoDoubleParamsTag     TagType = 34736 // DOUBLE
	TagType_GeoAsciiParamsTag      TagType = 34737 // ASCII
	TagType_ModelTiepointTag       TagType = 33922 // DOUBLE
	TagType_ModelPixelScaleTag     TagType = 33550 // DOUBLE
	TagType_ModelTransformationTag TagType = 34264 // DOUBLE, 16
	TagType_IntergraphMatrixTag    TagType = 33920 // DOUBLE, 17
)

type TagValue_PhotometricType uint16

const (
	TagValue_PhotometricType_WhiteIsZero TagValue_PhotometricType = 0
	TagValue_PhotometricType_BlackIsZero TagValue_PhotometricType = 1
	TagValue_PhotometricType_RGB         TagValue_PhotometricType = 2
	TagValue_PhotometricType_Paletted    TagValue_PhotometricType = 3
	TagValue_PhotometricType_TransMask   TagValue_PhotometricType = 4 // transparency mask
	TagValue_PhotometricType_CMYK        TagValue_PhotometricType = 5
	TagValue_PhotometricType_YCbCr       TagValue_PhotometricType = 6
	TagValue_PhotometricType_CIELab      TagValue_PhotometricType = 8
)

type TagValue_PredictorType uint16

const (
	TagValue_PredictorType_None       TagValue_PredictorType = 1
	TagValue_PredictorType_Horizontal TagValue_PredictorType = 2
)

type TagValue_ResolutionUnitType uint16

const (
	TagValue_ResolutionUnitType_None    TagValue_ResolutionUnitType = 1
	TagValue_ResolutionUnitType_PerInch TagValue_ResolutionUnitType = 2 // Dots per inch.
	TagValue_ResolutionUnitType_PerCM   TagValue_ResolutionUnitType = 3 // Dots per centimeter.
)
<commit_msg>more TagValue type<commit_after>// Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tiff

type TiffType uint16

const (
	TiffType_ClassicTIFF TiffType = 42
	TiffType_BigTIFF     TiffType = 43
)

func (p TiffType) Valid() bool {
	return p == TiffType_ClassicTIFF || p == TiffType_BigTIFF
}

type ImageType uint16

const (
	ImageType_Nil ImageType = iota
	ImageType_Bilevel
	ImageType_BilevelInvert
	ImageType_Paletted
	ImageType_Gray
	ImageType_GrayInvert
	ImageType_RGB
	ImageType_RGBA
	ImageType_NRGBA
)

type CompressType TagType

// Compression types (defined in various places in the spec and supplements).
const (
	CompressType_Nil        CompressType = 0     //
	CompressType_None       CompressType = 1     //
	CompressType_CCITT      CompressType = 2     //
	CompressType_G3         CompressType = 3     // Group 3 Fax.
	CompressType_G4         CompressType = 4     // Group 4 Fax.
	CompressType_LZW        CompressType = 5     //
	CompressType_JPEGOld    CompressType = 6     // Superseded by cJPEG.
	CompressType_JPEG       CompressType = 7     //
	CompressType_Deflate    CompressType = 8     // zlib compression.
	CompressType_PackBits   CompressType = 32773 //
	CompressType_DeflateOld CompressType = 32946 // Superseded by cDeflate.
)

type DataType uint16

// Data types (p. 14-16 of the spec).
const (
	DataType_Nil       DataType = iota //  0, invalid
	DataType_Byte                      //  1
	DataType_ASCII                     //  2
	DataType_Short                     //  3
	DataType_Long                      //  4
	DataType_Rational                  //  5
	DataType_SByte                     //  6
	DataType_Undefined                 //  7
	DataType_SShort                    //  8
	DataType_SLong                     //  9
	DataType_SRational                 // 10
	DataType_Float                     // 11
	DataType_Double                    // 12
	DataType_IFD                       // 13
	DataType_Unicode                   // 14
	DataType_Complex                   // 15
	DataType_Long8                     // 16
	DataType_SLong8                    // 17
	DataType_IFD8                      // 18
)

func (d DataType) Valid() bool {
	if d <= DataType_Nil || d > DataType_IFD8 {
		return false
	}
	return true
}

func (d DataType) IsIntType() bool {
	switch d {
	case DataType_Byte, DataType_Short, DataType_Long:
		return true
	case DataType_SByte, DataType_SShort, DataType_SLong:
		return true
	}
	return false
}
func (d DataType) IsFloatType() bool {
	switch d {
	case DataType_Float, DataType_Double:
		return true
	}
	return false
}
func (d DataType) IsRationalType() bool {
	switch d {
	case DataType_Rational, DataType_SRational:
		return true
	}
	return false
}
func (d DataType) IsStringType() bool {
	switch d {
	case DataType_ASCII, DataType_Unicode:
		return true
	}
	return false
}

func (d DataType) ByteSize() int {
	switch d {
	case DataType_Byte:
		return 1
	case DataType_ASCII:
		return 1
	case DataType_Short:
		return 2
	case DataType_Long:
		return 4
	case DataType_Rational:
		return 8
	case DataType_SByte:
		return 1
	case DataType_Undefined:
		return 1
	case DataType_SShort:
		return 2
	case DataType_SLong:
		return 4
	case DataType_SRational:
		return 8
	case DataType_Float:
		return 4
	case DataType_Double:
		return 8
	case DataType_IFD:
		return 4 // LONG
	case DataType_Unicode:
		return 2 // UTF16 ?
	case DataType_Complex:
		return 8
	case DataType_Long8:
		return 8
	case DataType_SLong8:
		return 8
	case DataType_IFD8:
		return 8 // LONG8
	}
	return 0
}

type TagType uint16

// Tags (see p. 28-41 of the spec).
const (
	_                                  TagType = 0     // Type(A/B/C/*), Num(1/*), Required, # comment
	TagType_NewSubfileType             TagType = 254   // LONG , 1, # Default=0
	TagType_SubfileType                TagType = 255   // SHORT, 1,
	TagType_ImageWidth                 TagType = 256   // SHORT/LONG/LONG8, 1, # Required
	TagType_ImageLength                TagType = 257   // SHORT/LONG/LONG8, 1, # Required
	TagType_BitsPerSample              TagType = 258   // SHORT, *, # Default=1. See SamplesPerPixel
	TagType_Compression                TagType = 259   // SHORT, 1, # Default=1
	TagType_PhotometricInterpretation  TagType = 262   // SHORT, 1,
	TagType_Threshholding              TagType = 263   // SHORT, 1, # Default=1
	TagType_CellWidth                  TagType = 264   // SHORT, 1,
	TagType_CellLenght                 TagType = 265   // SHORT, 1,
	TagType_FillOrder                  TagType = 266   // SHORT, 1, # Default=1
	TagType_DocumentName               TagType = 269   // ASCII
	TagType_ImageDescription           TagType = 270   // ASCII
	TagType_Make                       TagType = 271   // ASCII
	TagType_Model                      TagType = 272   // ASCII
	TagType_StripOffsets               TagType = 273   // SHORT/LONG/LONG8, *, # StripsPerImage
	TagType_Orientation                TagType = 274   // SHORT, 1, # Default=1
	TagType_SamplesPerPixel            TagType = 277   // SHORT, 1, # Default=1
	TagType_RowsPerStrip               TagType = 278   // SHORT/LONG/LONG8, 1,
	TagType_StripByteCounts            TagType = 279   // SHORT/LONG/LONG8, *, # StripsPerImage
	TagType_MinSampleValue             TagType = 280   // SHORT,    *, # Default=0
	TagType_MaxSampleValue             TagType = 281   // SHORT,    *, # Default=2^BitsPerSample-1
	TagType_XResolution                TagType = 282   // RATIONAL, 1, # Required?
	TagType_YResolution                TagType = 283   // RATIONAL, 1, # Required?
	TagType_PlanarConfiguration        TagType = 284   // SHORT,    1, # Defaule=1
	TagType_PageName                   TagType = 285   // ASCII
	TagType_XPosition                  TagType = 286   // RATIONAL,   1
	TagType_YPosition                  TagType = 287   // RATIONAL,   1
	TagType_FreeOffsets                TagType = 288   // LONG/LONG8, *
	TagType_FreeByteCounts             TagType = 289   // LONG/LONG8, *
	TagType_GrayResponseUnit           TagType = 290   // SHORT, 1,
	TagType_GrayResponseCurve          TagType = 291   // SHORT, *, # 2**BitPerSample
	TagType_T4Options                  TagType = 292   // LONG,  1, # Default=0
	TagType_T6Options                  TagType = 293   // LONG,  1, # Default=0
	TagType_ResolutionUnit             TagType = 296   // SHORT, 1, # Default=2
	TagType_PageNumber                 TagType = 297   // SHORT, 2,
	TagType_TransferFunction           TagType = 301   // SHORT, *, # {1 or SamplesPerPixel}*2**BitPerSample
	TagType_Software                   TagType = 305   // ASCII
	TagType_DateTime                   TagType = 306   // ASCII, 20, # YYYY:MM:DD HH:MM:SS, include NULL
	TagType_Artist                     TagType = 315   // ASCII
	TagType_HostComputer               TagType = 316   // ASCII
	TagType_Predictor                  TagType = 317   // SHORT, 1, # Default=1
	TagType_WhitePoint                 TagType = 318   // RATIONAL, 2
	TagType_PrimaryChromaticities      TagType = 319   // RATIONAL, 6
	TagType_ColorMap                   TagType = 320   // SHORT, *, # 3*(2**BitPerSample)
	TagType_HalftoneHints              TagType = 321   // SHORT, 2
	TagType_TileWidth                  TagType = 322   // SHORT/LONG, 1
	TagType_TileLength                 TagType = 323   // SHORT/LONG, 1
	TagType_TileOffsets                TagType = 324   // LONG/LONG8, *, # TilesPerImage
	TagType_TileByteCounts             TagType = 325   // SHORT/LONG, *, # TilesPerImage
	TagType_SubIFD                     TagType = 330   // LONG,  *
	TagType_InkSet                     TagType = 332   // SHORT, 1, # Default=1
	TagType_InkNames                   TagType = 333   // ASCII
	TagType_NumberOfInks               TagType = 334   // SHORT, 1, # Default=4
	TagType_DotRange                   TagType = 336   // BYTE/SHORT, # Default=[0,2^BitsPerSample-1]
	TagType_TargetPrinter              TagType = 337   // ASCII
	TagType_ExtraSamples               TagType = 338   // BYTE,  1,
	TagType_SampleFormat               TagType = 339   // SHORT, *, # SamplesPerPixel. Default=1
	TagType_SMinSampleValue            TagType = 340   // *,     *, # SamplesPerPixel, try double
	TagType_SMaxSampleValue            TagType = 341   // *,     *, # SamplesPerPixel, try double
	TagType_TransferRange              TagType = 342   // SHORT, 6,
	TagType_JPEGProc                   TagType = 512   // SHORT, 1,
	TagType_JPEGInterchangeFormat      TagType = 513   // LONG,  1,
	TagType_JPEGInterchangeFormatLngth TagType = 514   // LONG,  1,
	TagType_JPEGRestartInterval        TagType = 515   // SHORT, 1,
	TagType_JPEGLosslessPredictors     TagType = 517   // SHORT, *, # SamplesPerPixel
	TagType_JPEGPointTransforms        TagType = 518   // SHORT, *, # SamplesPerPixel
	TagType_JPEGQTables                TagType = 519   // LONG,  *, # SamplesPerPixel
	TagType_JPEGDCTables               TagType = 520   // LONG,  *, # SamplesPerPixel
	TagType_JPEGACTables               TagType = 521   // LONG,  *, # SamplesPerPixel
	TagType_YCbCrCoefficients          TagType = 529   // RATIONAL, 3
	TagType_YCbCrSubSampling           TagType = 530   // SHORT, 2, # Default=[2,2]
	TagType_YCbCrPositioning           TagType = 531   // SHORT, 1, # Default=1
	TagType_ReferenceBlackWhite        TagType = 532   // LONG , *, # 2*SamplesPerPixel
	TagType_Copyright                  TagType = 33432 // ASCII
)

const (
	TagType_GeoKeyDirectoryTag     TagType = 34735 // SHORT, *, # >= 4
	TagType_GeoDoubleParamsTag     TagType = 34736 // DOUBLE
	TagType_GeoAsciiParamsTag      TagType = 34737 // ASCII
	TagType_ModelTiepointTag       TagType = 33922 // DOUBLE
	TagType_ModelPixelScaleTag     TagType = 33550 // DOUBLE
	TagType_ModelTransformationTag TagType = 34264 // DOUBLE, 16
	TagType_IntergraphMatrixTag    TagType = 33920 // DOUBLE, 17
)

// subfile data descriptor
type TagValue_NewSubfileType TagType

const (
	TagValue_NewSubfileType_ReducedImage TagValue_NewSubfileType = 1 // reduced resolution version
	TagValue_NewSubfileType_Page         TagValue_NewSubfileType = 2 // one page of many
	TagValue_NewSubfileType_Mask         TagValue_NewSubfileType = 4 // transparency mask
)

// kind of data in subfile
type TagValue_SubfileType TagType

const (
	TagValue_SubfileType_Image        TagValue_SubfileType = 1 // full resolution image data
	TagValue_SubfileType_ReducedImage TagValue_SubfileType = 2 // reduced size image data
	TagValue_SubfileType_Page         TagValue_SubfileType = 3 // one page of many
)

type TagValue_PhotometricType TagType

const (
	TagValue_PhotometricType_WhiteIsZero TagValue_PhotometricType = 0
	TagValue_PhotometricType_BlackIsZero TagValue_PhotometricType = 1
	TagValue_PhotometricType_RGB         TagValue_PhotometricType = 2
	TagValue_PhotometricType_Paletted    TagValue_PhotometricType = 3
	TagValue_PhotometricType_TransMask   TagValue_PhotometricType = 4 // transparency mask
	TagValue_PhotometricType_CMYK        TagValue_PhotometricType = 5
	TagValue_PhotometricType_YCbCr       TagValue_PhotometricType = 6
	TagValue_PhotometricType_CIELab      TagValue_PhotometricType = 8
)

type TagValue_PredictorType TagType

const (
	TagValue_PredictorType_None       TagValue_PredictorType = 1
	TagValue_PredictorType_Horizontal TagValue_PredictorType = 2
)

type TagValue_ResolutionUnitType TagType

const (
	TagValue_ResolutionUnitType_None    TagValue_ResolutionUnitType = 1
	TagValue_ResolutionUnitType_PerInch TagValue_ResolutionUnitType = 2 // Dots per inch.
	TagValue_ResolutionUnitType_PerCM   TagValue_ResolutionUnitType = 3 // Dots per centimeter.
)
<|endoftext|>


<commit_before>// +build darwin

package main

// #cgo CFLAGS: -I${SRCDIR}/vendor/xhyve/include -x c -std=c11 -fno-common -arch x86_64 -DXHYVE_CONFIG_ASSERT -lxhyve -Os -fstrict-aliasing -Weverything -Wno-unknown-warning-option -Wno-reserved-id-macro -pedantic -fmessage-length=152 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0
// #cgo LDFLAGS: -L${SRCDIR} -lxhyve -arch x86_64 -framework Hypervisor -framework vmnet -force_load ${SRCDIR}/libxhyve.a
// #include "helper.h"
import "C"

// -Os -flto -fstrict-aliasing -Weverything -Werror -Wno-unknown-warning-option -Wno-reserved-id-macro -pedantic -fmessage-length=152 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -fcolor-diagnostics
// -Xlinker -object_path_lto
import (
	"errors"
	"fmt"
	"runtime"
	"strconv"
	"unsafe"

	"github.com/satori/go.uuid"
)

var (
	// ErrPCIDevice is returned when an error was found parsing PCI devices.
	ErrPCIDevice = errors.New("error parsing PCI device")
	// ErrLPCDevice is returned when an error was found parsing LPC device options.
	ErrLPCDevice = errors.New("error parsing LPC devices")
	// ErrInvalidMemsize is returned if memorize size is invalid.
	ErrInvalidMemsize = errors.New("invalid memory size")
	// ErrInvalidBootParams is returne when kexec or fbsd params are invalid.
	ErrInvalidBootParams = errors.New("boot parameters are invalid")
	// ErrCreatingVM is returned when xhyve was unable to create the virtual machine.
	ErrCreatingVM = errors.New("unable to create VM")
	// ErrMaxNumVCPUExceeded is returned when the number of vcpus requested for the guest
	// exceeds the limit imposed by xhyve.
	ErrMaxNumVCPUExceeded = errors.New("maximum number of vcpus requested is too high")
	// ErrSettingUpMemory is returned when an error was returned by xhyve when trying
	// to setup guest memory.
	ErrSettingUpMemory = errors.New("unable to setup memory for guest vm")
	// ErrInitializingMSR is returned when xhyve is unable to initialize MSR table
	ErrInitializingMSR = errors.New("unable to initialize MSR table")
	// ErrInitializingPCI is returned when xhyve is unable to initialize PCI emulation
	ErrInitializingPCI = errors.New("unable to initialize PCI emulation")
	// ErrBuildingMPTTable is returned when xhyve is unable to build MPT table
	ErrBuildingMPTTable = errors.New("unable to build MPT table")
	// ErrBuildingSMBIOS is returned when xhyve is unable to build smbios
	ErrBuildingSMBIOS = errors.New("unable to build smbios")
	// ErrBuildingACPI is returned when xhyve is unable to build ACPI
	ErrBuildingACPI = errors.New("unable to build ACPI")
)

// XHyveParams defines parameters needed by xhyve to boot up virtual machines.
type XHyveParams struct {
	// Number of CPUs to assigned to the guest vm.
	VCPUs int
	// Memory in megabytes to assign to guest vm.
	Memory string
	// PCI devices to attach to the guest vm, including bus and slot.
	// Example: []string{"2:0,virtio-net", "0:0,hostbridge"}
	PCIDevs []string
	// LPC devices to attach to the guest vm.
	LPCDevs string // -l com1,stdio
	// Whether to create ACPI tables or not.
	ACPI *bool
	// Universal identifier for the guest vm.
	UUID string
	// Whether to use localtime or UTC in Real Time Clock.
	RTCLocaltime *bool
	// Either kexec or fbsd params. Format:
	// kexec,kernel image,initrd,"cmdline"
	// fbsd,userboot,boot volume,"kernel env"
	BootParams string
	// Whether to enable or disable bvm console
	BVMConsole *bool
	// Whether to enable or disable mpt table generation
	MPTGen *bool
}

func setDefaults(p *XHyveParams) {
	if p.VCPUs < 1 {
		p.VCPUs = 1
	}

	memsize, err := strconv.Atoi(p.Memory)
	if memsize < 256 || err != nil {
		p.Memory = "256"
	}

	if p.UUID == "" {
		p.UUID = uuid.NewV4().String()
	}

	if p.ACPI == nil {
		p.ACPI = new(bool)
	}

	if p.RTCLocaltime == nil {
		p.RTCLocaltime = new(bool)
	}

	if p.BVMConsole == nil {
		p.BVMConsole = new(bool)
	}

	if p.MPTGen == nil {
		p.MPTGen = new(bool)
		*p.MPTGen = true
	}
}

func init() {
	runtime.LockOSThread()
}

// RunXHyve runs xhyve hypervisor with the given parameters.
func RunXHyve(p XHyveParams) error {
	setDefaults(&p)

	for _, d := range p.PCIDevs {
		device := C.CString(d)
		// defer is not adviced to have within a loop but we are not expecting a lot of PCI devices.
		defer C.free(unsafe.Pointer(device))
		if err := C.pci_parse_slot(device); err != 0 {
			return ErrPCIDevice
		}
	}

	devices := C.CString(p.LPCDevs)
	defer C.free(unsafe.Pointer(devices))
	if err := C.lpc_device_parse(devices); err != 0 {
		return ErrLPCDevice
	}

	bootParams := C.CString(p.BootParams)
	defer C.free(unsafe.Pointer(bootParams))

	if err := C.firmware_parse(bootParams); err != 0 {
		return ErrInvalidBootParams
	}

	fmt.Print("Creating VM... ")
	if err := C.xh_vm_create(); err != 0 {
		fmt.Println(err)
		return ErrCreatingVM
	}
	fmt.Println("done")

	maxVCPUs := C.num_vcpus_allowed()
	vcpus := C.int(p.VCPUs)
	if vcpus > maxVCPUs {
		return ErrMaxNumVCPUExceeded
	}

	var memsize C.size_t
	reqMemsize := C.CString(p.Memory)
	defer C.free(unsafe.Pointer(reqMemsize))
	if err := C.parse_memsize(reqMemsize, &memsize); err != 0 {
		fmt.Println(err)
		return ErrInvalidMemsize
	}

	fmt.Printf("Setting up memory to %d bytes... ", memsize)
	if err := C.xh_vm_setup_memory(memsize, C.VM_MMAP_ALL); err != 0 {
		fmt.Println(err)
		return ErrSettingUpMemory
	}
	fmt.Println("done")

	fmt.Print("Initializing msr... ")
	if err := C.init_msr(); err != 0 {
		fmt.Println(err)
		return ErrInitializingMSR
	}
	fmt.Println("done")

	C.init_mem()
	C.init_inout()
	C.pci_irq_init()
	C.ioapic_init()

	// Uses UTC by default.
	var rtcmode C.int
	if *p.RTCLocaltime {
		rtcmode = C.int(1)
	}
	C.rtc_init(rtcmode)
	C.sci_init()

	if err := C.init_pci(); err != 0 {
		fmt.Println(err)
		return ErrInitializingPCI
	}

	//C.init_dbgport(C.int(5555))

	if *p.BVMConsole {
		C.init_bvmcons()
	}

	if *p.MPTGen {
		if err := C.mptable_build(vcpus); err != 0 {
			return ErrBuildingMPTTable
		}
	}

	if err := C.smbios_build(); err != 0 {
		return ErrBuildingSMBIOS
	}

	if *p.ACPI {
		fmt.Printf("Building ACPI table for %d vcpus...", vcpus)
		if err := C.acpi_build(vcpus); err != 0 {
			return ErrBuildingACPI
		}
		fmt.Println("done")
	}

	var bsp C.int
	var rip C.uint64_t
	C.vcpu_add(bsp, bsp, rip)

	//signal.Ignore()

	fmt.Println("Starting hypervisor busy loop...")
	C.mevent_dispatch()
	fmt.Println("VM has been shutdown")

	return nil
}
<commit_msg>Cleans up code<commit_after>// +build darwin

package main

// #cgo CFLAGS: -I${SRCDIR}/vendor/xhyve/include -x c -std=c11 -fno-common -arch x86_64 -DXHYVE_CONFIG_ASSERT -lxhyve -Os -fstrict-aliasing -Wno-unknown-warning-option -Wno-reserved-id-macro -pedantic -fmessage-length=152 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0
// #cgo LDFLAGS: -L${SRCDIR} -lxhyve -arch x86_64 -framework Hypervisor -framework vmnet -force_load ${SRCDIR}/libxhyve.a
// #include "helper.h"
//
// // These variables are declared in xhyve.h as extern, and used in different C files.
// int guest_ncpus;
// char* guest_uuid_str;
// char* vmname = "hooklift";
//
import "C"

import (
	"errors"
	"fmt"
	"runtime"
	"strconv"
	"unsafe"

	"github.com/satori/go.uuid"
)

var (
	// ErrPCIDevice is returned when an error was found parsing PCI devices.
	ErrPCIDevice = errors.New("error parsing PCI device")
	// ErrLPCDevice is returned when an error was found parsing LPC device options.
	ErrLPCDevice = errors.New("error parsing LPC devices")
	// ErrInvalidMemsize is returned if memorize size is invalid.
	ErrInvalidMemsize = errors.New("invalid memory size")
	// ErrInvalidBootParams is returne when kexec or fbsd params are invalid.
	ErrInvalidBootParams = errors.New("boot parameters are invalid")
	// ErrCreatingVM is returned when xhyve was unable to create the virtual machine.
	ErrCreatingVM = errors.New("unable to create VM")
	// ErrMaxNumVCPUExceeded is returned when the number of vcpus requested for the guest
	// exceeds the limit imposed by xhyve.
	ErrMaxNumVCPUExceeded = errors.New("maximum number of vcpus requested is too high")
	// ErrSettingUpMemory is returned when an error was returned by xhyve when trying
	// to setup guest memory.
	ErrSettingUpMemory = errors.New("unable to setup memory for guest vm")
	// ErrInitializingMSR is returned when xhyve is unable to initialize MSR table
	ErrInitializingMSR = errors.New("unable to initialize MSR table")
	// ErrInitializingPCI is returned when xhyve is unable to initialize PCI emulation
	ErrInitializingPCI = errors.New("unable to initialize PCI emulation")
	// ErrBuildingMPTTable is returned when xhyve is unable to build MPT table
	ErrBuildingMPTTable = errors.New("unable to build MPT table")
	// ErrBuildingSMBIOS is returned when xhyve is unable to build smbios
	ErrBuildingSMBIOS = errors.New("unable to build smbios")
	// ErrBuildingACPI is returned when xhyve is unable to build ACPI
	ErrBuildingACPI = errors.New("unable to build ACPI")
)

// XHyveParams defines parameters needed by xhyve to boot up virtual machines.
type XHyveParams struct {
	// Number of CPUs to assigned to the guest vm.
	VCPUs int
	// Memory in megabytes to assign to guest vm.
	Memory string
	// PCI devices to attach to the guest vm, including bus and slot.
	// Example: []string{"2:0,virtio-net", "0:0,hostbridge"}
	PCIDevs []string
	// LPC devices to attach to the guest vm.
	LPCDevs string // -l com1,stdio
	// Whether to create ACPI tables or not.
	ACPI *bool
	// Universal identifier for the guest vm.
	UUID string
	// Whether to use localtime or UTC in Real Time Clock.
	RTCLocaltime *bool
	// Either kexec or fbsd params. Format:
	// kexec,kernel image,initrd,"cmdline"
	// fbsd,userboot,boot volume,"kernel env"
	BootParams string
	// Whether to enable or disable bvm console
	BVMConsole *bool
	// Whether to enable or disable mpt table generation
	MPTGen *bool
}

func setDefaults(p *XHyveParams) {
	if p.VCPUs < 1 {
		p.VCPUs = 1
	}

	memsize, err := strconv.Atoi(p.Memory)
	if memsize < 256 || err != nil {
		p.Memory = "256"
	}

	if p.UUID == "" {
		p.UUID = uuid.NewV4().String()
	}

	if p.ACPI == nil {
		p.ACPI = new(bool)
	}

	if p.RTCLocaltime == nil {
		p.RTCLocaltime = new(bool)
	}

	if p.BVMConsole == nil {
		p.BVMConsole = new(bool)
	}

	if p.MPTGen == nil {
		p.MPTGen = new(bool)
		*p.MPTGen = true
	}
}

func init() {
	runtime.LockOSThread()
	//signal.Ignore()
}

// RunXHyve runs xhyve hypervisor with the given parameters.
func RunXHyve(p XHyveParams) error {
	setDefaults(&p)

	maxVCPUs := C.num_vcpus_allowed()
	vcpus := C.int(p.VCPUs)
	if vcpus > maxVCPUs {
		return ErrMaxNumVCPUExceeded
	}

	// Sets global variable inside xhyve.c to number of vcpus.
	C.guest_ncpus = vcpus
	C.guest_uuid_str = C.CString(p.UUID)
	defer C.free(unsafe.Pointer(C.guest_uuid_str))

	for _, d := range p.PCIDevs {
		device := C.CString(d)
		// defer is not adviced to have within a loop but we are not expecting a lot of PCI devices.
		defer C.free(unsafe.Pointer(device))
		if err := C.pci_parse_slot(device); err != 0 {
			return ErrPCIDevice
		}
	}

	devices := C.CString(p.LPCDevs)
	defer C.free(unsafe.Pointer(devices))
	if err := C.lpc_device_parse(devices); err != 0 {
		return ErrLPCDevice
	}

	bootParams := C.CString(p.BootParams)
	defer C.free(unsafe.Pointer(bootParams))

	if err := C.firmware_parse(bootParams); err != 0 {
		return ErrInvalidBootParams
	}

	fmt.Print("Creating VM... ")
	if err := C.xh_vm_create(); err != 0 {
		fmt.Println(err)
		return ErrCreatingVM
	}
	fmt.Println("done")

	var memsize C.size_t
	reqMemsize := C.CString(p.Memory)
	defer C.free(unsafe.Pointer(reqMemsize))
	if err := C.parse_memsize(reqMemsize, &memsize); err != 0 {
		fmt.Println(err)
		return ErrInvalidMemsize
	}

	fmt.Printf("Setting up memory to %d bytes... ", memsize)
	if err := C.xh_vm_setup_memory(memsize, C.VM_MMAP_ALL); err != 0 {
		fmt.Println(err)
		return ErrSettingUpMemory
	}
	fmt.Println("done")

	fmt.Print("Initializing msr... ")
	if err := C.init_msr(); err != 0 {
		fmt.Println(err)
		return ErrInitializingMSR
	}
	fmt.Println("done")

	C.init_mem()
	C.init_inout()
	C.pci_irq_init()
	C.ioapic_init()

	// Uses UTC by default.
	var rtcmode C.int
	if *p.RTCLocaltime {
		rtcmode = C.int(1)
	}
	C.rtc_init(rtcmode)
	C.sci_init()

	if err := C.init_pci(); err != 0 {
		fmt.Println(err)
		return ErrInitializingPCI
	}

	C.init_dbgport(C.int(5555))

	if *p.BVMConsole {
		C.init_bvmcons()
	}

	if *p.MPTGen {
		if err := C.mptable_build(vcpus); err != 0 {
			return ErrBuildingMPTTable
		}
	}

	if err := C.smbios_build(); err != 0 {
		return ErrBuildingSMBIOS
	}

	if *p.ACPI {
		fmt.Printf("Building ACPI table for %d vcpus...", vcpus)
		if err := C.acpi_build(vcpus); err != 0 {
			return ErrBuildingACPI
		}
		fmt.Println("done")
	}

	const bsp C.int = C.int(0)
	var rip C.uint64_t
	C.vcpu_add(bsp, bsp, rip)

	fmt.Println("Starting hypervisor busy loop...")
	C.mevent_dispatch()
	fmt.Println("VM has been shut down")

	return nil
}
<|endoftext|>


<commit_before>// +build windows

package enforcer

import (
	"bufio"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os/exec"
	"strings"
	"sync"
	"syscall"

	"github.com/gentlemanautomaton/winsession"
	"github.com/gentlemanautomaton/winsession/wtsapi"
	"github.com/scjalliance/resourceful/enforcerui"
)

// SessionID is a session ID.
type SessionID = winsession.ID

// SessionData holds information about a windows session.
type SessionData = winsession.Session

// Session manages communication with an individual session.
type Session struct {
	data   SessionData
	cmd    Command
	logger Logger

	mutex    sync.RWMutex
	stop     context.CancelFunc
	kill     context.CancelFunc
	inbound  chan enforcerui.Message
	outbound chan enforcerui.Message
	stopped  chan struct{}
}

// NewSession creates a communication manager for the given session.
func NewSession(data SessionData, cmd Command, bufSize int, logger Logger) *Session {
	return &Session{
		data:     data,
		cmd:      cmd,
		logger:   logger,
		inbound:  make(chan enforcerui.Message, bufSize),
		outbound: make(chan enforcerui.Message, bufSize),
	}
}

// Send sends msg to s. It returns an error if the message buffer for
// s is full.
func (s *Session) Send(msg enforcerui.Message) (ok bool) {
	select {
	case s.outbound <- msg:
		return true
	default:
		s.log("The message buffer is full")
		return false
	}
}

/*
// SendPolicies attempts to send a policy change message to the session.
func (m *SessionManager) SendPolicyChange(oldPol, newPol policy.Set) bool {

}
*/

// Connect establishes a connection with s. It launches a ui process as the
// session's user.
func (s *Session) Connect() error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	// TODO: Implement back-off

	if s.stopped != nil {
		return errors.New("a connection has already been established")
	}

	s.log("Connecting")

	// Acquire a user token for the session
	token, err := wtsapi.QueryUserToken(uint32(s.data.ID))
	if err != nil {
		s.log("Failed to acquire token: %v", err)
		return err
	}

	// Make sure the token is valid for the user we expect
	userName := s.data.Info.UserName
	userDomain := s.data.Info.UserDomain
	if err := validateTokenForUser(token, userName, userDomain); err != nil {
		token.Close()
		s.log("Failed to validate token: %v", err)
		return err
	}

	s.debug("Aquired Token: %s\\%s", userDomain, userName)

	ctx, kill := context.WithCancel(context.Background())

	cmd := exec.CommandContext(ctx, s.cmd.Path, s.cmd.Args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow: true,
		Token:      token,
	}

	writer, err := cmd.StdinPipe()
	if err != nil {
		token.Close()
		kill()
		s.log("Failed to create stdin: %v", err)
		return err
	}

	reader, err := cmd.StdoutPipe()
	if err != nil {
		token.Close()
		kill()
		writer.Close()
		s.log("Failed to create stdin: %v", err)
		return err
	}

	// TODO: Use cmd.String() from Go 1.13
	s.debug("Starting UI Process: %s", strings.Join(cmd.Args, " "))

	if err := cmd.Start(); err != nil {
		token.Close()
		kill()
		writer.Close()
		reader.Close()
		s.log("Failed to start UI process: %v", err)
		return err
	}

	stopped := make(chan struct{})

	s.kill = kill
	s.stopped = stopped

	// Spawn the ui process
	go func(kill context.CancelFunc, stopped chan<- struct{}) {
		defer close(stopped)
		defer kill()
		defer token.Close()
		//defer reader.Close()
		err := cmd.Wait()
		s.mutex.Lock()
		s.kill = nil
		s.stopped = nil
		if err != nil {
			s.log("Disconnected: %v", err)
		} else {
			s.log("Disconnected")
		}
		s.mutex.Unlock()
	}(kill, stopped)

	// Send messages to the process
	go func() {
		defer writer.Close()
		enc := json.NewEncoder(writer)
		for {
			select {
			case <-ctx.Done():
				s.debug("Send: %v", ctx.Err())
				return
			case msg, ok := <-s.outbound:
				if !ok {
					s.debug("Send: EOF")
					return
				}
				s.debug("Send: %s", msg.Type)
				enc.Encode(msg)
			}

		}
	}()

	// Receive messages from the process
	go func() {
		scanner := bufio.NewScanner(reader)
		for scanner.Scan() {
			s.debug("Received: %s", scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			s.debug("Receive: %v", err)
		} else {
			s.debug("Receive: EOF")
		}
	}()

	s.log("Process Started")
	return nil
}

// Connected returns true if the session manager is connected.
func (s *Session) Connected() bool {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return s.stopped != nil
}

// Disconnect tears down the connection with s.
func (s *Session) Disconnect() error {
	s.mutex.Lock()

	var (
		kill    = s.kill
		stopped = s.stopped
	)

	if stopped == nil {
		s.mutex.Unlock()
		return errors.New("a connection to the session has not been established")
	}

	s.log("Disconnecting")

	s.mutex.Unlock()

	kill()
	<-stopped

	return nil
}

func (s *Session) log(format string, v ...interface{}) {
	if s.logger != nil {
		s.logger.Log(SessionEvent{
			SessionID:     uint32(s.data.ID),
			WindowStation: s.data.WindowStation,
			SessionUser:   s.data.Info.User(),
			Msg:           fmt.Sprintf(format, v...),
		})
	}
}

func (s *Session) debug(format string, v ...interface{}) {
	if s.logger != nil {
		s.logger.Log(SessionEvent{
			SessionID:     uint32(s.data.ID),
			WindowStation: s.data.WindowStation,
			SessionUser:   s.data.Info.User(),
			Msg:           fmt.Sprintf(format, v...),
			Debug:         true,
		})
	}
}
<commit_msg>enforcer: Minor documentation fixes for Session<commit_after>// +build windows

package enforcer

import (
	"bufio"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os/exec"
	"strings"
	"sync"
	"syscall"

	"github.com/gentlemanautomaton/winsession"
	"github.com/gentlemanautomaton/winsession/wtsapi"
	"github.com/scjalliance/resourceful/enforcerui"
)

// SessionID is a session ID.
type SessionID = winsession.ID

// SessionData holds information about a windows session.
type SessionData = winsession.Session

// Session manages communication with an individual session.
type Session struct {
	data   SessionData
	cmd    Command
	logger Logger

	mutex    sync.RWMutex
	stop     context.CancelFunc
	kill     context.CancelFunc
	inbound  chan enforcerui.Message
	outbound chan enforcerui.Message
	stopped  chan struct{}
}

// NewSession creates a communication manager for the given session.
func NewSession(data SessionData, cmd Command, bufSize int, logger Logger) *Session {
	return &Session{
		data:     data,
		cmd:      cmd,
		logger:   logger,
		inbound:  make(chan enforcerui.Message, bufSize),
		outbound: make(chan enforcerui.Message, bufSize),
	}
}

// Send sends msg to s. It returns false if the message buffer for
// s is full.
func (s *Session) Send(msg enforcerui.Message) (ok bool) {
	select {
	case s.outbound <- msg:
		return true
	default:
		s.log("The message buffer is full")
		return false
	}
}

/*
// SendPolicies attempts to send a policy change message to the session.
func (m *SessionManager) SendPolicyChange(oldPol, newPol policy.Set) bool {

}
*/

// Connect establishes a connection with s. It launches a ui process as the
// session's user.
func (s *Session) Connect() error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.stopped != nil {
		return errors.New("a connection has already been established")
	}

	s.log("Connecting")

	// Acquire a user token for the session
	token, err := wtsapi.QueryUserToken(uint32(s.data.ID))
	if err != nil {
		s.log("Failed to acquire token: %v", err)
		return err
	}

	// Make sure the token is valid for the user we expect
	userName := s.data.Info.UserName
	userDomain := s.data.Info.UserDomain
	if err := validateTokenForUser(token, userName, userDomain); err != nil {
		token.Close()
		s.log("Failed to validate token: %v", err)
		return err
	}

	s.debug("Aquired Token: %s\\%s", userDomain, userName)

	ctx, kill := context.WithCancel(context.Background())

	cmd := exec.CommandContext(ctx, s.cmd.Path, s.cmd.Args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow: true,
		Token:      token,
	}

	writer, err := cmd.StdinPipe()
	if err != nil {
		token.Close()
		kill()
		s.log("Failed to create stdin: %v", err)
		return err
	}

	reader, err := cmd.StdoutPipe()
	if err != nil {
		token.Close()
		kill()
		writer.Close()
		s.log("Failed to create stdin: %v", err)
		return err
	}

	// TODO: Use cmd.String() from Go 1.13
	s.debug("Starting UI Process: %s", strings.Join(cmd.Args, " "))

	if err := cmd.Start(); err != nil {
		token.Close()
		kill()
		writer.Close()
		reader.Close()
		s.log("Failed to start UI process: %v", err)
		return err
	}

	stopped := make(chan struct{})

	s.kill = kill
	s.stopped = stopped

	// Spawn the ui process
	go func(kill context.CancelFunc, stopped chan<- struct{}) {
		defer close(stopped)
		defer kill()
		defer token.Close()
		//defer reader.Close()
		err := cmd.Wait()
		s.mutex.Lock()
		s.kill = nil
		s.stopped = nil
		if err != nil {
			s.log("Disconnected: %v", err)
		} else {
			s.log("Disconnected")
		}
		s.mutex.Unlock()
	}(kill, stopped)

	// Send messages to the process
	go func() {
		defer writer.Close()
		enc := json.NewEncoder(writer)
		for {
			select {
			case <-ctx.Done():
				s.debug("Send: %v", ctx.Err())
				return
			case msg, ok := <-s.outbound:
				if !ok {
					s.debug("Send: EOF")
					return
				}
				s.debug("Send: %s", msg.Type)
				enc.Encode(msg)
			}

		}
	}()

	// Receive messages from the process
	go func() {
		scanner := bufio.NewScanner(reader)
		for scanner.Scan() {
			s.debug("Received: %s", scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			s.debug("Receive: %v", err)
		} else {
			s.debug("Receive: EOF")
		}
	}()

	s.log("Process Started")
	return nil
}

// Connected returns true if the session manager is connected.
func (s *Session) Connected() bool {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return s.stopped != nil
}

// Disconnect tears down the connection with s.
func (s *Session) Disconnect() error {
	s.mutex.Lock()

	var (
		kill    = s.kill
		stopped = s.stopped
	)

	if stopped == nil {
		s.mutex.Unlock()
		return errors.New("a connection to the session has not been established")
	}

	s.log("Disconnecting")

	s.mutex.Unlock()

	kill()
	<-stopped

	return nil
}

func (s *Session) log(format string, v ...interface{}) {
	if s.logger != nil {
		s.logger.Log(SessionEvent{
			SessionID:     uint32(s.data.ID),
			WindowStation: s.data.WindowStation,
			SessionUser:   s.data.Info.User(),
			Msg:           fmt.Sprintf(format, v...),
		})
	}
}

func (s *Session) debug(format string, v ...interface{}) {
	if s.logger != nil {
		s.logger.Log(SessionEvent{
			SessionID:     uint32(s.data.ID),
			WindowStation: s.data.WindowStation,
			SessionUser:   s.data.Info.User(),
			Msg:           fmt.Sprintf(format, v...),
			Debug:         true,
		})
	}
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"
	"testing"
	"time"

	"github.com/keybase/go/libkb"
	keybase_1 "github.com/keybase/protocol/go"
)

func TestLoginNewDevice(t *testing.T) {
	kexTimeout = 1 * time.Second
	// fake kex server implementation
	ksrv := newKexsrv()

	tc := libkb.SetupTest(t, "login")
	u1 := CreateAndSignupFakeUser(t, "login")
	devX := G.Env.GetDeviceID()

	docui := &ldocuiDevice{&ldocui{}, ""}

	// this is all pretty hacky to get kex running on device X...
	secui := libkb.TestSecretUI{u1.Passphrase}
	kexX := NewKex(ksrv, nil, &libkb.UIGroup{Secret: secui}, SetDebugName("device x"))
	me, err := libkb.LoadMe(libkb.LoadUserArg{PublicKeyOptional: true})
	if err != nil {
		t.Fatal(err)
	}
	kexX.getSecret = func() string {
		return docui.secret
	}
	kexX.Listen(me, *devX)
	ksrv.RegisterTestDevice(kexX, *devX)

	G.LoginState.Logout()
	tc.Cleanup()

	// redo SetupTest to get a new home directory...should look like a new device.
	tc2 := libkb.SetupTest(t, "login")
	defer tc2.Cleanup()

	larg := LoginEngineArg{
		Login: libkb.LoginArg{
			Force:      true,
			Prompt:     false,
			Username:   u1.Username,
			Passphrase: u1.Passphrase,
			NoUi:       true,
		},
		KexSrv: ksrv,
	}

	before := docui.selectSignerCount

	li := NewLoginEngine()
	ctx := NewContext(G.UI.GetLogUI(), docui, &gpgtestui{}, secui, &libkb.TestLoginUI{})
	if err := RunEngine(li, ctx, larg, nil); err != nil {
		t.Fatal(err)
	}

	after := docui.selectSignerCount
	if after-before != 1 {
		t.Errorf("doc ui SelectSigner called %d times, expected 1", after-before)
	}

	testUserHasDeviceKey(t)
}

type ldocuiDevice struct {
	*ldocui
	secret string
}

// select the first device
func (l *ldocuiDevice) SelectSigner(arg keybase_1.SelectSignerArg) (res keybase_1.SelectSignerRes, err error) {
	l.selectSignerCount++
	if len(arg.Devices) == 0 {
		return res, fmt.Errorf("expected len(devices) > 0")
	}
	res.Action = keybase_1.SelectSignerAction_SIGN
	devid := arg.Devices[0].DeviceID
	res.Signer = &keybase_1.DeviceSigner{Kind: keybase_1.DeviceSignerKind_DEVICE, DeviceID: &devid}
	return
}

func (l *ldocuiDevice) DisplaySecretWords(arg keybase_1.DisplaySecretWordsArg) error {
	l.secret = arg.Secret
	G.Log.Info("secret words: %s", arg.Secret)
	return nil
}

type kexsrv struct {
	devices map[libkb.DeviceID]KexServer
}

func newKexsrv() *kexsrv {
	return &kexsrv{devices: make(map[libkb.DeviceID]KexServer)}
}

func (k *kexsrv) StartKexSession(ctx *KexContext, id KexStrongID) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.StartKexSession(ctx, id)
	}
	return k.gocall(f)
}

func (k *kexsrv) StartReverseKexSession(ctx *KexContext) error { return nil }

func (k *kexsrv) Hello(ctx *KexContext, devID libkb.DeviceID, devKeyID libkb.KID) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.Hello(ctx, devID, devKeyID)
	}
	return k.gocall(f)
}

func (k *kexsrv) PleaseSign(ctx *KexContext, eddsa libkb.NaclSigningKeyPublic, sig, devType, devDesc string) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.PleaseSign(ctx, eddsa, sig, devType, devDesc)
	}
	return k.gocall(f)
}

func (k *kexsrv) Done(ctx *KexContext, mt libkb.MerkleTriple) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.Done(ctx, mt)
	}
	return k.gocall(f)
}

func (k *kexsrv) RegisterTestDevice(srv KexServer, device libkb.DeviceID) error {
	k.devices[device] = srv
	return nil
}

func (k *kexsrv) gocall(fn func() error) error {
	ch := make(chan error)
	go func() {
		err := fn()
		ch <- err
	}()
	return <-ch
}

func (k *kexsrv) findDevice(id libkb.DeviceID) (KexServer, error) {
	s, ok := k.devices[id]
	if !ok {
		return nil, fmt.Errorf("device %x not registered", id)
	}
	return s, nil
}
<commit_msg>fixed test<commit_after>package engine

import (
	"fmt"
	"testing"
	"time"

	"github.com/keybase/go/libkb"
	keybase_1 "github.com/keybase/protocol/go"
)

func TestLoginNewDevice(t *testing.T) {
	kexTimeout = 1 * time.Second
	// fake kex server implementation
	ksrv := newKexsrv()

	tc := libkb.SetupTest(t, "login")
	u1 := CreateAndSignupFakeUser(t, "login")
	devX := G.Env.GetDeviceID()

	docui := &ldocuiDevice{&ldocui{}, ""}

	// this is all pretty hacky to get kex running on device X...
	secui := libkb.TestSecretUI{u1.Passphrase}
	xctx := NewContext(docui, secui, G.UI.GetLogUI())
	kexX := NewKex(ksrv, nil, SetDebugName("device x"))
	me, err := libkb.LoadMe(libkb.LoadUserArg{PublicKeyOptional: true})
	if err != nil {
		t.Fatal(err)
	}
	kexX.getSecret = func() string {
		return docui.secret
	}
	kexX.Listen(xctx, me, *devX)
	ksrv.RegisterTestDevice(kexX, *devX)

	G.LoginState.Logout()
	tc.Cleanup()

	// redo SetupTest to get a new home directory...should look like a new device.
	tc2 := libkb.SetupTest(t, "login")
	defer tc2.Cleanup()

	larg := LoginEngineArg{
		Login: libkb.LoginArg{
			Force:      true,
			Prompt:     false,
			Username:   u1.Username,
			Passphrase: u1.Passphrase,
			NoUi:       true,
		},
		KexSrv: ksrv,
	}

	before := docui.selectSignerCount

	li := NewLoginEngine()
	ctx := NewContext(G.UI.GetLogUI(), docui, &gpgtestui{}, secui, &libkb.TestLoginUI{})
	if err := RunEngine(li, ctx, larg, nil); err != nil {
		t.Fatal(err)
	}

	after := docui.selectSignerCount
	if after-before != 1 {
		t.Errorf("doc ui SelectSigner called %d times, expected 1", after-before)
	}

	testUserHasDeviceKey(t)
}

type ldocuiDevice struct {
	*ldocui
	secret string
}

// select the first device
func (l *ldocuiDevice) SelectSigner(arg keybase_1.SelectSignerArg) (res keybase_1.SelectSignerRes, err error) {
	l.selectSignerCount++
	if len(arg.Devices) == 0 {
		return res, fmt.Errorf("expected len(devices) > 0")
	}
	res.Action = keybase_1.SelectSignerAction_SIGN
	devid := arg.Devices[0].DeviceID
	res.Signer = &keybase_1.DeviceSigner{Kind: keybase_1.DeviceSignerKind_DEVICE, DeviceID: &devid}
	return
}

func (l *ldocuiDevice) DisplaySecretWords(arg keybase_1.DisplaySecretWordsArg) error {
	l.secret = arg.Secret
	G.Log.Info("secret words: %s", arg.Secret)
	return nil
}

type kexsrv struct {
	devices map[libkb.DeviceID]KexServer
}

func newKexsrv() *kexsrv {
	return &kexsrv{devices: make(map[libkb.DeviceID]KexServer)}
}

func (k *kexsrv) StartKexSession(ctx *KexContext, id KexStrongID) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.StartKexSession(ctx, id)
	}
	return k.gocall(f)
}

func (k *kexsrv) StartReverseKexSession(ctx *KexContext) error { return nil }

func (k *kexsrv) Hello(ctx *KexContext, devID libkb.DeviceID, devKeyID libkb.KID) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.Hello(ctx, devID, devKeyID)
	}
	return k.gocall(f)
}

func (k *kexsrv) PleaseSign(ctx *KexContext, eddsa libkb.NaclSigningKeyPublic, sig, devType, devDesc string) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.PleaseSign(ctx, eddsa, sig, devType, devDesc)
	}
	return k.gocall(f)
}

func (k *kexsrv) Done(ctx *KexContext, mt libkb.MerkleTriple) error {
	s, err := k.findDevice(ctx.Dst)
	if err != nil {
		return err
	}
	f := func() error {
		return s.Done(ctx, mt)
	}
	return k.gocall(f)
}

func (k *kexsrv) RegisterTestDevice(srv KexServer, device libkb.DeviceID) error {
	k.devices[device] = srv
	return nil
}

func (k *kexsrv) gocall(fn func() error) error {
	ch := make(chan error)
	go func() {
		err := fn()
		ch <- err
	}()
	return <-ch
}

func (k *kexsrv) findDevice(id libkb.DeviceID) (KexServer, error) {
	s, ok := k.devices[id]
	if !ok {
		return nil, fmt.Errorf("device %x not registered", id)
	}
	return s, nil
}
<|endoftext|>