<commit_before>// Copyright (c) 2017 DG Lab
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// dave project main.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"democonf"
	"rpc"
)

// URL for accessing RPC
var rpcurl = "http://127.0.0.1:10030"

// ID for accessing RPC
var rpcuser = "user"

// Password for accessing RPC
var rpcpass = "pass"

// Listen addr for RPC Proxy
var laddr = ":8030"

//  getNewAddress use confidential
var confidential = false

var rpcClient *rpc.Rpc

var interval = 3 * time.Second

// Item details
type Item struct {
	Price   float64
	Asset   string
	Timeout int64
}

var items = map[string]Item{
	"Caramel Macchiato Coffee": Item{Price: float64(200), Asset: "MELON", Timeout: int64(60 * 60)},
}

// Order details
type Order struct {
	Item       string
	Addr       string
	Status     int
	Asset      string
	Price      float64
	Timeout    int64
	LastModify int64
}

var list = []*Order{}

var logger *log.Logger

var stop = false

func loop() {
	fmt.Println("Loop interval:", interval)
	for {
		time.Sleep(interval)
		if stop {
			break
		}
		for _, order := range list {
			if order.Status != 0 {
				continue
			}
			now := time.Now().Unix()
			if order.Timeout <= now {
				fmt.Println("Timeout!", order.Addr)
				order.Status = -1
				order.LastModify = now
				continue
			}
			amount, res, err := rpcClient.RequestAndCastNumber("getreceivedbyaddress", order.Addr, 1, order.Asset)
			if err != nil {
				logger.Printf("Rpc#RequestAndCastNumber error:%v res:%+v", err, res)
				continue
			}
			if amount >= order.Price {
				fmt.Println("Paid!", order.Addr)
				order.Status = 1
				order.LastModify = now
			}
		}
	}
}

func orderhandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST,GET")
	w.Header().Add("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
	w.Header().Add("Access-Control-Max-Age", "-1")

	item := r.FormValue("item")
	result := make(map[string]interface{})
	result["result"] = false
	for key, val := range items {
		if key == item {
			addr, err := rpcClient.GetNewAddr(confidential)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				logger.Println("getNewAddress error", err)
				return
			}
			result["result"] = true
			vals := url.Values{}
			result["name"] = key
			vals["name"] = []string{key}
			result["addr"] = addr
			vals["addr"] = []string{addr}
			result["price"] = val.Price
			vals["price"] = []string{fmt.Sprintf("%v", val.Price)}
			result["asset"] = val.Asset
			vals["asset"] = []string{val.Asset}
			uri := fmt.Sprint("px:invoice?", vals.Encode())
			fmt.Println(uri)
			result["uri"] = uri
			now := time.Now().Unix()
			order := &Order{Item: key, Addr: addr, Status: 0, Timeout: now + val.Timeout, Price: val.Price, Asset: val.Asset, LastModify: now}
			list = append(list, order)
			bs, _ := json.Marshal(order)
			fmt.Println("Order", string(bs))
			break
		}
	}
	if !result["result"].(bool) {
		w.WriteHeader(http.StatusNotFound)
		result["error"] = fmt.Sprint("Not Found item. ", item)
	}
	bs, _ := json.Marshal(result)
	w.Write(bs)
}

func listhandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST,GET")
	w.Header().Add("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
	w.Header().Add("Access-Control-Max-Age", "-1")

	res := make(map[string]interface{})
	res["result"] = list
	bs, _ := json.Marshal(res)
	w.Write(bs)
}

func loadConf() {
	conf := democonf.NewDemoConf("dave")
	rpcurl = conf.GetString("rpcurl", rpcurl)
	rpcuser = conf.GetString("rpcuser", rpcuser)
	rpcpass = conf.GetString("rpcpass", rpcpass)
	laddr = conf.GetString("laddr", laddr)
	confidential = conf.GetBool("confidential", confidential)
}

func main() {
	logger = log.New(os.Stdout, "Dave:", log.LstdFlags+log.Lshortfile)
	fmt.Println("Dave starting")

	loadConf()
	rpcClient = rpc.NewRpc(rpcurl, rpcuser, rpcpass)

	listener, err := net.Listen("tcp", laddr)
	if err != nil {
		logger.Println("net#Listen error:", err)
		return
	}
	defer listener.Close()

	mux := http.NewServeMux()
	mux.HandleFunc("/order", orderhandler)
	mux.HandleFunc("/list", listhandler)
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	fmt.Println("html path:", http.Dir(dir+"/html/dave"))
	mux.Handle("/", http.FileServer(http.Dir(dir+"/html/dave")))
	fmt.Println("start listening...", listener.Addr().Network(), listener.Addr())
	go http.Serve(listener, mux)

	// signal handling (ctrl + c)
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT)
	go func() {
		fmt.Println(<-sig)
		stop = true
	}()

	loop()

	fmt.Println("Dave stopping")
}
<commit_msg>dave use cyclic<commit_after>// Copyright (c) 2017 DG Lab
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// dave project main.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"time"

	"democonf"
	"lib"
	"rpc"
)

// URL for accessing RPC
var rpcurl = "http://127.0.0.1:10030"

// ID for accessing RPC
var rpcuser = "user"

// Password for accessing RPC
var rpcpass = "pass"

// Listen addr for RPC Proxy
var laddr = ":8030"

//  getNewAddress use confidential
var confidential = false

var rpcClient *rpc.Rpc

// Item details
type Item struct {
	Price   float64
	Asset   string
	Timeout int64
}

var items = map[string]Item{
	"Caramel Macchiato Coffee": Item{Price: float64(200), Asset: "MELON", Timeout: int64(60 * 60)},
}

// Order details
type Order struct {
	Item       string
	Addr       string
	Status     int
	Asset      string
	Price      float64
	Timeout    int64
	LastModify int64
}

var list = []*Order{}

var logger *log.Logger

func callback() {
	for _, order := range list {
		if order.Status != 0 {
			continue
		}
		now := time.Now().Unix()
		if order.Timeout <= now {
			fmt.Println("Timeout!", order.Addr)
			order.Status = -1
			order.LastModify = now
			continue
		}
		amount, res, err := rpcClient.RequestAndCastNumber("getreceivedbyaddress", order.Addr, 1, order.Asset)
		if err != nil {
			logger.Printf("Rpc#RequestAndCastNumber error:%v res:%+v", err, res)
			continue
		}
		if amount >= order.Price {
			fmt.Println("Paid!", order.Addr)
			order.Status = 1
			order.LastModify = now
		}
	}
}

func orderhandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST,GET")
	w.Header().Add("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
	w.Header().Add("Access-Control-Max-Age", "-1")

	item := r.FormValue("item")
	result := make(map[string]interface{})
	result["result"] = false
	for key, val := range items {
		if key == item {
			addr, err := rpcClient.GetNewAddr(confidential)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				logger.Println("getNewAddress error", err)
				return
			}
			result["result"] = true
			vals := url.Values{}
			result["name"] = key
			vals["name"] = []string{key}
			result["addr"] = addr
			vals["addr"] = []string{addr}
			result["price"] = val.Price
			vals["price"] = []string{fmt.Sprintf("%v", val.Price)}
			result["asset"] = val.Asset
			vals["asset"] = []string{val.Asset}
			uri := fmt.Sprint("px:invoice?", vals.Encode())
			fmt.Println(uri)
			result["uri"] = uri
			now := time.Now().Unix()
			order := &Order{Item: key, Addr: addr, Status: 0, Timeout: now + val.Timeout, Price: val.Price, Asset: val.Asset, LastModify: now}
			list = append(list, order)
			bs, _ := json.Marshal(order)
			fmt.Println("Order", string(bs))
			break
		}
	}
	if !result["result"].(bool) {
		w.WriteHeader(http.StatusNotFound)
		result["error"] = fmt.Sprint("Not Found item. ", item)
	}
	bs, _ := json.Marshal(result)
	w.Write(bs)
}

func listhandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Methods", "POST,GET")
	w.Header().Add("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
	w.Header().Add("Access-Control-Max-Age", "-1")

	res := make(map[string]interface{})
	res["result"] = list
	bs, _ := json.Marshal(res)
	w.Write(bs)
}

func loadConf() {
	conf := democonf.NewDemoConf("dave")
	rpcurl = conf.GetString("rpcurl", rpcurl)
	rpcuser = conf.GetString("rpcuser", rpcuser)
	rpcpass = conf.GetString("rpcpass", rpcpass)
	laddr = conf.GetString("laddr", laddr)
	confidential = conf.GetBool("confidential", confidential)
}

func main() {
	logger = log.New(os.Stdout, "Dave:", log.LstdFlags+log.Lshortfile)
	fmt.Println("Dave starting")

	loadConf()
	rpcClient = rpc.NewRpc(rpcurl, rpcuser, rpcpass)

	listener, err := net.Listen("tcp", laddr)
	if err != nil {
		logger.Println("net#Listen error:", err)
		return
	}
	defer listener.Close()

	mux := http.NewServeMux()
	mux.HandleFunc("/order", orderhandler)
	mux.HandleFunc("/list", listhandler)
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	fmt.Println("html path:", http.Dir(dir+"/html/dave"))
	mux.Handle("/", http.FileServer(http.Dir(dir+"/html/dave")))
	fmt.Println("start listening...", listener.Addr().Network(), listener.Addr())
	go http.Serve(listener, mux)

	lib.SetLogger(logger)
	lib.StartCyclic(callback, 3, true)

	fmt.Println("Dave stopping")
}
<|endoftext|>


<commit_before>package command

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd"
)

// NewExecWatchCommand returns the CLI command for "exec-watch".
func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:  "exec-watch",
		Usage: "watch a key for changes and exec an executable",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
			cli.BoolFlag{Name: "recursive", Usage: "watch all values for key and child keys"},
		},
		Action: func(c *cli.Context) {
			handleKey(c, execWatchCommandFunc)
		},
	}
}

// execWatchCommandFunc executes the "exec-watch" command.
func execWatchCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) {
	_ = io.Copy
	_ = exec.Command
	args := c.Args()
	argsLen := len(args)

	if argsLen < 2 {
		return nil, errors.New("Key and command to exec required")
	}

	key := args[argsLen-1]
	cmdArgs := args[:argsLen-1]

	index := 0
	if c.Int("after-index") != 0 {
		index = c.Int("after-index") + 1
		key = args[0]
		cmdArgs = args[2:]
	}

	recursive := c.Bool("recursive")
	if recursive != false {
		key = args[0]
		cmdArgs = args[2:]
	}

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)
	stop := make(chan bool)

	go func() {
		<-sigch
		stop <- true
		os.Exit(0)
	}()

	receiver := make(chan *etcd.Response)
	client.SetConsistency(etcd.STRONG_CONSISTENCY)
	go client.Watch(key, uint64(index), recursive, receiver, stop)

	for {
		resp := <-receiver
		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
		cmd.Env = environResponse(resp, os.Environ())

		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		stderr, err := cmd.StderrPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		err = cmd.Start()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		go io.Copy(os.Stdout, stdout)
		go io.Copy(os.Stderr, stderr)
		cmd.Wait()
	}
}

func environResponse(resp *etcd.Response, env []string) []string {
	env = append(env, "ETCD_WATCH_ACTION="+resp.Action)
	env = append(env, "ETCD_WATCH_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.Node.ModifiedIndex))
	env = append(env, "ETCD_WATCH_KEY="+resp.Node.Key)
	env = append(env, "ETCD_WATCH_VALUE="+resp.Node.Value)
	return env
}
<commit_msg>etcdctl: remove SetConsistency call<commit_after>package command

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd"
)

// NewExecWatchCommand returns the CLI command for "exec-watch".
func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:  "exec-watch",
		Usage: "watch a key for changes and exec an executable",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
			cli.BoolFlag{Name: "recursive", Usage: "watch all values for key and child keys"},
		},
		Action: func(c *cli.Context) {
			handleKey(c, execWatchCommandFunc)
		},
	}
}

// execWatchCommandFunc executes the "exec-watch" command.
func execWatchCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) {
	_ = io.Copy
	_ = exec.Command
	args := c.Args()
	argsLen := len(args)

	if argsLen < 2 {
		return nil, errors.New("Key and command to exec required")
	}

	key := args[argsLen-1]
	cmdArgs := args[:argsLen-1]

	index := 0
	if c.Int("after-index") != 0 {
		index = c.Int("after-index") + 1
		key = args[0]
		cmdArgs = args[2:]
	}

	recursive := c.Bool("recursive")
	if recursive != false {
		key = args[0]
		cmdArgs = args[2:]
	}

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)
	stop := make(chan bool)

	go func() {
		<-sigch
		stop <- true
		os.Exit(0)
	}()

	receiver := make(chan *etcd.Response)
	go client.Watch(key, uint64(index), recursive, receiver, stop)

	for {
		resp := <-receiver
		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
		cmd.Env = environResponse(resp, os.Environ())

		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		stderr, err := cmd.StderrPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		err = cmd.Start()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		go io.Copy(os.Stdout, stdout)
		go io.Copy(os.Stderr, stderr)
		cmd.Wait()
	}
}

func environResponse(resp *etcd.Response, env []string) []string {
	env = append(env, "ETCD_WATCH_ACTION="+resp.Action)
	env = append(env, "ETCD_WATCH_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.Node.ModifiedIndex))
	env = append(env, "ETCD_WATCH_KEY="+resp.Node.Key)
	env = append(env, "ETCD_WATCH_VALUE="+resp.Node.Value)
	return env
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/json"
	"github.com/vgheri/goCacheIt/Godeps/_workspace/src/github.com/gorilla/mux"
	"github.com/vgheri/goCacheIt/splay"
	//"log"
	"net/http"
)

// Handler is a strictly typed object containing the list of available handlers
type Handler struct {
	HandleGetValue func(w http.ResponseWriter, r *http.Request)
	HandleAddValue func(w http.ResponseWriter, r *http.Request)
}

const mimeTypeJSON string = "application/json; charset=UTF-8"

var dataStore *splay.Tree

// New initializes the package with the underlying data store instance
func New(store *splay.Tree) *Handler {
	dataStore = store
	handler := &Handler{
		HandleGetValue: getValue,
		HandleAddValue: addValue,
	}
	return handler
}

// KeyValueCouple models the input data for the POST request
type KeyValueCouple struct {
	Key   string    //`json:"key"`
	Value splay.Any //`json:"value"`
}

// APIError models the error object sent back to the client on error
type APIError struct {
	Code    int
	Message string
}

// getValue retrieves a value by key from the datastore
func getValue(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	key := vars["key"]
	node, err := dataStore.Get(key)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if node == nil {
		writeJSONError(w, "Key not found", http.StatusNotFound)
		return
	}
	value, err := json.Marshal(node.Value)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", mimeTypeJSON)
	w.Write(value)
	return
}

// addValue adds a couple {key,value} to the datastore
func addValue(w http.ResponseWriter, r *http.Request) {
	var reqBody KeyValueCouple

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&reqBody); err != nil {
		writeJSONError(w, err.Error(), 422)
		return
	}

	if reqBody.Key == "" {
		writeJSONError(w, "Please specify a valid key and a value",
			http.StatusBadRequest)
		return
	}
	_, err := dataStore.Insert(reqBody.Key, reqBody.Value)
	if err != nil {
		writeJSONError(w, err.Error(),
			http.StatusBadRequest)
		return
	}
	w.WriteHeader(http.StatusCreated)
	return
}

func writeJSONError(w http.ResponseWriter, errorMsg string, httpErrorCode int) {
	w.Header().Set("Content-Type", mimeTypeJSON)
	w.WriteHeader(httpErrorCode)
	apiErr := APIError{Message: errorMsg}
	if err := json.NewEncoder(w).Encode(apiErr); err != nil {
		panic(err)
	}
}
<commit_msg>Added json comments for APIError struct<commit_after>package handlers

import (
	"encoding/json"
	"github.com/vgheri/goCacheIt/Godeps/_workspace/src/github.com/gorilla/mux"
	"github.com/vgheri/goCacheIt/splay"
	//"log"
	"net/http"
)

// Handler is a strictly typed object containing the list of available handlers
type Handler struct {
	HandleGetValue func(w http.ResponseWriter, r *http.Request)
	HandleAddValue func(w http.ResponseWriter, r *http.Request)
}

const mimeTypeJSON string = "application/json; charset=UTF-8"

var dataStore *splay.Tree

// New initializes the package with the underlying data store instance
func New(store *splay.Tree) *Handler {
	dataStore = store
	handler := &Handler{
		HandleGetValue: getValue,
		HandleAddValue: addValue,
	}
	return handler
}

// KeyValueCouple models the input data for the POST request
type KeyValueCouple struct {
	Key   string    //`json:"key"`
	Value splay.Any //`json:"value"`
}

// APIError models the error object sent back to the client on error
type APIError struct {
	Code    int    //`json:"code"`
	Message string //`json:"message"`
}

// getValue retrieves a value by key from the datastore
func getValue(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	key := vars["key"]
	node, err := dataStore.Get(key)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if node == nil {
		writeJSONError(w, "Key not found", http.StatusNotFound)
		return
	}
	value, err := json.Marshal(node.Value)
	if err != nil {
		writeJSONError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", mimeTypeJSON)
	w.Write(value)
	return
}

// addValue adds a couple {key,value} to the datastore
func addValue(w http.ResponseWriter, r *http.Request) {
	var reqBody KeyValueCouple

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&reqBody); err != nil {
		writeJSONError(w, err.Error(), 422)
		return
	}

	if reqBody.Key == "" {
		writeJSONError(w, "Please specify a valid key and a value",
			http.StatusBadRequest)
		return
	}
	_, err := dataStore.Insert(reqBody.Key, reqBody.Value)
	if err != nil {
		writeJSONError(w, err.Error(),
			http.StatusBadRequest)
		return
	}
	w.WriteHeader(http.StatusCreated)
	return
}

func writeJSONError(w http.ResponseWriter, errorMsg string, httpErrorCode int) {
	w.Header().Set("Content-Type", mimeTypeJSON)
	w.WriteHeader(httpErrorCode)
	apiErr := APIError{Message: errorMsg}
	if err := json.NewEncoder(w).Encode(apiErr); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package keybase

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/kardianos/osext"
	"github.com/keybase/go-updater"
	"github.com/keybase/go-updater/command"
	"github.com/keybase/go-updater/process"
	"github.com/keybase/go-updater/util"
)

// execPath returns the app bundle path where this executable is located
func (c config) execPath() string {
	path, err := osext.Executable()
	if err != nil {
		c.log.Warningf("Error trying to determine our executable path: %s", err)
		return ""
	}
	return path
}

// destinationPath returns the app bundle path where this executable is located
func (c config) destinationPath() string {
	return appBundleForPath(c.execPath())
}

func appBundleForPath(path string) string {
	if path == "" {
		return ""
	}
	paths := strings.SplitN(path, ".app", 2)
	// If no match, return ""
	if len(paths) <= 1 {
		return ""
	}
	return paths[0] + ".app"
}

func libraryDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	return filepath.Join(usr.HomeDir, "Library"), nil
}

// Dir returns where to store config
func Dir(appName string) (string, error) {
	if appName == "" {
		return "", fmt.Errorf("No app name for dir")
	}
	libDir, err := libraryDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(libDir, "Application Support", appName), nil
}

// CacheDir returns where to store temporary files
func CacheDir(appName string) (string, error) {
	if appName == "" {
		return "", fmt.Errorf("No app name for dir")
	}
	return filepath.Join(os.TempDir(), appName), nil
}

// LogDir is where to log
func LogDir(appName string) (string, error) {
	libDir, err := libraryDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(libDir, "Logs"), nil
}

func (c config) osVersion() string {
	result, err := command.Exec("/usr/bin/sw_vers", []string{"-productVersion"}, 5*time.Second, c.log)
	if err != nil {
		c.log.Warningf("Error trying to determine OS version: %s (%s)", err, result.CombinedOutput())
		return ""
	}
	return strings.TrimSpace(result.Stdout.String())
}

func (c config) osArch() string {
	cmd := exec.Command("uname", "-m")
	var buf bytes.Buffer
	cmd.Stdout = &buf
	err := cmd.Run()
	if err != nil {
		return runtime.GOARCH
	}
	return buf.String()
}

func (c config) promptProgram() (command.Program, error) {
	destinationPath := c.destinationPath()
	if destinationPath == "" {
		return command.Program{}, fmt.Errorf("No destination path")
	}
	return command.Program{
		Path: filepath.Join(destinationPath, "Contents", "Resources", "KeybaseUpdater.app", "Contents", "MacOS", "Updater"),
	}, nil
}

func (c config) notifyProgram() string {
	// No notify program for Darwin
	return ""
}

func (c context) BeforeUpdatePrompt(update updater.Update, options updater.UpdateOptions) error {
	return nil
}

// UpdatePrompt is called when the user needs to accept an update
func (c context) UpdatePrompt(update updater.Update, options updater.UpdateOptions, promptOptions updater.UpdatePromptOptions) (*updater.UpdatePromptResponse, error) {
	promptProgram, err := c.config.promptProgram()
	if err != nil {
		return nil, err
	}
	return c.updatePrompt(promptProgram, update, options, promptOptions, time.Hour)
}

// PausedPrompt is called when the we can't update cause the app is in use.
// We return true if the use wants to cancel the update.
func (c context) PausedPrompt() bool {
	promptProgram, err := c.config.promptProgram()
	if err != nil {
		c.log.Warningf("Error trying to get prompt path: %s", err)
		return false
	}
	cancelUpdate, err := c.pausedPrompt(promptProgram, 5*time.Minute)
	if err != nil {
		c.log.Warningf("Error in paused prompt: %s", err)
		return false
	}
	return cancelUpdate
}

const serviceInBundlePath = "/Contents/SharedSupport/bin/keybase"
const kbfsInBundlePath = "/Contents/SharedSupport/bin/kbfs"

type processPaths struct {
	serviceProcPath string
	kbfsProcPath    string
	appPath         string
	appProcPath     string
}

func (c context) lookupProcessPaths() (p processPaths, _ error) {
	appPath := c.config.destinationPath() // "/Applications/Keybase.app"
	if appPath == "" {
		return p, fmt.Errorf("No app path")
	}
	appBundleName := filepath.Base(appPath) // "Keybase.app"

	p.appPath = appPath
	p.serviceProcPath = appBundleName + serviceInBundlePath
	p.kbfsProcPath = appBundleName + kbfsInBundlePath
	p.appProcPath = appBundleName + "/Contents/MacOS/"

	return p, nil
}

// stop will quit the app and any services
func (c context) stop() error {
	// Stop app
	appExitResult, appExitErr := command.Exec(c.config.keybasePath(), []string{"ctl", "app-exit"}, 30*time.Second, c.log)
	c.log.Infof("Stop output: %s", appExitResult.CombinedOutput())
	if appExitErr != nil {
		c.log.Warningf("Error requesting app exit: %s", appExitErr)
	}

	// Stop the redirector so it can be upgraded for all users.
	_, redirectorErr := command.Exec(c.config.keybasePath(), []string{"uninstall", "--components=redirector", fmt.Sprintf("--source-path=%s", sourcePath)}, time.Minute, c.log)
	if redirectorErr != nil {
		c.log.Warningf("Error stopping the redirector: %s", redirectorErr)
	}

	// Stop services
	servicesExitResult, servicesExitErr := command.Exec(c.config.keybasePath(), []string{"ctl", "stop", "--include=service,kbfs"}, 30*time.Second, c.log)
	c.log.Infof("Stop output: %s", servicesExitResult.CombinedOutput())
	if servicesExitErr != nil {
		c.log.Warningf("Error stopping services: %s", servicesExitErr)
	}

	paths, err := c.lookupProcessPaths()
	if err != nil {
		return err
	}

	// SIGKILL the app if it failed to exit (if it exited, then this doesn't do anything)
	c.log.Infof("Killing (if failed to exit) %s", paths.appProcPath)
	process.KillAll(process.NewMatcher(paths.appProcPath, process.PathContains, c.log), c.log)

	return nil
}

// AfterApply is called after an update is applied
func (c context) AfterApply(update updater.Update) error {
	if err := c.stop(); err != nil {
		c.log.Warningf("Error trying to stop: %s", err)
	}

	if err := c.start(10*time.Second, time.Second); err != nil {
		c.log.Warningf("Error trying to start the app: %s", err)
	}
	return nil
}

// Start the app.
// The wait is how log to wait for processes and the app to start before
// reporting that an error occurred.
func (c context) start(wait time.Duration, delay time.Duration) error {
	procPaths, err := c.lookupProcessPaths()
	if err != nil {
		return err
	}

	if err := OpenAppDarwin(procPaths.appPath, c.log); err != nil {
		c.log.Warningf("Error opening app: %s", err)
	}

	// Check to make sure processes started
	c.log.Debugf("Checking processes: %#v", procPaths)
	serviceProcErr := c.checkProcess(procPaths.serviceProcPath, wait, delay)
	kbfsProcErr := c.checkProcess(procPaths.kbfsProcPath, wait, delay)
	appProcErr := c.checkProcess(procPaths.appProcPath, wait, delay)

	return util.CombineErrors(serviceProcErr, kbfsProcErr, appProcErr)
}

func (c context) checkProcess(match string, wait time.Duration, delay time.Duration) error {
	matcher := process.NewMatcher(match, process.PathContains, c.log)
	procs, err := process.FindProcesses(matcher, wait, delay, c.log)
	if err != nil {
		return fmt.Errorf("Error checking on process (%s): %s", match, err)
	}
	if len(procs) == 0 {
		return fmt.Errorf("No process found for %s", match)
	}
	return nil
}

// OpenAppDarwin starts an app
func OpenAppDarwin(appPath string, log process.Log) error {
	return openAppDarwin("/usr/bin/open", appPath, time.Second, log)
}

func openAppDarwin(bin string, appPath string, retryDelay time.Duration, log process.Log) error {
	tryOpen := func() error {
		env := append(os.Environ(), "KEYBASE_RESTORE_UI=true", "KEYBASE_START_UI=hideWindow", "KEYBASE_OPEN_FROM=updater")
		result, err := command.ExecWithEnv(bin, []string{appPath}, env, time.Minute, log)
		if err != nil {
			return fmt.Errorf("Open error: %s; %s", err, result.CombinedOutput())
		}
		return nil
	}
	// We need to try 10 times because Gatekeeper has some issues, for example,
	// http://www.openradar.me/23614087
	var err error
	for i := 0; i < 10; i++ {
		err = tryOpen()
		if err == nil {
			break
		}
		log.Errorf("Open error (trying again in %s): %s", retryDelay, err)
		time.Sleep(retryDelay)
	}
	return err
}

func (c context) check(sourcePath string, destinationPath string) error {
	// Check to make sure the update source path is a real directory
	ok, err := util.IsDirReal(sourcePath)
	if err != nil {
		return err
	}
	if !ok {
		return fmt.Errorf("Source path isn't a directory")
	}
	return nil
}

func (c context) apply(localPath string, destinationPath string, tmpDir string) error {
	// The file name we unzip over should match the (base) file in the destination path
	filename := filepath.Base(destinationPath)
	return util.UnzipOver(localPath, filename, destinationPath, c.check, tmpDir, c.log)
}

func (c context) Apply(update updater.Update, options updater.UpdateOptions, tmpDir string) error {
	if update.Asset == nil {
		return fmt.Errorf("No asset")
	}
	localPath := update.Asset.LocalPath
	destinationPath := options.DestinationPath

	err := c.apply(localPath, destinationPath, tmpDir)
	switch err := err.(type) {
	case nil:
	case *os.LinkError:
		if err.Op == "rename" && err.Old == "/Applications/Keybase.app" {
			c.log.Infof("The error was a problem renaming (moving) the app, let's trying installing the app via keybase install --components=app which has more privileges")

			// Unzip and get source path
			unzipPath, err := util.UnzipPath(localPath, c.log)
			defer util.RemoveFileAtPath(unzipPath)
			if err != nil {
				return err
			}
			sourcePath := filepath.Join(unzipPath, filepath.Base(destinationPath))

			_, installErr := command.Exec(c.config.keybasePath(), []string{"install", "--components=app", fmt.Sprintf("--source-path=%s", sourcePath)}, time.Minute, c.log)
			if installErr != nil {
				c.log.Errorf("Error trying to install the app (privileged): %s", installErr)
				return installErr
			}
		} else {
			return err
		}
	default:
		return err
	}

	// Update spotlight
	c.log.Debugf("Updating spotlight: %s", destinationPath)
	spotlightResult, spotLightErr := command.Exec("/usr/bin/mdimport", []string{destinationPath}, 20*time.Second, c.log)
	if spotLightErr != nil {
		c.log.Warningf("Error trying to update spotlight: %s, (%s)", spotLightErr, spotlightResult.CombinedOutput())
	}

	// Remove quantantine (if any)
	c.log.Debugf("Remove quarantine: %s", destinationPath)
	quarantineResult, quarantineErr := command.Exec("/usr/bin/xattr", []string{"-d", "com.apple.quarantine", destinationPath}, 20*time.Second, c.log)
	if quarantineErr != nil {
		c.log.Warningf("Error trying to remove quarantine: %s, (%s)", quarantineErr, quarantineResult.CombinedOutput())
	}

	return nil
}
<commit_msg>osx: fix compile for redirector uninstall<commit_after>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package keybase

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/kardianos/osext"
	"github.com/keybase/go-updater"
	"github.com/keybase/go-updater/command"
	"github.com/keybase/go-updater/process"
	"github.com/keybase/go-updater/util"
)

// execPath returns the app bundle path where this executable is located
func (c config) execPath() string {
	path, err := osext.Executable()
	if err != nil {
		c.log.Warningf("Error trying to determine our executable path: %s", err)
		return ""
	}
	return path
}

// destinationPath returns the app bundle path where this executable is located
func (c config) destinationPath() string {
	return appBundleForPath(c.execPath())
}

func appBundleForPath(path string) string {
	if path == "" {
		return ""
	}
	paths := strings.SplitN(path, ".app", 2)
	// If no match, return ""
	if len(paths) <= 1 {
		return ""
	}
	return paths[0] + ".app"
}

func libraryDir() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	return filepath.Join(usr.HomeDir, "Library"), nil
}

// Dir returns where to store config
func Dir(appName string) (string, error) {
	if appName == "" {
		return "", fmt.Errorf("No app name for dir")
	}
	libDir, err := libraryDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(libDir, "Application Support", appName), nil
}

// CacheDir returns where to store temporary files
func CacheDir(appName string) (string, error) {
	if appName == "" {
		return "", fmt.Errorf("No app name for dir")
	}
	return filepath.Join(os.TempDir(), appName), nil
}

// LogDir is where to log
func LogDir(appName string) (string, error) {
	libDir, err := libraryDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(libDir, "Logs"), nil
}

func (c config) osVersion() string {
	result, err := command.Exec("/usr/bin/sw_vers", []string{"-productVersion"}, 5*time.Second, c.log)
	if err != nil {
		c.log.Warningf("Error trying to determine OS version: %s (%s)", err, result.CombinedOutput())
		return ""
	}
	return strings.TrimSpace(result.Stdout.String())
}

func (c config) osArch() string {
	cmd := exec.Command("uname", "-m")
	var buf bytes.Buffer
	cmd.Stdout = &buf
	err := cmd.Run()
	if err != nil {
		return runtime.GOARCH
	}
	return buf.String()
}

func (c config) promptProgram() (command.Program, error) {
	destinationPath := c.destinationPath()
	if destinationPath == "" {
		return command.Program{}, fmt.Errorf("No destination path")
	}
	return command.Program{
		Path: filepath.Join(destinationPath, "Contents", "Resources", "KeybaseUpdater.app", "Contents", "MacOS", "Updater"),
	}, nil
}

func (c config) notifyProgram() string {
	// No notify program for Darwin
	return ""
}

func (c context) BeforeUpdatePrompt(update updater.Update, options updater.UpdateOptions) error {
	return nil
}

// UpdatePrompt is called when the user needs to accept an update
func (c context) UpdatePrompt(update updater.Update, options updater.UpdateOptions, promptOptions updater.UpdatePromptOptions) (*updater.UpdatePromptResponse, error) {
	promptProgram, err := c.config.promptProgram()
	if err != nil {
		return nil, err
	}
	return c.updatePrompt(promptProgram, update, options, promptOptions, time.Hour)
}

// PausedPrompt is called when the we can't update cause the app is in use.
// We return true if the use wants to cancel the update.
func (c context) PausedPrompt() bool {
	promptProgram, err := c.config.promptProgram()
	if err != nil {
		c.log.Warningf("Error trying to get prompt path: %s", err)
		return false
	}
	cancelUpdate, err := c.pausedPrompt(promptProgram, 5*time.Minute)
	if err != nil {
		c.log.Warningf("Error in paused prompt: %s", err)
		return false
	}
	return cancelUpdate
}

const serviceInBundlePath = "/Contents/SharedSupport/bin/keybase"
const kbfsInBundlePath = "/Contents/SharedSupport/bin/kbfs"

type processPaths struct {
	serviceProcPath string
	kbfsProcPath    string
	appPath         string
	appProcPath     string
}

func (c context) lookupProcessPaths() (p processPaths, _ error) {
	appPath := c.config.destinationPath() // "/Applications/Keybase.app"
	if appPath == "" {
		return p, fmt.Errorf("No app path")
	}
	appBundleName := filepath.Base(appPath) // "Keybase.app"

	p.appPath = appPath
	p.serviceProcPath = appBundleName + serviceInBundlePath
	p.kbfsProcPath = appBundleName + kbfsInBundlePath
	p.appProcPath = appBundleName + "/Contents/MacOS/"

	return p, nil
}

// stop will quit the app and any services
func (c context) stop() error {
	// Stop app
	appExitResult, appExitErr := command.Exec(c.config.keybasePath(), []string{"ctl", "app-exit"}, 30*time.Second, c.log)
	c.log.Infof("Stop output: %s", appExitResult.CombinedOutput())
	if appExitErr != nil {
		c.log.Warningf("Error requesting app exit: %s", appExitErr)
	}

	// Stop the redirector so it can be upgraded for all users.
	_, redirectorErr := command.Exec(c.config.keybasePath(), []string{"uninstall", "--components=redirector"}, time.Minute, c.log)
	if redirectorErr != nil {
		c.log.Warningf("Error stopping the redirector: %s", redirectorErr)
	}

	// Stop services
	servicesExitResult, servicesExitErr := command.Exec(c.config.keybasePath(), []string{"ctl", "stop", "--include=service,kbfs"}, 30*time.Second, c.log)
	c.log.Infof("Stop output: %s", servicesExitResult.CombinedOutput())
	if servicesExitErr != nil {
		c.log.Warningf("Error stopping services: %s", servicesExitErr)
	}

	paths, err := c.lookupProcessPaths()
	if err != nil {
		return err
	}

	// SIGKILL the app if it failed to exit (if it exited, then this doesn't do anything)
	c.log.Infof("Killing (if failed to exit) %s", paths.appProcPath)
	process.KillAll(process.NewMatcher(paths.appProcPath, process.PathContains, c.log), c.log)

	return nil
}

// AfterApply is called after an update is applied
func (c context) AfterApply(update updater.Update) error {
	if err := c.stop(); err != nil {
		c.log.Warningf("Error trying to stop: %s", err)
	}

	if err := c.start(10*time.Second, time.Second); err != nil {
		c.log.Warningf("Error trying to start the app: %s", err)
	}
	return nil
}

// Start the app.
// The wait is how log to wait for processes and the app to start before
// reporting that an error occurred.
func (c context) start(wait time.Duration, delay time.Duration) error {
	procPaths, err := c.lookupProcessPaths()
	if err != nil {
		return err
	}

	if err := OpenAppDarwin(procPaths.appPath, c.log); err != nil {
		c.log.Warningf("Error opening app: %s", err)
	}

	// Check to make sure processes started
	c.log.Debugf("Checking processes: %#v", procPaths)
	serviceProcErr := c.checkProcess(procPaths.serviceProcPath, wait, delay)
	kbfsProcErr := c.checkProcess(procPaths.kbfsProcPath, wait, delay)
	appProcErr := c.checkProcess(procPaths.appProcPath, wait, delay)

	return util.CombineErrors(serviceProcErr, kbfsProcErr, appProcErr)
}

func (c context) checkProcess(match string, wait time.Duration, delay time.Duration) error {
	matcher := process.NewMatcher(match, process.PathContains, c.log)
	procs, err := process.FindProcesses(matcher, wait, delay, c.log)
	if err != nil {
		return fmt.Errorf("Error checking on process (%s): %s", match, err)
	}
	if len(procs) == 0 {
		return fmt.Errorf("No process found for %s", match)
	}
	return nil
}

// OpenAppDarwin starts an app
func OpenAppDarwin(appPath string, log process.Log) error {
	return openAppDarwin("/usr/bin/open", appPath, time.Second, log)
}

func openAppDarwin(bin string, appPath string, retryDelay time.Duration, log process.Log) error {
	tryOpen := func() error {
		env := append(os.Environ(), "KEYBASE_RESTORE_UI=true", "KEYBASE_START_UI=hideWindow", "KEYBASE_OPEN_FROM=updater")
		result, err := command.ExecWithEnv(bin, []string{appPath}, env, time.Minute, log)
		if err != nil {
			return fmt.Errorf("Open error: %s; %s", err, result.CombinedOutput())
		}
		return nil
	}
	// We need to try 10 times because Gatekeeper has some issues, for example,
	// http://www.openradar.me/23614087
	var err error
	for i := 0; i < 10; i++ {
		err = tryOpen()
		if err == nil {
			break
		}
		log.Errorf("Open error (trying again in %s): %s", retryDelay, err)
		time.Sleep(retryDelay)
	}
	return err
}

func (c context) check(sourcePath string, destinationPath string) error {
	// Check to make sure the update source path is a real directory
	ok, err := util.IsDirReal(sourcePath)
	if err != nil {
		return err
	}
	if !ok {
		return fmt.Errorf("Source path isn't a directory")
	}
	return nil
}

func (c context) apply(localPath string, destinationPath string, tmpDir string) error {
	// The file name we unzip over should match the (base) file in the destination path
	filename := filepath.Base(destinationPath)
	return util.UnzipOver(localPath, filename, destinationPath, c.check, tmpDir, c.log)
}

func (c context) Apply(update updater.Update, options updater.UpdateOptions, tmpDir string) error {
	if update.Asset == nil {
		return fmt.Errorf("No asset")
	}
	localPath := update.Asset.LocalPath
	destinationPath := options.DestinationPath

	err := c.apply(localPath, destinationPath, tmpDir)
	switch err := err.(type) {
	case nil:
	case *os.LinkError:
		if err.Op == "rename" && err.Old == "/Applications/Keybase.app" {
			c.log.Infof("The error was a problem renaming (moving) the app, let's trying installing the app via keybase install --components=app which has more privileges")

			// Unzip and get source path
			unzipPath, err := util.UnzipPath(localPath, c.log)
			defer util.RemoveFileAtPath(unzipPath)
			if err != nil {
				return err
			}
			sourcePath := filepath.Join(unzipPath, filepath.Base(destinationPath))

			_, installErr := command.Exec(c.config.keybasePath(), []string{"install", "--components=app", fmt.Sprintf("--source-path=%s", sourcePath)}, time.Minute, c.log)
			if installErr != nil {
				c.log.Errorf("Error trying to install the app (privileged): %s", installErr)
				return installErr
			}
		} else {
			return err
		}
	default:
		return err
	}

	// Update spotlight
	c.log.Debugf("Updating spotlight: %s", destinationPath)
	spotlightResult, spotLightErr := command.Exec("/usr/bin/mdimport", []string{destinationPath}, 20*time.Second, c.log)
	if spotLightErr != nil {
		c.log.Warningf("Error trying to update spotlight: %s, (%s)", spotLightErr, spotlightResult.CombinedOutput())
	}

	// Remove quantantine (if any)
	c.log.Debugf("Remove quarantine: %s", destinationPath)
	quarantineResult, quarantineErr := command.Exec("/usr/bin/xattr", []string{"-d", "com.apple.quarantine", destinationPath}, 20*time.Second, c.log)
	if quarantineErr != nil {
		c.log.Warningf("Error trying to remove quarantine: %s, (%s)", quarantineErr, quarantineResult.CombinedOutput())
	}

	return nil
}
<|endoftext|>


<commit_before>package ssdp

import "net"

// Interfaces specify target interfaces to multicast.  If no interfaces are
// specified, all interfaces will be used.
var Interfaces []net.Interface

func interfaces() []net.Interface {
	if Interfaces == nil {
		Interfaces = interfacesIPv4()
	}
	return Interfaces
}

func interfacesIPv4() []net.Interface {
	iflist, err := net.Interfaces()
	if err != nil {
		logf("failed to list interfaces: %s", err)
		return make([]net.Interface, 0)
	}
	list := make([]net.Interface, 0, len(iflist))
	for _, ifi := range iflist {
		if !hasIPv4Address(&ifi) {
			continue
		}
		list = append(list, ifi)
	}
	return list
}

func hasIPv4Address(ifi *net.Interface) bool {
	if ifi.Flags&net.FlagUp == 0 {
		return false
	}
	addrs, err := ifi.Addrs()
	if err != nil {
		return false
	}
	for _, a := range addrs {
		ip, _, err := net.ParseCIDR(a.String())
		if err != nil {
			continue
		}
		if len(ip.To4()) == net.IPv4len && !ip.IsUnspecified() {
			return true
		}
	}
	return false
}
<commit_msg>separate link-up check<commit_after>package ssdp

import "net"

// Interfaces specify target interfaces to multicast.  If no interfaces are
// specified, all interfaces will be used.
var Interfaces []net.Interface

func interfaces() []net.Interface {
	if Interfaces == nil {
		Interfaces = interfacesIPv4()
	}
	return Interfaces
}

func interfacesIPv4() []net.Interface {
	iflist, err := net.Interfaces()
	if err != nil {
		logf("failed to list interfaces: %s", err)
		return make([]net.Interface, 0)
	}
	list := make([]net.Interface, 0, len(iflist))
	for _, ifi := range iflist {
		if !hasLinkUp(&ifi) || !hasIPv4Address(&ifi) {
			continue
		}
		list = append(list, ifi)
	}
	return list
}

// hasLinkUp checks an I/F have link-up or not.
func hasLinkUp(ifi *net.Interface) bool {
	return ifi.Flags&net.FlagUp != 0
}

// hasIPv4Address checks an I/F have IPv4 address.
func hasIPv4Address(ifi *net.Interface) bool {
	addrs, err := ifi.Addrs()
	if err != nil {
		return false
	}
	for _, a := range addrs {
		ip, _, err := net.ParseCIDR(a.String())
		if err != nil {
			continue
		}
		if len(ip.To4()) == net.IPv4len && !ip.IsUnspecified() {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package steam

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"regexp"
	"strconv"
)

// Due to the JSON being string, etc... we cannot re-use EconItem
// Also, "assetid" is included as "id" not as assetid.
type InventoryItem struct {
	AssetID    uint64        `json:"id,string,omitempty"`
	InstanceID uint64        `json:"instanceid,string,omitempty"`
	ClassID    uint64        `json:"classid,string,omitempty"`
	AppID      uint64        `json:"appid"`     // This!  (May be null; see desc if so)
	ContextID  uint64        `json:"contextid"` // Ditto  (May be null; see desc if so)
	Desc       *EconItemDesc `json:"-"`
}

type InventoryContext struct {
	ID         uint64 `json:"id,string"` /* Apparently context id needs at least 64 bits...  */
	AssetCount uint32 `json:"asset_count"`
	Name       string `json:"name"`
}

type InventoryAppStats struct {
	AppID            uint64                       `json:"appid"`
	Name             string                       `json:"name"`
	AssetCount       uint32                       `json:"asset_count"`
	Icon             string                       `json:"icon"`
	Link             string                       `json:"link"`
	InventoryLogo    string                       `json:"inventory_logo"`
	TradePermissions string                       `json:"trade_permissions"`
	Contexts         map[string]*InventoryContext `json:"rgContexts"`
}

var inventoryContextRegexp = regexp.MustCompile("var g_rgAppContextData = (.*?);")

func (session *Session) parseInventory(sid SteamID, appID, contextID uint64, start uint32, tradableOnly bool, items *[]*InventoryItem) (uint32, error) {
	params := url.Values{
		"start": {strconv.FormatUint(uint64(start), 10)},
	}
	if tradableOnly {
		params.Set("trading", "1")
	}

	resp, err := session.client.Get(fmt.Sprintf("https://steamcommunity.com/profiles/%d/inventory/json/%d/%d/?", sid, appID, contextID) + params.Encode())
	if err != nil {
		return 0, err
	}

	type Response struct {
		Success      bool            `json:"success"`
		ErrorMsg     string          `json:"Error"`
		MoreStart    interface{}     `json:"more_start"` // This can be a bool or a number...
		Inventory    json.RawMessage `json:"rgInventory"`
		Descriptions json.RawMessage `json:"rgDescriptions"`
		/* Missing: rgCurrency  */
	}

	var response Response
	if err = json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return 0, err
	}

	if !response.Success {
		return 0, errors.New(response.ErrorMsg)
	}

	var inventory map[string]*InventoryItem
	if err = json.Unmarshal(response.Inventory, &inventory); err != nil {
		// empty inventory...
		// NB: This only occurs on first run...
		return 0, nil
	}

	var descriptions map[string]*EconItemDesc
	if err = json.Unmarshal(response.Descriptions, &descriptions); err != nil {
		if inventory != nil && len(inventory) != 0 {
			return 0, err
		}

		return 0, nil
	}

	// Morph inventory into an array of items.
	// This is due to Steam returning the items in the following format:
	//	rgInventory: {
	//		"54xxx": {
	//			"id": "54xxx"
	//			...
	//		}
	//	}
	// We also glue the descriptions.
	for _, value := range inventory {
		if desc, ok := descriptions[strconv.FormatUint(value.ClassID, 10)+"_"+strconv.FormatUint(value.InstanceID, 10)]; ok {
			value.Desc = desc
		}

		*items = append(*items, value)
	}

	io.Copy(ioutil.Discard, resp.Body)
	switch v := response.MoreStart.(type) {
	case int:
		return uint32(v), nil
	case uint:
		return uint32(v), nil
	case bool:
		break
	default:
		return 0, fmt.Errorf("parseInventory: missing implementation for type %v", v)
	}

	return 0, nil
}

func (session *Session) GetInventory(sid SteamID, appID, contextID uint64, tradableOnly bool) ([]*InventoryItem, error) {
	items := []*InventoryItem{}
	more := uint32(0)

	for {
		next, err := session.parseInventory(sid, appID, contextID, more, tradableOnly, &items)
		if err != nil {
			return nil, err
		}

		if next == 0 {
			break
		}

		more = next
	}

	return items, nil
}

func (session *Session) GetInventoryAppStats(sid SteamID) (map[string]InventoryAppStats, error) {
	resp, err := session.client.Get("https://steamcommunity.com/profiles/" + sid.ToString() + "/inventory")
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	m := inventoryContextRegexp.FindSubmatch(body)
	if m == nil || len(m) != 2 {
		return nil, err
	}

	inven := map[string]InventoryAppStats{}
	if err = json.Unmarshal(m[1], &inven); err != nil {
		return nil, err
	}

	return inven, nil
}
<commit_msg>inventory: fix for empty inventories<commit_after>package steam

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"regexp"
	"strconv"
)

// Due to the JSON being string, etc... we cannot re-use EconItem
// Also, "assetid" is included as "id" not as assetid.
type InventoryItem struct {
	AssetID    uint64        `json:"id,string,omitempty"`
	InstanceID uint64        `json:"instanceid,string,omitempty"`
	ClassID    uint64        `json:"classid,string,omitempty"`
	AppID      uint64        `json:"appid"`     // This!  (May be null; see desc if so)
	ContextID  uint64        `json:"contextid"` // Ditto  (May be null; see desc if so)
	Desc       *EconItemDesc `json:"-"`
}

type InventoryContext struct {
	ID         uint64 `json:"id,string"` /* Apparently context id needs at least 64 bits...  */
	AssetCount uint32 `json:"asset_count"`
	Name       string `json:"name"`
}

type InventoryAppStats struct {
	AppID            uint64                       `json:"appid"`
	Name             string                       `json:"name"`
	AssetCount       uint32                       `json:"asset_count"`
	Icon             string                       `json:"icon"`
	Link             string                       `json:"link"`
	InventoryLogo    string                       `json:"inventory_logo"`
	TradePermissions string                       `json:"trade_permissions"`
	Contexts         map[string]*InventoryContext `json:"rgContexts"`
}

var inventoryContextRegexp = regexp.MustCompile("var g_rgAppContextData = (.*?);")

func (session *Session) parseInventory(sid SteamID, appID, contextID uint64, start uint32, tradableOnly bool, items *[]*InventoryItem) (uint32, error) {
	params := url.Values{
		"start": {strconv.FormatUint(uint64(start), 10)},
	}
	if tradableOnly {
		params.Set("trading", "1")
	}

	resp, err := session.client.Get(fmt.Sprintf("https://steamcommunity.com/profiles/%d/inventory/json/%d/%d/?", sid, appID, contextID) + params.Encode())
	if err != nil {
		return 0, err
	}

	type Response struct {
		Success      bool            `json:"success"`
		ErrorMsg     string          `json:"Error"`
		MoreStart    interface{}     `json:"more_start"` // This can be a bool or a number...
		Inventory    json.RawMessage `json:"rgInventory"`
		Descriptions json.RawMessage `json:"rgDescriptions"`
		/* Missing: rgCurrency  */
	}

	var response Response
	if err = json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return 0, err
	}

	if !response.Success {
		if len(response.ErrorMsg) != 0 {
			return 0, errors.New(response.ErrorMsg)
		}

		return 0, nil // empty inventory
	}

	var inventory map[string]*InventoryItem
	if err = json.Unmarshal(response.Inventory, &inventory); err != nil {
		// empty inventory...
		// NB: This only occurs on first run...
		return 0, nil
	}

	var descriptions map[string]*EconItemDesc
	if err = json.Unmarshal(response.Descriptions, &descriptions); err != nil {
		if inventory != nil && len(inventory) != 0 {
			return 0, err
		}

		return 0, nil
	}

	// Morph inventory into an array of items.
	// This is due to Steam returning the items in the following format:
	//	rgInventory: {
	//		"54xxx": {
	//			"id": "54xxx"
	//			...
	//		}
	//	}
	// We also glue the descriptions.
	for _, value := range inventory {
		if desc, ok := descriptions[strconv.FormatUint(value.ClassID, 10)+"_"+strconv.FormatUint(value.InstanceID, 10)]; ok {
			value.Desc = desc
		}

		*items = append(*items, value)
	}

	io.Copy(ioutil.Discard, resp.Body)
	switch v := response.MoreStart.(type) {
	case int:
		return uint32(v), nil
	case uint:
		return uint32(v), nil
	case bool:
		break
	default:
		return 0, fmt.Errorf("parseInventory: missing implementation for type %v", v)
	}

	return 0, nil
}

func (session *Session) GetInventory(sid SteamID, appID, contextID uint64, tradableOnly bool) ([]*InventoryItem, error) {
	items := []*InventoryItem{}
	more := uint32(0)

	for {
		next, err := session.parseInventory(sid, appID, contextID, more, tradableOnly, &items)
		if err != nil {
			return nil, err
		}

		if next == 0 {
			break
		}

		more = next
	}

	return items, nil
}

func (session *Session) GetInventoryAppStats(sid SteamID) (map[string]InventoryAppStats, error) {
	resp, err := session.client.Get("https://steamcommunity.com/profiles/" + sid.ToString() + "/inventory")
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	m := inventoryContextRegexp.FindSubmatch(body)
	if m == nil || len(m) != 2 {
		return nil, err
	}

	inven := map[string]InventoryAppStats{}
	if err = json.Unmarshal(m[1], &inven); err != nil {
		return nil, err
	}

	return inven, nil
}
<|endoftext|>


<commit_before>package pgx

import (
	"context"

	"github.com/jackc/pgconn"
	"github.com/jackc/pgtype"
	errors "golang.org/x/xerrors"
)

type batchItem struct {
	query             string
	arguments         []interface{}
	parameterOIDs     []pgtype.OID
	resultFormatCodes []int16
}

// Batch queries are a way of bundling multiple queries together to avoid
// unnecessary network round trips.
type Batch struct {
	conn  *Conn
	items []*batchItem
	err   error

	mrr *pgconn.MultiResultReader
}

// BeginBatch returns a *Batch query for c.
func (c *Conn) BeginBatch() *Batch {
	return &Batch{conn: c}
}

// Conn returns the underlying connection that b will or was performed on.
func (b *Batch) Conn() *Conn {
	return b.conn
}

// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement. parameterOIDs and
// resultFormatCodes should be nil if query is a prepared statement. Otherwise, parameterOIDs are required if there are
// parameters and resultFormatCodes are required if there is a result.
func (b *Batch) Queue(query string, arguments []interface{}, parameterOIDs []pgtype.OID, resultFormatCodes []int16) {
	b.items = append(b.items, &batchItem{
		query:             query,
		arguments:         arguments,
		parameterOIDs:     parameterOIDs,
		resultFormatCodes: resultFormatCodes,
	})
}

// Send sends all queued queries to the server at once. All queries are run in an implicit transaction unless explicit
// transaction control statements are executed.
func (b *Batch) Send(ctx context.Context) error {
	if b.err != nil {
		return b.err
	}

	batch := &pgconn.Batch{}

	for _, bi := range b.items {
		var parameterOIDs []pgtype.OID
		ps := b.conn.preparedStatements[bi.query]

		if ps != nil {
			parameterOIDs = ps.ParameterOIDs
		} else {
			parameterOIDs = bi.parameterOIDs
		}

		args, err := convertDriverValuers(bi.arguments)
		if err != nil {
			return err
		}

		paramFormats := make([]int16, len(args))
		paramValues := make([][]byte, len(args))
		for i := range args {
			paramFormats[i] = chooseParameterFormatCode(b.conn.ConnInfo, parameterOIDs[i], args[i])
			paramValues[i], err = newencodePreparedStatementArgument(b.conn.ConnInfo, parameterOIDs[i], args[i])
			if err != nil {
				return err
			}

		}

		if ps != nil {
			resultFormats := bi.resultFormatCodes
			if resultFormats == nil {
				resultFormats = make([]int16, len(ps.FieldDescriptions))
				for i := range resultFormats {
					if dt, ok := b.conn.ConnInfo.DataTypeForOID(ps.FieldDescriptions[i].DataType); ok {
						if _, ok := dt.Value.(pgtype.BinaryDecoder); ok {
							resultFormats[i] = BinaryFormatCode
						} else {
							resultFormats[i] = TextFormatCode
						}
					}
				}
			}

			batch.ExecPrepared(ps.Name, paramValues, paramFormats, resultFormats)
		} else {
			oids := make([]uint32, len(parameterOIDs))
			for i := 0; i < len(parameterOIDs); i++ {
				oids[i] = uint32(parameterOIDs[i])
			}
			batch.ExecParams(bi.query, paramValues, oids, paramFormats, bi.resultFormatCodes)
		}
	}

	b.mrr = b.conn.pgConn.ExecBatch(ctx, batch)

	return nil
}

// ExecResults reads the results from the next query in the batch as if the
// query has been sent with Exec.
func (b *Batch) ExecResults() (pgconn.CommandTag, error) {
	if !b.mrr.NextResult() {
		err := b.mrr.Close()
		if err == nil {
			err = errors.New("no result")
		}
		return nil, err
	}

	return b.mrr.ResultReader().Close()
}

// QueryResults reads the results from the next query in the batch as if the
// query has been sent with Query.
func (b *Batch) QueryResults() (Rows, error) {
	rows := b.conn.getRows("batch query", nil)

	if !b.mrr.NextResult() {
		rows.err = b.mrr.Close()
		if rows.err == nil {
			rows.err = errors.New("no result")
		}
		rows.closed = true
		return rows, rows.err
	}

	rows.resultReader = b.mrr.ResultReader()
	return rows, nil
}

// QueryRowResults reads the results from the next query in the batch as if the
// query has been sent with QueryRow.
func (b *Batch) QueryRowResults() Row {
	rows, _ := b.QueryResults()
	return (*connRow)(rows.(*connRows))

}

// Close closes the batch operation. Any error that occured during a batch
// operation may have made it impossible to resyncronize the connection with the
// server. In this case the underlying connection will have been closed.
func (b *Batch) Close() (err error) {
	return b.mrr.Close()
}

func (b *Batch) die(err error) {
	if b.err != nil {
		return
	}

	b.err = err
	b.conn.die(err)
}
<commit_msg>Remove Batch.die<commit_after>package pgx

import (
	"context"

	"github.com/jackc/pgconn"
	"github.com/jackc/pgtype"
	errors "golang.org/x/xerrors"
)

type batchItem struct {
	query             string
	arguments         []interface{}
	parameterOIDs     []pgtype.OID
	resultFormatCodes []int16
}

// Batch queries are a way of bundling multiple queries together to avoid
// unnecessary network round trips.
type Batch struct {
	conn  *Conn
	items []*batchItem
	err   error

	mrr *pgconn.MultiResultReader
}

// BeginBatch returns a *Batch query for c.
func (c *Conn) BeginBatch() *Batch {
	return &Batch{conn: c}
}

// Conn returns the underlying connection that b will or was performed on.
func (b *Batch) Conn() *Conn {
	return b.conn
}

// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement. parameterOIDs and
// resultFormatCodes should be nil if query is a prepared statement. Otherwise, parameterOIDs are required if there are
// parameters and resultFormatCodes are required if there is a result.
func (b *Batch) Queue(query string, arguments []interface{}, parameterOIDs []pgtype.OID, resultFormatCodes []int16) {
	b.items = append(b.items, &batchItem{
		query:             query,
		arguments:         arguments,
		parameterOIDs:     parameterOIDs,
		resultFormatCodes: resultFormatCodes,
	})
}

// Send sends all queued queries to the server at once. All queries are run in an implicit transaction unless explicit
// transaction control statements are executed.
func (b *Batch) Send(ctx context.Context) error {
	if b.err != nil {
		return b.err
	}

	batch := &pgconn.Batch{}

	for _, bi := range b.items {
		var parameterOIDs []pgtype.OID
		ps := b.conn.preparedStatements[bi.query]

		if ps != nil {
			parameterOIDs = ps.ParameterOIDs
		} else {
			parameterOIDs = bi.parameterOIDs
		}

		args, err := convertDriverValuers(bi.arguments)
		if err != nil {
			return err
		}

		paramFormats := make([]int16, len(args))
		paramValues := make([][]byte, len(args))
		for i := range args {
			paramFormats[i] = chooseParameterFormatCode(b.conn.ConnInfo, parameterOIDs[i], args[i])
			paramValues[i], err = newencodePreparedStatementArgument(b.conn.ConnInfo, parameterOIDs[i], args[i])
			if err != nil {
				return err
			}

		}

		if ps != nil {
			resultFormats := bi.resultFormatCodes
			if resultFormats == nil {
				resultFormats = make([]int16, len(ps.FieldDescriptions))
				for i := range resultFormats {
					if dt, ok := b.conn.ConnInfo.DataTypeForOID(ps.FieldDescriptions[i].DataType); ok {
						if _, ok := dt.Value.(pgtype.BinaryDecoder); ok {
							resultFormats[i] = BinaryFormatCode
						} else {
							resultFormats[i] = TextFormatCode
						}
					}
				}
			}

			batch.ExecPrepared(ps.Name, paramValues, paramFormats, resultFormats)
		} else {
			oids := make([]uint32, len(parameterOIDs))
			for i := 0; i < len(parameterOIDs); i++ {
				oids[i] = uint32(parameterOIDs[i])
			}
			batch.ExecParams(bi.query, paramValues, oids, paramFormats, bi.resultFormatCodes)
		}
	}

	b.mrr = b.conn.pgConn.ExecBatch(ctx, batch)

	return nil
}

// ExecResults reads the results from the next query in the batch as if the
// query has been sent with Exec.
func (b *Batch) ExecResults() (pgconn.CommandTag, error) {
	if !b.mrr.NextResult() {
		err := b.mrr.Close()
		if err == nil {
			err = errors.New("no result")
		}
		return nil, err
	}

	return b.mrr.ResultReader().Close()
}

// QueryResults reads the results from the next query in the batch as if the
// query has been sent with Query.
func (b *Batch) QueryResults() (Rows, error) {
	rows := b.conn.getRows("batch query", nil)

	if !b.mrr.NextResult() {
		rows.err = b.mrr.Close()
		if rows.err == nil {
			rows.err = errors.New("no result")
		}
		rows.closed = true
		return rows, rows.err
	}

	rows.resultReader = b.mrr.ResultReader()
	return rows, nil
}

// QueryRowResults reads the results from the next query in the batch as if the
// query has been sent with QueryRow.
func (b *Batch) QueryRowResults() Row {
	rows, _ := b.QueryResults()
	return (*connRow)(rows.(*connRows))

}

// Close closes the batch operation. Any error that occured during a batch
// operation may have made it impossible to resyncronize the connection with the
// server. In this case the underlying connection will have been closed.
func (b *Batch) Close() (err error) {
	return b.mrr.Close()
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup
	nextOpID    uint64
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:  logger,
		wrapped: wrapped,
	}

	return
}

// Log information for an operation with the given unique ID. calldepth is the
// depth to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint64,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"%v:%v] %v",
		path.Base(file),
		line,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	var bfReq bazilfuse.Request

	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		bfReq, err = c.wrapped.ReadRequest()
		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "Received: %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "Responding OK to Statfs.")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Convert it, if possible.
		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v)
		}

		if op = fuseops.Convert(bfReq, logForOp, &c.opsInFlight); op == nil {
			c.log(opID, 1, "Returning ENOSYS for unknown bazilfuse request: %v", bfReq)
			bfReq.RespondError(ENOSYS)
			continue
		}

		c.opsInFlight.Add(1)
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<commit_msg>Added the op ID.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup
	nextOpID    uint64
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:  logger,
		wrapped: wrapped,
	}

	return
}

// Log information for an operation with the given unique ID. calldepth is the
// depth to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint64,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%016x %v:%v] %v",
		opID,
		path.Base(file),
		line,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	var bfReq bazilfuse.Request

	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		bfReq, err = c.wrapped.ReadRequest()
		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "Received: %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "Responding OK to Statfs.")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Convert it, if possible.
		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v)
		}

		if op = fuseops.Convert(bfReq, logForOp, &c.opsInFlight); op == nil {
			c.log(opID, 1, "Returning ENOSYS for unknown bazilfuse request: %v", bfReq)
			bfReq.RespondError(ENOSYS)
			continue
		}

		c.opsInFlight.Add(1)
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from bazilfuse request ID (*not* the op ID for logging used above)
	// to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[bazilfuse.RequestID]func()
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:    logger,
		wrapped:   wrapped,
		parentCtx: parentCtx,
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	reqID bazilfuse.RequestID,
	f func())

// Set up state for an op that is about to be returned to the user, given its
// bazilfuse request ID.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(reqID bazilfuse.RequestID) (ctx context.Context) {
	// Note that the op is in flight.
	c.opsInFlight.Add(1)

	// Set up a cancellation function.
	ctx, cancel := context.WithCancel(c.parentCtx)
	c.recordCancelFunc(reqID, cancel)

	return
}

// Clean up all state associated with an op to which the user has responded.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(reqID bazilfuse.RequestID) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	cancel, ok := c.cancelFuncs[reqID]
	if !ok {
		panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
	}

	cancel()
	delete(c.cancelFuncs, reqID)

	// Decrement the in-flight counter.
	c.opsInFlight.Done()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		var bfReq bazilfuse.Request
		bfReq, err = c.wrapped.ReadRequest()

		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "<- %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Set up op dependencies.
		var reqID bazilfuse.RequestID = bfReq.Hdr().ID
		opCtx := c.beginOp(reqID)

		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp(reqID) }

		op = fuseops.Convert(opCtx, bfReq, logForOp, finished)
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<commit_msg>Connection.recordCancelFunc<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from bazilfuse request ID (*not* the op ID for logging used above)
	// to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[bazilfuse.RequestID]func()
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:    logger,
		wrapped:   wrapped,
		parentCtx: parentCtx,
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	reqID bazilfuse.RequestID,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[reqID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", reqID))
	}

	c.cancelFuncs[reqID] = f
}

// Set up state for an op that is about to be returned to the user, given its
// bazilfuse request ID.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(reqID bazilfuse.RequestID) (ctx context.Context) {
	// Note that the op is in flight.
	c.opsInFlight.Add(1)

	// Set up a cancellation function.
	ctx, cancel := context.WithCancel(c.parentCtx)
	c.recordCancelFunc(reqID, cancel)

	return
}

// Clean up all state associated with an op to which the user has responded.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(reqID bazilfuse.RequestID) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	cancel, ok := c.cancelFuncs[reqID]
	if !ok {
		panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
	}

	cancel()
	delete(c.cancelFuncs, reqID)

	// Decrement the in-flight counter.
	c.opsInFlight.Done()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		var bfReq bazilfuse.Request
		bfReq, err = c.wrapped.ReadRequest()

		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "<- %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Set up op dependencies.
		var reqID bazilfuse.RequestID = bfReq.Hdr().ID
		opCtx := c.beginOp(reqID)

		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp(reqID) }

		op = fuseops.Convert(opCtx, bfReq, logForOp, finished)
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/fsouza/go-dockerclient"
)

//Containers representing information about the running containers
type Containers struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Command string `json:"command"`
	Port    string `json:"port"`
}

//Post represents a post to the server
type Post struct {
	AgentID             string       `json:"agentid"`
	ExpiredAfterSeconds int          `json:"expireAfterSeconds"`
	Containers          []Containers `json:"containers"`
}

var (
	serverURL          string
	agentID            string
	refreshRate        int
	expireAfterSeconds int
)

func init() {
	flag.StringVar(&serverURL, "server", "http://localhost:8989", "The server uri where dockmaster is located.")
	flag.StringVar(&agentID, "agent", "localhost", "The name of an Agent. Example: TestQA1.")
	flag.IntVar(&refreshRate, "refresh", 60, "The rate at which this agent should check for changes in seconds.")
	flag.IntVar(&expireAfterSeconds, "expireAfterSeconds", 60, "The rate at which data sent by this agent should expire in seconds.")

	flag.Parse()
}

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)
	log.Println("Started listening... Refresh rate is :", refreshRate)
	for {
		log.Println("Assembling Post...")
		post := Post{AgentID: agentID, ExpiredAfterSeconds: expireAfterSeconds}
		containers := []Containers{}
		runningContainers, err := client.ListContainers(docker.ListContainersOptions{All: false})
		if err != nil {
			panic("Failed to connect to Docker Client." + err.Error())
		}
		for _, v := range runningContainers {
			c := Containers{}
			c.ID = v.ID
			c.Name = strings.Join(v.Names, ",")
			for _, p := range v.Ports {
				c.Port += p.IP + ":" + p.Type + ":" + strconv.Itoa(int(p.PrivatePort)) + ":" + strconv.Itoa(int(p.PublicPort))
			}
			c.Command = v.Command
			containers = append(containers, c)
		}
		post.Containers = containers

		postString, err := json.Marshal(post)
		if err != nil {
			log.Println("Error occured while trying ot marshal POST:", err.Error())
		}
		req, err := http.NewRequest("POST", serverURL+"/api/1/add", bytes.NewBuffer(postString))
		if err != nil {
			log.Println("Failed to create post request... Trying again later.")
			time.Sleep(time.Second * time.Duration(refreshRate))
			continue
		}

		req.Header.Set("Content-Type", "application/json")
		log.Println("Posting JSON:", string(postString))
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			log.Println("Failed to receive from server... Trying again later.")
			time.Sleep(time.Second * time.Duration(refreshRate))
			continue
		}
		defer resp.Body.Close()
		//TODO: Verify the response
		time.Sleep(time.Second * time.Duration(refreshRate))
	}
}
<commit_msg>Added first draft of a two way agent. Where agent is also a server.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/fsouza/go-dockerclient"
	"github.com/gin-gonic/gin"
)

//APIBASE The base of the API that this agent provides
const APIBASE = "/api/1"

//Containers representing information about the running containers
type Containers struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Command string `json:"command"`
	Port    string `json:"port"`
}

//Post represents a post to the server
type Post struct {
	AgentID             string       `json:"agentid"`
	ExpiredAfterSeconds int          `json:"expireAfterSeconds"`
	Containers          []Containers `json:"containers"`
}

var (
	serverURL          string
	agentID            string
	refreshRate        int
	expireAfterSeconds int
	port               string
)

func init() {
	flag.StringVar(&serverURL, "server", "http://localhost:8989", "The server uri where dockmaster is located.")
	flag.StringVar(&agentID, "agent", "localhost", "The name of an Agent. Example: TestQA1.")
	flag.IntVar(&refreshRate, "refresh", 60, "The rate at which this agent should check for changes in seconds.")
	flag.IntVar(&expireAfterSeconds, "expireAfterSeconds", 60, "The rate at which data sent by this agent should expire in seconds.")
	flag.StringVar(&port, "port", "9999", "The port number on which this agent is running on.")

	flag.Parse()
	go startDiscovering()
}

func main() {
	router := gin.Default()
	v1 := router.Group(APIBASE)
	{
		v1.POST("/order", orderContainer)
	}
	router.Run(":" + port)
}

func orderContainer(c *gin.Context) {
	var errorMessage struct {
		Error string `json:"error"`
	}
	var order struct {
		Command     string `json:"command"`
		ContainerID string `json:"id"`
	}

	err := c.BindJSON(&order)
	if err != nil {
		e := errorMessage
		e.Error = "error occured while unmarshaling order:" + err.Error()
		c.JSON(http.StatusBadRequest, e)
		return
	}

	//reflect.ValueOf(&t).MethodByName("Foo").Call([]reflect.Value{})
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)
	runningContainers, err := client.ListContainers(docker.ListContainersOptions{All: true})

	switch order.Command {
	case "start":
		for _, v := range runningContainers {
			if v.ID == order.ContainerID {
				client.StartContainer(order.ContainerID, &docker.HostConfig{})
			}
		}
	case "stop":
		for _, v := range runningContainers {
			client.StopContainer(v.ID, 1)
		}
	}
}

func startDiscovering() {
	endpoint := "unix:///var/run/docker.sock"
	client, _ := docker.NewClient(endpoint)
	log.Println("Started listening... Refresh rate is :", refreshRate)
	for {
		log.Println("Assembling Post...")
		post := Post{AgentID: agentID, ExpiredAfterSeconds: expireAfterSeconds}
		containers := []Containers{}
		runningContainers, err := client.ListContainers(docker.ListContainersOptions{All: false})
		if err != nil {
			panic("Failed to connect to Docker Client." + err.Error())
		}
		for _, v := range runningContainers {
			c := Containers{}
			c.ID = v.ID
			c.Name = strings.Join(v.Names, ",")
			for _, p := range v.Ports {
				c.Port += p.IP + ":" + p.Type + ":" + strconv.Itoa(int(p.PrivatePort)) + ":" + strconv.Itoa(int(p.PublicPort))
			}
			c.Command = v.Command
			containers = append(containers, c)
		}
		post.Containers = containers

		postString, err := json.Marshal(post)
		if err != nil {
			log.Println("Error occured while trying ot marshal POST:", err.Error())
		}
		req, err := http.NewRequest("POST", serverURL+"/api/1/add", bytes.NewBuffer(postString))
		if err != nil {
			log.Println("Failed to create post request... Trying again later.")
			time.Sleep(time.Second * time.Duration(refreshRate))
			continue
		}

		req.Header.Set("Content-Type", "application/json")
		log.Println("Posting JSON:", string(postString))
		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			log.Println("Failed to receive from server... Trying again later.")
			time.Sleep(time.Second * time.Duration(refreshRate))
			continue
		}
		defer resp.Body.Close()
		//TODO: Verify the response
		time.Sleep(time.Second * time.Duration(refreshRate))
	}
}
<|endoftext|>


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

// Package agent provides hooks programs can register to retrieve
// diagnostics data by using gops.
package agent

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	gosignal "os/signal"
	"runtime"
	"runtime/pprof"
	"strconv"
	"time"

	"github.com/google/gops/internal"
	"github.com/google/gops/signal"
)

// Start stars the gops agent on a host process. Once agent started,
// users can use the advanced gops features.
//
// Note: The agent exposes an endpoint via a TCP connection that can be used by
// any program on the system. Review your security requirements before
// starting the agent.
func Start() error {
	gopsdir, err := internal.ConfigDir()
	if err != nil {
		return err
	}
	err = os.MkdirAll(gopsdir, os.ModePerm)
	if err != nil {
		return err
	}

	// TODO(jbd): Expose these endpoints on HTTP. Then, we can enable
	// the agent on Windows systems.
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return err
	}
	port := l.Addr().(*net.TCPAddr).Port
	portfile := fmt.Sprintf("%s/%d", gopsdir, os.Getpid())
	err = ioutil.WriteFile(portfile, []byte(strconv.Itoa(port)), os.ModePerm)
	if err != nil {
		return err
	}

	c := make(chan os.Signal, 1)
	gosignal.Notify(c, os.Interrupt)
	go func() {
		// cleanup the socket on shutdown.
		<-c
		os.Remove(portfile)
		os.Exit(1)
	}()

	go func() {
		buf := make([]byte, 1)
		for {
			fd, err := l.Accept()
			if err != nil {
				fmt.Fprintf(os.Stderr, "gops: %v", err)
				continue
			}
			if _, err := fd.Read(buf); err != nil {
				fmt.Fprintf(os.Stderr, "gops: %v", err)
				continue
			}
			if err := handle(fd, buf); err != nil {
				fmt.Fprintf(os.Stderr, "gops: %v", err)
				continue
			}
			fd.Close()
		}
	}()
	return err
}

func handle(conn net.Conn, msg []byte) error {
	switch msg[0] {
	case signal.StackTrace:
		buf := make([]byte, 1<<16)
		n := runtime.Stack(buf, true)
		_, err := conn.Write(buf[:n])
		return err
	case signal.GC:
		runtime.GC()
		_, err := conn.Write([]byte("ok"))
		return err
	case signal.MemStats:
		var s runtime.MemStats
		runtime.ReadMemStats(&s)
		fmt.Fprintf(conn, "alloc: %v bytes\n", s.Alloc)
		fmt.Fprintf(conn, "total-alloc: %v bytes\n", s.TotalAlloc)
		fmt.Fprintf(conn, "sys: %v bytes\n", s.Sys)
		fmt.Fprintf(conn, "lookups: %v\n", s.Lookups)
		fmt.Fprintf(conn, "mallocs: %v\n", s.Mallocs)
		fmt.Fprintf(conn, "frees: %v\n", s.Frees)
		fmt.Fprintf(conn, "heap-alloc: %v bytes\n", s.HeapAlloc)
		fmt.Fprintf(conn, "heap-sys: %v bytes\n", s.HeapSys)
		fmt.Fprintf(conn, "heap-idle: %v bytes\n", s.HeapIdle)
		fmt.Fprintf(conn, "heap-in-use: %v bytes\n", s.HeapInuse)
		fmt.Fprintf(conn, "heap-released: %v bytes\n", s.HeapReleased)
		fmt.Fprintf(conn, "heap-objects: %v\n", s.HeapObjects)
		fmt.Fprintf(conn, "stack-in-use: %v bytes\n", s.StackInuse)
		fmt.Fprintf(conn, "stack-sys: %v bytes\n", s.StackSys)
		fmt.Fprintf(conn, "next-gc: when heap-alloc >= %v bytes\n", s.NextGC)
		fmt.Fprintf(conn, "last-gc: %v ns ago\n", s.LastGC)
		fmt.Fprintf(conn, "gc-pause: %v ns\n", s.PauseTotalNs)
		fmt.Fprintf(conn, "num-gc: %v\n", s.NumGC)
		fmt.Fprintf(conn, "enable-gc: %v\n", s.EnableGC)
		fmt.Fprintf(conn, "debug-gc: %v\n", s.DebugGC)
	case signal.Version:
		fmt.Fprintf(conn, "%v\n", runtime.Version())
	case signal.HeapProfile:
		pprof.Lookup("heap").WriteTo(conn, 0)
	case signal.CPUProfile:
		if err := pprof.StartCPUProfile(conn); err != nil {
			return nil
		}
		time.Sleep(30 * time.Second)
		pprof.StopCPUProfile()
	case signal.Vitals:
		fmt.Fprintf(conn, "goroutines: %v\n", runtime.NumGoroutine())
		fmt.Fprintf(conn, "OS threads: %v\n", pprof.Lookup("threadcreate").Count())
		fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0))
		fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU())
	}
	return nil
}
<commit_msg>agent: fix time ago issue (#14)<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package agent provides hooks programs can register to retrieve
// diagnostics data by using gops.
package agent

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	gosignal "os/signal"
	"runtime"
	"runtime/pprof"
	"strconv"
	"time"

	"github.com/google/gops/internal"
	"github.com/google/gops/signal"
)

// Start stars the gops agent on a host process. Once agent started,
// users can use the advanced gops features.
//
// Note: The agent exposes an endpoint via a TCP connection that can be used by
// any program on the system. Review your security requirements before
// starting the agent.
func Start() error {
	gopsdir, err := internal.ConfigDir()
	if err != nil {
		return err
	}
	err = os.MkdirAll(gopsdir, os.ModePerm)
	if err != nil {
		return err
	}

	// TODO(jbd): Expose these endpoints on HTTP. Then, we can enable
	// the agent on Windows systems.
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return err
	}
	port := l.Addr().(*net.TCPAddr).Port
	portfile := fmt.Sprintf("%s/%d", gopsdir, os.Getpid())
	err = ioutil.WriteFile(portfile, []byte(strconv.Itoa(port)), os.ModePerm)
	if err != nil {
		return err
	}

	c := make(chan os.Signal, 1)
	gosignal.Notify(c, os.Interrupt)
	go func() {
		// cleanup the socket on shutdown.
		<-c
		os.Remove(portfile)
		os.Exit(1)
	}()

	go func() {
		buf := make([]byte, 1)
		for {
			fd, err := l.Accept()
			if err != nil {
				fmt.Fprintf(os.Stderr, "gops: %v", err)
				continue
			}
			if _, err := fd.Read(buf); err != nil {
				fmt.Fprintf(os.Stderr, "gops: %v", err)
				continue
			}
			if err := handle(fd, buf); err != nil {
				fmt.Fprintf(os.Stderr, "gops: %v", err)
				continue
			}
			fd.Close()
		}
	}()
	return err
}

func handle(conn net.Conn, msg []byte) error {
	switch msg[0] {
	case signal.StackTrace:
		buf := make([]byte, 1<<16)
		n := runtime.Stack(buf, true)
		_, err := conn.Write(buf[:n])
		return err
	case signal.GC:
		runtime.GC()
		_, err := conn.Write([]byte("ok"))
		return err
	case signal.MemStats:
		var s runtime.MemStats
		runtime.ReadMemStats(&s)
		fmt.Fprintf(conn, "alloc: %v bytes\n", s.Alloc)
		fmt.Fprintf(conn, "total-alloc: %v bytes\n", s.TotalAlloc)
		fmt.Fprintf(conn, "sys: %v bytes\n", s.Sys)
		fmt.Fprintf(conn, "lookups: %v\n", s.Lookups)
		fmt.Fprintf(conn, "mallocs: %v\n", s.Mallocs)
		fmt.Fprintf(conn, "frees: %v\n", s.Frees)
		fmt.Fprintf(conn, "heap-alloc: %v bytes\n", s.HeapAlloc)
		fmt.Fprintf(conn, "heap-sys: %v bytes\n", s.HeapSys)
		fmt.Fprintf(conn, "heap-idle: %v bytes\n", s.HeapIdle)
		fmt.Fprintf(conn, "heap-in-use: %v bytes\n", s.HeapInuse)
		fmt.Fprintf(conn, "heap-released: %v bytes\n", s.HeapReleased)
		fmt.Fprintf(conn, "heap-objects: %v\n", s.HeapObjects)
		fmt.Fprintf(conn, "stack-in-use: %v bytes\n", s.StackInuse)
		fmt.Fprintf(conn, "stack-sys: %v bytes\n", s.StackSys)
		fmt.Fprintf(conn, "next-gc: when heap-alloc >= %v bytes\n", s.NextGC)
		fmt.Fprintf(conn, "last-gc: %v ns\n", s.LastGC)
		fmt.Fprintf(conn, "gc-pause: %v ns\n", s.PauseTotalNs)
		fmt.Fprintf(conn, "num-gc: %v\n", s.NumGC)
		fmt.Fprintf(conn, "enable-gc: %v\n", s.EnableGC)
		fmt.Fprintf(conn, "debug-gc: %v\n", s.DebugGC)
	case signal.Version:
		fmt.Fprintf(conn, "%v\n", runtime.Version())
	case signal.HeapProfile:
		pprof.Lookup("heap").WriteTo(conn, 0)
	case signal.CPUProfile:
		if err := pprof.StartCPUProfile(conn); err != nil {
			return nil
		}
		time.Sleep(30 * time.Second)
		pprof.StopCPUProfile()
	case signal.Vitals:
		fmt.Fprintf(conn, "goroutines: %v\n", runtime.NumGoroutine())
		fmt.Fprintf(conn, "OS threads: %v\n", pprof.Lookup("threadcreate").Count())
		fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0))
		fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU())
	}
	return nil
}
<|endoftext|>