<commit_before>// +build linux

/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package mount

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

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/nsenter"
)

const (
	// hostProcMountsPath is the default mount path for rootfs
	hostProcMountsPath = "/rootfs/proc/1/mounts"
	// hostProcMountinfoPath is the default mount info path for rootfs
	hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
)

// Currently, all docker containers receive their own mount namespaces.
// NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
type NsenterMounter struct {
	ne *nsenter.Nsenter
}

func NewNsenterMounter() *NsenterMounter {
	return &NsenterMounter{ne: nsenter.NewNsenter()}
}

// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})

// Mount runs mount(8) in the host's root mount namespace.  Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
	bind, bindRemountOpts := isBind(options)

	if bind {
		err := n.doNsenterMount(source, target, fstype, []string{"bind"})
		if err != nil {
			return err
		}
		return n.doNsenterMount(source, target, fstype, bindRemountOpts)
	}

	return n.doNsenterMount(source, target, fstype, options)
}

// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
	glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
	cmd, args := n.makeNsenterArgs(source, target, fstype, options)
	outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
	}
	return err
}

// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
	mountCmd := n.ne.AbsHostPath("mount")
	mountArgs := makeMountArgs(source, target, fstype, options)

	if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes
		//   host's systemd-run.
		// * systemd-run creates a transient scope (=~ cgroup) and executes its
		//   argument (/bin/mount) there.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		//   (systemd-run --scope finishes at this point, returning mount's exit
		//   code and stdout/stderr - thats one of --scope benefits).
		// * systemd keeps the fuse daemon running in the scope (i.e. in its own
		//   cgroup) until the fuse daemon dies (another --scope benefit).
		//   Kubelet container can be restarted and the fuse daemon survives.
		// * When the daemon dies (e.g. during unmount) systemd removes the
		//   scope automatically.
		mountCmd, mountArgs = addSystemdScope(systemdRunPath, target, mountCmd, mountArgs)
	} else {
		// Fall back to simple mount when the host has no systemd.
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes host's /bin/mount.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		// * Any fuse daemon runs in cgroup of kubelet docker container,
		//   restart of kubelet container will kill it!

		// No code here, mountCmd and mountArgs use /bin/mount
	}

	return mountCmd, mountArgs
}

// Unmount runs umount(8) in the host's mount namespace.
func (n *NsenterMounter) Unmount(target string) error {
	args := []string{target}
	// No need to execute systemd-run here, it's enough that unmount is executed
	// in the host's mount namespace. It will finish appropriate fuse daemon(s)
	// running in any scope.
	glog.V(5).Infof("nsenter unmount args: %v", args)
	outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
	}
	return err
}

// List returns a list of all mounted filesystems in the host's mount namespace.
func (*NsenterMounter) List() ([]MountPoint, error) {
	return listProcMounts(hostProcMountsPath)
}

func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
	return IsNotMountPoint(m, dir)
}

func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
	deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
	return (mp.Path == dir) || (mp.Path == deletedDir)
}

// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
// in the host's root mount namespace.
func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
	file, err := filepath.Abs(file)
	if err != nil {
		return true, err
	}

	// Check the directory exists
	if _, err = os.Stat(file); os.IsNotExist(err) {
		glog.V(5).Infof("findmnt: directory %s does not exist", file)
		return true, err
	}
	// Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get only
	// the first of multiple possible mountpoints using --first-only.
	// Also add fstype output to make sure that the output of target file will give the full path
	// TODO: Need more refactoring for this function. Track the solution with issue #26996
	args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
	glog.V(5).Infof("nsenter findmnt args: %v", args)
	out, err := n.ne.Exec("findmnt", args).CombinedOutput()
	if err != nil {
		glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
		// Different operating systems behave differently for paths which are not mount points.
		// On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
		// It's safer to assume that it's not a mount point.
		return true, nil
	}
	mountTarget, err := parseFindMnt(string(out))
	if err != nil {
		return false, err
	}

	glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)

	if mountTarget == file {
		glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
		return false, nil
	}
	glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
	return true, nil
}

// parse output of "findmnt -o target,fstype" and return just the target
func parseFindMnt(out string) (string, error) {
	// cut trailing newline
	out = strings.TrimSuffix(out, "\n")
	// cut everything after the last space - it's the filesystem type
	i := strings.LastIndex(out, " ")
	if i == -1 {
		return "", fmt.Errorf("error parsing findmnt output, expected at least one space: %q", out)
	}
	return out[:i], nil
}

// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device.
func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
	return exclusiveOpenFailsOnDevice(pathname)
}

// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
	pathType, err := n.GetFileType(pathname)
	isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
	return isDevice, err
}

//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return getDeviceNameFromMount(n, mountPath, pluginDir)
}

func (n *NsenterMounter) MakeRShared(path string) error {
	return doMakeRShared(path, hostProcMountinfoPath)
}

func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
	var pathType FileType
	outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
	if err != nil {
		return pathType, err
	}

	switch string(outputBytes) {
	case "socket":
		return FileTypeSocket, nil
	case "character special file":
		return FileTypeCharDev, nil
	case "block special file":
		return FileTypeBlockDev, nil
	case "directory":
		return FileTypeDirectory, nil
	case "regular file":
		return FileTypeFile, nil
	}

	return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

func (mounter *NsenterMounter) MakeDir(pathname string) error {
	args := []string{"-p", pathname}
	if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) MakeFile(pathname string) error {
	args := []string{pathname}
	if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
	args := []string{pathname}
	_, err := mounter.ne.Exec("ls", args).CombinedOutput()
	if err == nil {
		return true
	}
	return false
}
<commit_msg>Show findmt command output in case of error<commit_after>// +build linux

/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package mount

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

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/nsenter"
)

const (
	// hostProcMountsPath is the default mount path for rootfs
	hostProcMountsPath = "/rootfs/proc/1/mounts"
	// hostProcMountinfoPath is the default mount info path for rootfs
	hostProcMountinfoPath = "/rootfs/proc/1/mountinfo"
)

// Currently, all docker containers receive their own mount namespaces.
// NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
type NsenterMounter struct {
	ne *nsenter.Nsenter
}

func NewNsenterMounter() *NsenterMounter {
	return &NsenterMounter{ne: nsenter.NewNsenter()}
}

// NsenterMounter implements mount.Interface
var _ = Interface(&NsenterMounter{})

// Mount runs mount(8) in the host's root mount namespace.  Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
	bind, bindRemountOpts := isBind(options)

	if bind {
		err := n.doNsenterMount(source, target, fstype, []string{"bind"})
		if err != nil {
			return err
		}
		return n.doNsenterMount(source, target, fstype, bindRemountOpts)
	}

	return n.doNsenterMount(source, target, fstype, options)
}

// doNsenterMount nsenters the host's mount namespace and performs the
// requested mount.
func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
	glog.V(5).Infof("nsenter mount %s %s %s %v", source, target, fstype, options)
	cmd, args := n.makeNsenterArgs(source, target, fstype, options)
	outputBytes, err := n.ne.Exec(cmd, args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
	}
	return err
}

// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) (string, []string) {
	mountCmd := n.ne.AbsHostPath("mount")
	mountArgs := makeMountArgs(source, target, fstype, options)

	if systemdRunPath, hasSystemd := n.ne.SupportsSystemd(); hasSystemd {
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/systemd-run --description=... --scope -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes
		//   host's systemd-run.
		// * systemd-run creates a transient scope (=~ cgroup) and executes its
		//   argument (/bin/mount) there.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		//   (systemd-run --scope finishes at this point, returning mount's exit
		//   code and stdout/stderr - thats one of --scope benefits).
		// * systemd keeps the fuse daemon running in the scope (i.e. in its own
		//   cgroup) until the fuse daemon dies (another --scope benefit).
		//   Kubelet container can be restarted and the fuse daemon survives.
		// * When the daemon dies (e.g. during unmount) systemd removes the
		//   scope automatically.
		mountCmd, mountArgs = addSystemdScope(systemdRunPath, target, mountCmd, mountArgs)
	} else {
		// Fall back to simple mount when the host has no systemd.
		// Complete command line:
		// nsenter --mount=/rootfs/proc/1/ns/mnt -- /bin/mount -t <type> <what> <where>
		// Expected flow is:
		// * nsenter breaks out of container's mount namespace and executes host's /bin/mount.
		// * mount does its job, forks a fuse daemon if necessary and finishes.
		// * Any fuse daemon runs in cgroup of kubelet docker container,
		//   restart of kubelet container will kill it!

		// No code here, mountCmd and mountArgs use /bin/mount
	}

	return mountCmd, mountArgs
}

// Unmount runs umount(8) in the host's mount namespace.
func (n *NsenterMounter) Unmount(target string) error {
	args := []string{target}
	// No need to execute systemd-run here, it's enough that unmount is executed
	// in the host's mount namespace. It will finish appropriate fuse daemon(s)
	// running in any scope.
	glog.V(5).Infof("nsenter unmount args: %v", args)
	outputBytes, err := n.ne.Exec("umount", args).CombinedOutput()
	if len(outputBytes) != 0 {
		glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
	}
	return err
}

// List returns a list of all mounted filesystems in the host's mount namespace.
func (*NsenterMounter) List() ([]MountPoint, error) {
	return listProcMounts(hostProcMountsPath)
}

func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
	return IsNotMountPoint(m, dir)
}

func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
	deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
	return (mp.Path == dir) || (mp.Path == deletedDir)
}

// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
// in the host's root mount namespace.
func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
	file, err := filepath.Abs(file)
	if err != nil {
		return true, err
	}

	// Check the directory exists
	if _, err = os.Stat(file); os.IsNotExist(err) {
		glog.V(5).Infof("findmnt: directory %s does not exist", file)
		return true, err
	}
	// Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get only
	// the first of multiple possible mountpoints using --first-only.
	// Also add fstype output to make sure that the output of target file will give the full path
	// TODO: Need more refactoring for this function. Track the solution with issue #26996
	args := []string{"-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
	glog.V(5).Infof("nsenter findmnt args: %v", args)
	out, err := n.ne.Exec("findmnt", args).CombinedOutput()
	if err != nil {
		glog.V(2).Infof("Failed findmnt command for path %s: %s %v", file, out, err)
		// Different operating systems behave differently for paths which are not mount points.
		// On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
		// It's safer to assume that it's not a mount point.
		return true, nil
	}
	mountTarget, err := parseFindMnt(string(out))
	if err != nil {
		return false, err
	}

	glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)

	if mountTarget == file {
		glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
		return false, nil
	}
	glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
	return true, nil
}

// parse output of "findmnt -o target,fstype" and return just the target
func parseFindMnt(out string) (string, error) {
	// cut trailing newline
	out = strings.TrimSuffix(out, "\n")
	// cut everything after the last space - it's the filesystem type
	i := strings.LastIndex(out, " ")
	if i == -1 {
		return "", fmt.Errorf("error parsing findmnt output, expected at least one space: %q", out)
	}
	return out[:i], nil
}

// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
// Returns true if open returns errno EBUSY, and false if errno is nil.
// Returns an error if errno is any error other than EBUSY.
// Returns with error if pathname is not a device.
func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
	return exclusiveOpenFailsOnDevice(pathname)
}

// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
// to a device.
func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
	pathType, err := n.GetFileType(pathname)
	isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
	return isDevice, err
}

//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return getDeviceNameFromMount(n, mountPath, pluginDir)
}

func (n *NsenterMounter) MakeRShared(path string) error {
	return doMakeRShared(path, hostProcMountinfoPath)
}

func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
	var pathType FileType
	outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
	if err != nil {
		return pathType, err
	}

	switch string(outputBytes) {
	case "socket":
		return FileTypeSocket, nil
	case "character special file":
		return FileTypeCharDev, nil
	case "block special file":
		return FileTypeBlockDev, nil
	case "directory":
		return FileTypeDirectory, nil
	case "regular file":
		return FileTypeFile, nil
	}

	return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}

func (mounter *NsenterMounter) MakeDir(pathname string) error {
	args := []string{"-p", pathname}
	if _, err := mounter.ne.Exec("mkdir", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) MakeFile(pathname string) error {
	args := []string{pathname}
	if _, err := mounter.ne.Exec("touch", args).CombinedOutput(); err != nil {
		return err
	}
	return nil
}

func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
	args := []string{pathname}
	_, err := mounter.ne.Exec("ls", args).CombinedOutput()
	if err == nil {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/abates/hdhomerun"
	"net"
	"os"
	"path"
	"strings"
	"time"
)

func usage() {
	programName := path.Base(os.Args[0])
	fmt.Printf("Usage:\n")
	fmt.Printf("\t%s discover\n", programName)
	fmt.Printf("\t%s <id> get help\n", programName)
	fmt.Printf("\t%s <id> get <item>\n", programName)
	fmt.Printf("\t%s <id> set <item> <value>\n", programName)
	fmt.Printf("\t%s <id> scan <tuner> [<filename>]\n", programName)
	fmt.Printf("\t%s <id> save <tuner> <filename>\n", programName)
	fmt.Printf("\t%s <id> upgrade <filename>\n", programName)
	os.Exit(0)
}

func deviceFromId(id string) (*hdhomerun.Device, error) {
	var ip net.IP
	if strings.Contains(id, ".") {
		if strings.Contains(id, ":") {
			return nil, fmt.Errorf("Multicast not yet implemented")
		} else if strings.Contains(id, "-") {
			return nil, fmt.Errorf("Tuner number not yet implemented")
		} else {
			ipAddr, err := net.ResolveIPAddr("ip4", id)
			if err != nil {
				return nil, err
			}
			ip = ipAddr.IP
		}
	}

	for discoverResult := range hdhomerun.Discover(ip, time.Millisecond*200) {
		if discoverResult.Err != nil {
			return discoverResult.Device, discoverResult.Err
		}

		device := discoverResult.Device
		if ip != nil || device.ID() == id {
			device.Connect()
			return device, nil
		}
	}

	return nil, nil
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}

	if os.Args[1] == "discover" {
		for discoverResult := range hdhomerun.Discover(nil, time.Millisecond*200) {
			if discoverResult.Err != nil {
				fmt.Printf("Error during discovery: %v\n", discoverResult.Err)
			} else {
				fmt.Printf("hdhomerun device %s found at %s\n", discoverResult.Device.ID(), discoverResult.Device.Addr())
			}
		}
	} else {
		device, err := deviceFromId(os.Args[1])
		if device == nil || err != nil {
			fmt.Printf("Could not connect to device: %v\n", err)
			os.Exit(1)
		}

		switch os.Args[2] {
		case "get":
			if len(os.Args) < 4 {
				usage()
			}
			resp, err := device.Get(os.Args[3])
			if err != nil {
				fmt.Printf("Failed to get value: %v\n", err)
				os.Exit(1)
			}
			fmt.Printf("%s\n", resp)
		case "set":
			if len(os.Args) < 5 {
				usage()
			}
			resp, err := device.Set(os.Args[3], os.Args[4])
			if err != nil {
				fmt.Printf("Failed to set value: %v\n", err)
				os.Exit(1)
			}
			fmt.Printf("%s\n", resp)
		case "scan":
			tuner := device.Tuner(0)
			for channel := range tuner.Scan() {
				fmt.Printf("Channel %d\n", channel.Frequency)
				fmt.Printf("\tTSID: %d ONID: %d\n", channel.TSID, channel.ONID)
				for _, program := range channel.Programs {
					fmt.Printf("\t%s\n", program.Name)
				}
			}
		case "save":
			fmt.Printf("Not implemented\n")
		case "upgrade":
			fmt.Printf("Not implemented\n")
		default:
			fmt.Printf("Unknown command %s\n", os.Args[3])
			os.Exit(1)
		}
	}
}
<commit_msg>Updated for ID move to ScanResult<commit_after>package main

import (
	"fmt"
	"github.com/abates/hdhomerun"
	"net"
	"os"
	"path"
	"strings"
	"time"
)

func usage() {
	programName := path.Base(os.Args[0])
	fmt.Printf("Usage:\n")
	fmt.Printf("\t%s discover\n", programName)
	fmt.Printf("\t%s <id> get help\n", programName)
	fmt.Printf("\t%s <id> get <item>\n", programName)
	fmt.Printf("\t%s <id> set <item> <value>\n", programName)
	fmt.Printf("\t%s <id> scan <tuner> [<filename>]\n", programName)
	fmt.Printf("\t%s <id> save <tuner> <filename>\n", programName)
	fmt.Printf("\t%s <id> upgrade <filename>\n", programName)
	os.Exit(0)
}

func deviceFromId(id string) (*hdhomerun.Device, error) {
	var ip net.IP
	if strings.Contains(id, ".") {
		if strings.Contains(id, ":") {
			return nil, fmt.Errorf("Multicast not yet implemented")
		} else if strings.Contains(id, "-") {
			return nil, fmt.Errorf("Tuner number not yet implemented")
		} else {
			ipAddr, err := net.ResolveIPAddr("ip4", id)
			if err != nil {
				return nil, err
			}
			ip = ipAddr.IP
		}
	}

	for discoverResult := range hdhomerun.Discover(ip, time.Millisecond*200) {
		if discoverResult.Err != nil {
			return discoverResult.Device, discoverResult.Err
		}

		device := discoverResult.Device
		if ip != nil || discoverResult.ID.String() == id {
			device.Connect()
			return device, nil
		}
	}

	return nil, nil
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}

	if os.Args[1] == "discover" {
		for discoverResult := range hdhomerun.Discover(nil, time.Millisecond*200) {
			if discoverResult.Err != nil {
				fmt.Printf("Error during discovery: %v\n", discoverResult.Err)
			} else {
				fmt.Printf("hdhomerun device %s found at %s\n", discoverResult.ID, discoverResult.Device.Addr())
			}
		}
	} else {
		device, err := deviceFromId(os.Args[1])
		if device == nil || err != nil {
			fmt.Printf("Could not connect to device: %v\n", err)
			os.Exit(1)
		}

		switch os.Args[2] {
		case "get":
			if len(os.Args) < 4 {
				usage()
			}
			resp, err := device.Get(os.Args[3])
			if err != nil {
				fmt.Printf("Failed to get value: %v\n", err)
				os.Exit(1)
			}
			fmt.Printf("%s\n", resp)
		case "set":
			if len(os.Args) < 5 {
				usage()
			}
			resp, err := device.Set(os.Args[3], os.Args[4])
			if err != nil {
				fmt.Printf("Failed to set value: %v\n", err)
				os.Exit(1)
			}
			fmt.Printf("%s\n", resp)
		case "scan":
			tuner := device.Tuner(0)
			for channel := range tuner.Scan() {
				fmt.Printf("Channel %s:%d\n", channel.Modulation, channel.Frequency)
				fmt.Printf("\tTSID: %d ONID: %d\n", channel.TSID, channel.ONID)
				for _, program := range channel.Programs {
					fmt.Printf("\t%s\n", program.Name)
				}
			}
		case "save":
			fmt.Printf("Not implemented\n")
		case "upgrade":
			fmt.Printf("Not implemented\n")
		default:
			fmt.Printf("Unknown command %s\n", os.Args[3])
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 VMware, 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 ovf

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var data_vsphere = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment
     xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:ve="http://www.vmware.com/schema/ovfenv"
     oe:id=""
     ve:vCenterId="vm-12345">
   <PlatformSection>
      <Kind>VMware ESXi</Kind>
      <Version>5.5.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en</Locale>
   </PlatformSection>
   <PropertySection>
         <Property oe:key="foo" oe:value="42"/>
         <Property oe:key="bar" oe:value="0"/>
   </PropertySection>
   <ve:EthernetAdapterSection>
      <ve:Adapter ve:mac="00:00:00:00:00:00" ve:network="foo" ve:unitNumber="7"/>
   </ve:EthernetAdapterSection>
</Environment>`)

var data_vapprun = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     oe:id="CoreOS-vmw">
   <PlatformSection>
      <Kind>vapprun</Kind>
      <Version>1.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en_US</Locale>
   </PlatformSection>
   <PropertySection>
      <Property oe:key="foo" oe:value="42"/>
      <Property oe:key="bar" oe:value="0"/>
      <Property oe:key="guestinfo.user_data.url" oe:value="https://gist.githubusercontent.com/sigma/5a64aac1693da9ca70d2/raw/plop.yaml"/>
      <Property oe:key="guestinfo.user_data.doc" oe:value=""/>
      <Property oe:key="guestinfo.meta_data.url" oe:value=""/>
      <Property oe:key="guestinfo.meta_data.doc" oe:value=""/>
   </PropertySection>
</Environment>`)

func TestOvfEnvProperties(t *testing.T) {

	var testerFunc = func(env_str []byte) func() {
		return func() {
			env, err := ReadEnvironment(env_str)
			So(err, ShouldBeNil)
			props := env.Properties

			var val string
			var ok bool
			Convey(`Property "foo"`, func() {
				val, ok = props["foo"]
				So(ok, ShouldBeTrue)
				So(val, ShouldEqual, "42")
			})

			Convey(`Property "bar"`, func() {
				val, ok = props["bar"]
				So(ok, ShouldBeTrue)
				So(val, ShouldEqual, "0")
			})
		}
	}

	Convey("With vAppRun environment", t, testerFunc(data_vapprun))
	Convey("With vSphere environment", t, testerFunc(data_vsphere))
}

func TestOvfEnvPlatform(t *testing.T) {
	Convey("With vSphere environment", t, func() {
		env, err := ReadEnvironment(data_vsphere)
		So(err, ShouldBeNil)
		platform := env.Platform

		So(platform.Kind, ShouldEqual, "VMware ESXi")
		So(platform.Version, ShouldEqual, "5.5.0")
		So(platform.Vendor, ShouldEqual, "VMware, Inc.")
		So(platform.Locale, ShouldEqual, "en")
	})
}

func TestVappRunUserDataUrl(t *testing.T) {
	Convey("With vAppRun environment", t, func() {
		env, err := ReadEnvironment(data_vapprun)
		So(err, ShouldBeNil)
		props := env.Properties

		var val string
		var ok bool

		val, ok = props["guestinfo.user_data.url"]
		So(ok, ShouldBeTrue)
		So(val, ShouldEqual, "https://gist.githubusercontent.com/sigma/5a64aac1693da9ca70d2/raw/plop.yaml")
	})
}

func TestInvalidData(t *testing.T) {
	Convey("With invalid data", t, func() {
		_, err := ReadEnvironment(append(data_vsphere, []byte("garbage")...))
		So(err, ShouldBeNil)
	})
}
<commit_msg>providers/vmware: convert OVF tests to testify<commit_after>// Copyright 2014-2015 VMware, 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 ovf

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

var data_vsphere = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment
     xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:ve="http://www.vmware.com/schema/ovfenv"
     oe:id=""
     ve:vCenterId="vm-12345">
   <PlatformSection>
      <Kind>VMware ESXi</Kind>
      <Version>5.5.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en</Locale>
   </PlatformSection>
   <PropertySection>
         <Property oe:key="foo" oe:value="42"/>
         <Property oe:key="bar" oe:value="0"/>
   </PropertySection>
   <ve:EthernetAdapterSection>
      <ve:Adapter ve:mac="00:00:00:00:00:00" ve:network="foo" ve:unitNumber="7"/>
   </ve:EthernetAdapterSection>
</Environment>`)

var data_vapprun = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     oe:id="CoreOS-vmw">
   <PlatformSection>
      <Kind>vapprun</Kind>
      <Version>1.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en_US</Locale>
   </PlatformSection>
   <PropertySection>
      <Property oe:key="foo" oe:value="42"/>
      <Property oe:key="bar" oe:value="0"/>
      <Property oe:key="guestinfo.user_data.url" oe:value="https://gist.githubusercontent.com/sigma/5a64aac1693da9ca70d2/raw/plop.yaml"/>
      <Property oe:key="guestinfo.user_data.doc" oe:value=""/>
      <Property oe:key="guestinfo.meta_data.url" oe:value=""/>
      <Property oe:key="guestinfo.meta_data.doc" oe:value=""/>
   </PropertySection>
</Environment>`)

func TestOvfEnvProperties(t *testing.T) {
	var testOne = func(env_str []byte) {
		env, err := ReadEnvironment(env_str)
		assert.Nil(t, err)
		props := env.Properties

		var val string
		var ok bool
		val, ok = props["foo"]
		assert.True(t, ok)
		assert.Equal(t, val, "42")

		val, ok = props["bar"]
		assert.True(t, ok)
		assert.Equal(t, val, "0")
	}

	testOne(data_vapprun)
	testOne(data_vsphere)
}

func TestOvfEnvPlatform(t *testing.T) {
	env, err := ReadEnvironment(data_vsphere)
	assert.Nil(t, err)
	platform := env.Platform

	assert.Equal(t, platform.Kind, "VMware ESXi")
	assert.Equal(t, platform.Version, "5.5.0")
	assert.Equal(t, platform.Vendor, "VMware, Inc.")
	assert.Equal(t, platform.Locale, "en")
}

func TestVappRunUserDataUrl(t *testing.T) {
	env, err := ReadEnvironment(data_vapprun)
	assert.Nil(t, err)
	props := env.Properties

	var val string
	var ok bool

	val, ok = props["guestinfo.user_data.url"]
	assert.True(t, ok)
	assert.Equal(t, val, "https://gist.githubusercontent.com/sigma/5a64aac1693da9ca70d2/raw/plop.yaml")
}

func TestInvalidData(t *testing.T) {
	_, err := ReadEnvironment(append(data_vsphere, []byte("garbage")...))
	assert.Nil(t, err)
}
<|endoftext|>


<commit_before>package index

import (
	"fmt"
	"lucene/store"
)

const (
	CODEC_MAGIC = 0x3fd76c17
)

func CheckHeaderNoMagic(in *store.DataInput, codec string, minVersion, maxVersion int) (v int, err error) {
	actualCodec, err := in.ReadString()
	if err != nil {
		return 0, err
	}
	if actualCodec != codec {
		return 0, &CorruptIndexError{fmt.Sprintf(
			"codec mismatch: actual codec=%v vs expected codec=%v (resource: %v)", actualCodec, codec, in)}
	}

	actualVersion, err := in.ReadInt()
	if err != nil {
		return 0, err
	}
	if actualVersion < minVersion {
		return 0, NewIndexFormatTooOldError(in, actualVersion, minVersion, maxVersion)
	}
	if actualVersion > maxVersion {
		return 0, NewIndexFormatTooNewError(in, actualVersion, minVersion, maxVersion)
	}

	return actualVersion, nil
}
<commit_msg>add checkHeader method<commit_after>package index

import (
	"fmt"
	"lucene/store"
)

const (
	CODEC_MAGIC = 0x3fd76c17
)

func CheckHeader(in *store.DataInput, codec string, minVersion, maxVersion int) (v int, err error) {
	// Safety to guard against reading a bogus string:
	actualHeader, err := in.ReadInt()
	if err != nil {
		return 0, err
	}
	if actualHeader != CODEC_MAGIC {
		return 0, &CorruptIndexError{fmt.Sprintf(
			"codec header mismatch: actual header=%v vs expected header=%v (resource: %v)",
			actualHeader, CODEC_MAGIC, in)}
	}
	return CheckHeaderNoMagic(in, codec, minVersion, maxVersion)
}

func CheckHeaderNoMagic(in *store.DataInput, codec string, minVersion, maxVersion int) (v int, err error) {
	actualCodec, err := in.ReadString()
	if err != nil {
		return 0, err
	}
	if actualCodec != codec {
		return 0, &CorruptIndexError{fmt.Sprintf(
			"codec mismatch: actual codec=%v vs expected codec=%v (resource: %v)", actualCodec, codec, in)}
	}

	actualVersion, err := in.ReadInt()
	if err != nil {
		return 0, err
	}
	if actualVersion < minVersion {
		return 0, NewIndexFormatTooOldError(in, actualVersion, minVersion, maxVersion)
	}
	if actualVersion > maxVersion {
		return 0, NewIndexFormatTooNewError(in, actualVersion, minVersion, maxVersion)
	}

	return actualVersion, nil
}
<|endoftext|>


<commit_before>// Instruments allows you to collects metrics over discrete time intervals.
//
// Collected metrics will only reflect observations from last time window only,
// rather than including observations from prior windows, contrary to EWMA based metrics.
//
// 	timer := instruments.NewTimer(-1)
//
//	registry := instruments.NewRegistry()
//	registry.Register("processing-time", timer)
//
//	go reporter.Log("process", registry, time.Minute)
//
//	timer.Time(func() {
//	  ...
//	})
//
// Instruments support two types of instruments:
// Discrete instruments return a single value, and Sample instruments a sorted array of values.
//
// Theses base instruments are available:
//
// - Rate: tracks the rate of values per seconds.
// - Reservoir: randomly samples values.
// - Derive: tracks the rate of values based on the delta with previous value.
// - Gauge: tracks last value.
// - Timer: tracks durations.
//
// You can create custom instruments or compose new instruments form the built-in
// instruments as long as they implements the Sample or Discrete interfaces.
//
// Registry enforce the Discrete and Sample interfaces,
// creating a custom Reporter should be trivial, for example:
//
// 	for k, m := range r.Instruments() {
// 	 	switch i := m.(type) {
// 	 	case instruments.Discrete:
// 	 	 	s := i.Snapshot()
// 	 	 	report(k, s)
// 	 	case instruments.Sample:
// 	 	 	s := instruments.Quantile(i.Snapshot(), 0.95)
// 	 	 	report(k, s)
// 	 	}
//	}
//
package instruments

import (
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

const rateScale = 1e-9

// Represents a single value instrument.
type Discrete interface {
	Snapshot() int64
}

// Represents a sample instrument.
type Sample interface {
	Snapshot() []int64
}

// Track the rate of values per second.
type Rate struct {
	count int64
	time  int64
}

// Create a new rate instrument.
func NewRate() *Rate {
	return &Rate{
		time: time.Now().UnixNano(),
	}
}

// Update rate value.
func (r *Rate) Update(v int64) {
	atomic.AddInt64(&r.count, v)
}

// Return the number of values per second since the last snapshot,
// and reset the count to zero.
func (r *Rate) Snapshot() int64 {
	now := time.Now().UnixNano()
	t := atomic.SwapInt64(&r.time, now)
	c := atomic.SwapInt64(&r.count, 0)
	s := float64(c) / rateScale / float64(now-t)
	return Ceil(s)
}

// Track the rate of deltas per seconds.
type Derive struct {
	rate  *Rate
	value int64
}

// Create a new derive instruments.
func NewDerive(v int64) *Derive {
	return &Derive{
		value: v,
		rate:  NewRate(),
	}
}

// Update rate value based on the stored previous value.
func (d *Derive) Update(v int64) {
	p := atomic.SwapInt64(&d.value, v)
	d.rate.Update(v - p)
}

// Return the number of values per seconds since the last snapshot,
// and reset the count to zero.
func (d *Derive) Snapshot() int64 {
	return d.rate.Snapshot()
}

// Track a sample of values.
type Reservoir struct {
	size   int64
	values []int64
	m      sync.Mutex
}

const defaultReservoirSize = 1028

// Create a new reservoir of the given size.
// If size is negative, it will create a sample of DefaultReservoirSize size.
func NewReservoir(size int64) *Reservoir {
	if size <= 0 {
		size = defaultReservoirSize
	}
	return &Reservoir{
		values: make([]int64, size),
	}
}

// Fill the sample randomly with given value,
// for reference, see: http://en.wikipedia.org/wiki/Reservoir_sampling
func (r *Reservoir) Update(v int64) {
	r.m.Lock()
	defer r.m.Unlock()
	s := atomic.AddInt64(&r.size, 1)
	if int(s) <= len(r.values) {
		// Not full
		r.values[s-1] = v
	} else {
		// Full
		l := rand.Int63n(s)
		if int(l) < len(r.values) {
			r.values[l] = v
		}
	}
}

// Return sample as a sorted array.
func (r *Reservoir) Snapshot() []int64 {
	r.m.Lock()
	defer r.m.Unlock()
	s := atomic.SwapInt64(&r.size, 0)
	v := make([]int64, min(int(s), len(r.values)))
	copy(v, r.values)
	r.values = make([]int64, cap(r.values))
	sorted(v)
	return v
}

// Tracks a value.
type Gauge struct {
	value int64
}

// Creates a new Gauge with the given value.
func NewGauge(v int64) *Gauge {
	return &Gauge{
		value: v,
	}
}

// Update the current stored value.
func (g *Gauge) Update(v int64) {
	atomic.StoreInt64(&g.value, v)
}

// Return the current value.
func (g *Gauge) Snapshot() int64 {
	return atomic.LoadInt64(&g.value)
}

// Tracks durations.
type Timer struct {
	r *Reservoir
}

// Create a new Timer with the given sample size.
func NewTimer(size int64) *Timer {
	return &Timer{
		r: NewReservoir(size),
	}
}

// Add duration to the sample in ms.
func (t *Timer) Update(d time.Duration) {
	v := Floor(d.Seconds() * 1000)
	t.r.Update(v)
}

// Returns durations sample as a sorted array.
func (t *Timer) Snapshot() []int64 {
	return t.r.Snapshot()
}

// Records given function execution time.
func (t *Timer) Time(f func()) {
	ts := time.Now()
	f()
	t.Update(time.Since(ts))
}
<commit_msg>fix whitespace<commit_after>// Instruments allows you to collects metrics over discrete time intervals.
//
// Collected metrics will only reflect observations from last time window only,
// rather than including observations from prior windows, contrary to EWMA based metrics.
//
// 	timer := instruments.NewTimer(-1)
//
//	registry := instruments.NewRegistry()
//	registry.Register("processing-time", timer)
//
//	go reporter.Log("process", registry, time.Minute)
//
//	timer.Time(func() {
//	  ...
//	})
//
// Instruments support two types of instruments:
// Discrete instruments return a single value, and Sample instruments a sorted array of values.
//
// Theses base instruments are available:
//
// - Rate: tracks the rate of values per seconds.
//
// - Reservoir: randomly samples values.
//
// - Derive: tracks the rate of values based on the delta with previous value.
//
// - Gauge: tracks last value.
//
// - Timer: tracks durations.
//
// You can create custom instruments or compose new instruments form the built-in
// instruments as long as they implements the Sample or Discrete interfaces.
//
// Registry enforce the Discrete and Sample interfaces,
// creating a custom Reporter should be trivial, for example:
//
// 	for k, m := range r.Instruments() {
// 	 	switch i := m.(type) {
// 	 	case instruments.Discrete:
// 	 	 	s := i.Snapshot()
// 	 	 	report(k, s)
// 	 	case instruments.Sample:
// 	 	 	s := instruments.Quantile(i.Snapshot(), 0.95)
// 	 	 	report(k, s)
// 	 	}
//	}
//
package instruments

import (
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

const rateScale = 1e-9

// Represents a single value instrument.
type Discrete interface {
	Snapshot() int64
}

// Represents a sample instrument.
type Sample interface {
	Snapshot() []int64
}

// Track the rate of values per second.
type Rate struct {
	count int64
	time  int64
}

// Create a new rate instrument.
func NewRate() *Rate {
	return &Rate{
		time: time.Now().UnixNano(),
	}
}

// Update rate value.
func (r *Rate) Update(v int64) {
	atomic.AddInt64(&r.count, v)
}

// Return the number of values per second since the last snapshot,
// and reset the count to zero.
func (r *Rate) Snapshot() int64 {
	now := time.Now().UnixNano()
	t := atomic.SwapInt64(&r.time, now)
	c := atomic.SwapInt64(&r.count, 0)
	s := float64(c) / rateScale / float64(now-t)
	return Ceil(s)
}

// Track the rate of deltas per seconds.
type Derive struct {
	rate  *Rate
	value int64
}

// Create a new derive instruments.
func NewDerive(v int64) *Derive {
	return &Derive{
		value: v,
		rate:  NewRate(),
	}
}

// Update rate value based on the stored previous value.
func (d *Derive) Update(v int64) {
	p := atomic.SwapInt64(&d.value, v)
	d.rate.Update(v - p)
}

// Return the number of values per seconds since the last snapshot,
// and reset the count to zero.
func (d *Derive) Snapshot() int64 {
	return d.rate.Snapshot()
}

// Track a sample of values.
type Reservoir struct {
	size   int64
	values []int64
	m      sync.Mutex
}

const defaultReservoirSize = 1028

// Create a new reservoir of the given size.
// If size is negative, it will create a sample of DefaultReservoirSize size.
func NewReservoir(size int64) *Reservoir {
	if size <= 0 {
		size = defaultReservoirSize
	}
	return &Reservoir{
		values: make([]int64, size),
	}
}

// Fill the sample randomly with given value,
// for reference, see: http://en.wikipedia.org/wiki/Reservoir_sampling
func (r *Reservoir) Update(v int64) {
	r.m.Lock()
	defer r.m.Unlock()
	s := atomic.AddInt64(&r.size, 1)
	if int(s) <= len(r.values) {
		// Not full
		r.values[s-1] = v
	} else {
		// Full
		l := rand.Int63n(s)
		if int(l) < len(r.values) {
			r.values[l] = v
		}
	}
}

// Return sample as a sorted array.
func (r *Reservoir) Snapshot() []int64 {
	r.m.Lock()
	defer r.m.Unlock()
	s := atomic.SwapInt64(&r.size, 0)
	v := make([]int64, min(int(s), len(r.values)))
	copy(v, r.values)
	r.values = make([]int64, cap(r.values))
	sorted(v)
	return v
}

// Tracks a value.
type Gauge struct {
	value int64
}

// Creates a new Gauge with the given value.
func NewGauge(v int64) *Gauge {
	return &Gauge{
		value: v,
	}
}

// Update the current stored value.
func (g *Gauge) Update(v int64) {
	atomic.StoreInt64(&g.value, v)
}

// Return the current value.
func (g *Gauge) Snapshot() int64 {
	return atomic.LoadInt64(&g.value)
}

// Tracks durations.
type Timer struct {
	r *Reservoir
}

// Create a new Timer with the given sample size.
func NewTimer(size int64) *Timer {
	return &Timer{
		r: NewReservoir(size),
	}
}

// Add duration to the sample in ms.
func (t *Timer) Update(d time.Duration) {
	v := Floor(d.Seconds() * 1000)
	t.r.Update(v)
}

// Returns durations sample as a sorted array.
func (t *Timer) Snapshot() []int64 {
	return t.r.Snapshot()
}

// Records given function execution time.
func (t *Timer) Time(f func()) {
	ts := time.Now()
	f()
	t.Update(time.Since(ts))
}
<|endoftext|>


<commit_before>package interactive

import (
	"os"
	"fmt"

	"reflect"
	"sort"
	"strings"

	"go/parser"

	rl "code.google.com/p/go-gnureadline"
	//rl "github.com/sbinet/go-readline"
)

func Run(env *Env) {
	var line string
	term := os.ExpandEnv("TERM")

	names := completions(env)

	rl.SetAttemptedCompletionFunction(func(text string, start, end int) []string {
		if text == "" {
			return nil
		}

		n := len(names)
		top := sort.Search(n, func(i int) bool { return text <= names[i] })
		bot := sort.Search(n, func(i int) bool { return i > top && !strings.HasPrefix(names[i], text) })

		if bot == top {
			return nil
		} else if bot - top == 1 {
			return names[top:bot]
		} else {
			ntop := names[top]
			nbot := names[bot-1]
			n := len(ntop)
			if len(nbot) < n {
				n = len(nbot)
			}
			var i int
			for i = 0; i < n && ntop[i] == nbot[i]; i += 1 {}

			return append([]string{ntop[0:i]}, names[top:bot]...)
		}
	})

	line, rlerr := rl.Readline("go> ")
	for rlerr == nil && line != "quit" {

		//line = "func() {" + line + "}"
		if expr, err := parser.ParseExpr(line); err != nil {
			fmt.Printf("%s\n", err)
		} else if vals, _, err := evalExpr(expr, env); err != nil {
			fmt.Printf("%s\n", err)
		} else if len(vals) == 0 {
			fmt.Printf("void")
		} else if len(vals) == 1 {
			fmt.Printf("%v\n", vals[0].Interface())
		} else {
			sep := "("
			for _, v := range vals {
				fmt.Printf("%s%v", sep, v.Interface())
			}
			fmt.Printf(")\n")
		}

		line, rlerr = rl.Readline("go> ")

	}
	//WriteHistory("data/deleteme.history")
	rl.Rl_reset_terminal(term)
}

func completions (env *Env) (names []string) {
	prefix := env.Name
	if env.Name == "." {
		for k := range builtinTypes {
			names = append(names, k)
		}
		for k := range builtinFuncs {
			names = append(names, k)
		}
		for _, v := range env.Pkgs {
			names = append(names, completions(v)...)
		}
		prefix = ""
	}

	for k := range env.Vars {
		names = append(names, prefix + k)
	}

	for k := range env.Consts {
		names = append(names, prefix + k)
	}

	for k := range env.Funcs {
		names = append(names, k)
	}

	for k, v := range env.Types {
		names = append(names, prefix + k)
		if v.Kind() == reflect.Struct {
			for i := 0; i < v.NumField(); i += 1 {
				names = append(names, v.Field(i).Name)
			}
		}
		if v.Kind() == reflect.Struct || v.Kind() == reflect.Interface {
			for i := 0; i < v.NumMethod(); i += 1 {
				names = append(names, v.Method(i).Name)
			}
		}
	}
	sort.Strings(names)
	return names
}
<commit_msg>Fixed tab completing with no valid matches.<commit_after>package interactive

import (
	"os"
	"fmt"

	"reflect"
	"sort"
	"strings"

	"go/parser"

	rl "code.google.com/p/go-gnureadline"
)

func Run(env *Env) {
	var line string
	term := os.ExpandEnv("TERM")

	names := completions(env)

	rl.SetAttemptedCompletionFunction(func(text string, start, end int) []string {
		if text == "" {
			return nil
		}

		n := len(names)
		top := sort.Search(n, func(i int) bool { return text <= names[i] })
		bot := sort.Search(n, func(i int) bool { return i >= top && !strings.HasPrefix(names[i], text) })

		if bot == top {
			return nil
		} else if bot - top == 1 {
			return names[top:bot]
		} else {
			ntop := names[top]
			nbot := names[bot-1]
			n := len(ntop)
			if len(nbot) < n {
				n = len(nbot)
			}
			var i int
			for i = 0; i < n && ntop[i] == nbot[i]; i += 1 {}

			return append([]string{ntop[0:i]}, names[top:bot]...)
		}
	})

	line, rlerr := rl.Readline("go> ")
	for rlerr == nil && line != "quit" {

		//line = "func() {" + line + "}"
		if expr, err := parser.ParseExpr(line); err != nil {
			fmt.Printf("%s\n", err)
		} else if vals, _, err := evalExpr(expr, env); err != nil {
			fmt.Printf("%s\n", err)
		} else if len(vals) == 0 {
			fmt.Printf("void")
		} else if len(vals) == 1 {
			fmt.Printf("%v\n", vals[0].Interface())
		} else {
			sep := "("
			for _, v := range vals {
				fmt.Printf("%s%v", sep, v.Interface())
			}
			fmt.Printf(")\n")
		}

		line, rlerr = rl.Readline("go> ")

	}
	//WriteHistory("data/deleteme.history")
	rl.Rl_reset_terminal(term)
}

func completions (env *Env) (names []string) {
	prefix := env.Name
	if env.Name == "." {
		for k := range builtinTypes {
			names = append(names, k)
		}
		for k := range builtinFuncs {
			names = append(names, k)
		}
		for _, v := range env.Pkgs {
			names = append(names, completions(v)...)
		}
		prefix = ""
	}

	for k := range env.Vars {
		names = append(names, prefix + k)
	}

	for k := range env.Consts {
		names = append(names, prefix + k)
	}

	for k := range env.Funcs {
		names = append(names, k)
	}

	for k, v := range env.Types {
		names = append(names, prefix + k)
		if v.Kind() == reflect.Struct {
			for i := 0; i < v.NumField(); i += 1 {
				names = append(names, v.Field(i).Name)
			}
		}
		if v.Kind() == reflect.Struct || v.Kind() == reflect.Interface {
			for i := 0; i < v.NumMethod(); i += 1 {
				names = append(names, v.Method(i).Name)
			}
		}
	}
	sort.Strings(names)
	return names
}
<|endoftext|>


<commit_before>package spogoto

import (
	"fmt"
	"math/rand"
)

// Interpreter interprets Spogoto code.
type Interpreter interface {
	RandInt() int64
	RandFloat() float64
	Run(string) RunSet
}

type Options struct {
	MaxInstructions int64
}

var DefaultOptions = Options{
	MaxInstructions: 100,
}

type interpreter struct {
	Rand    *rand.Rand
	Parser  *Parser
	Options Options
}

func (i *interpreter) Run(code string) (r RunSet) {
	r = i.createRunSet()
	instructions := i.Parser.Parse(code)
	inCount := int64(len(instructions))
	r.Cursor().Instructions = instructions
	for r.Cursor().Position < inCount {
		instruction := instructions[r.Cursor().Position]
		t := instruction.Type
		fn := instruction.Function
		if t == "" {
			// No type so noop
			r.Cursor().Position++
			continue
		}

		if fn == "" {
			// Literal type
			r.Stack(t).PushLiteral(instruction.Value)
		} else if t == "cursor" {
			r.CursorCommand(fn)
		} else {
			// It's calling a function
			r.Stack(t).Call(fn, r, i)
		}

		r.Cursor().Position++
		r.IncrementInstructionCount()
		if r.InstructionCount() > i.Options.MaxInstructions {
			break
		}
	}

	return r
}

// RandInt generates a random integer between 0 and 9
func (i *interpreter) RandInt() int64 {
	return i.Rand.Int63n(10)
}

// RandFloat generates a random float number between 0 and 1
func (i *interpreter) RandFloat() float64 {
	return i.Rand.Float64()
}

// RandomLiteral
func (i *interpreter) RandomLiteral(t string) string {
	switch t {
	case "integer":
		return i.randomInteger()
	case "float":
		return i.randomFloat()
	default:
		return i.randomBoolean()
	}
}

func (i *interpreter) RandomSymbol() string {
	symbols := i.Parser.Symbols()
	idx := i.Rand.Int63n(int64(len(symbols)))
	return symbols[idx]
}

func (i *interpreter) randomBoolean() string {
	if i.RandFloat() > 0.5 {
		return "true"
	} else {
		return "false"
	}
}

func (i *interpreter) randomInteger() string {
	var sign = ""
	if i.RandFloat() > 0.5 {
		sign = "-"
	}
	return fmt.Sprintf("%s%d", sign, i.RandInt())
}

func (i *interpreter) randomFloat() string {
	var sign = ""
	if i.RandFloat() > 0.5 {
		sign = "-"
	}
	return fmt.Sprintf("%s%f", sign, i.RandFloat())
}

func (i *interpreter) setupParser(r RunSet) {
	p := NewParser()
	for t, stack := range r.DataStacks() {
		for fn, _ := range stack.Functions() {
			p.RegisterFunction(t, fn)
		}
	}

	for fnc, _ := range r.CursorCommands() {
		p.RegisterFunction("cursor", fnc)
	}

	i.Parser = p
}

// NewInterpreter constructs a new Intepreter.
func NewInterpreter(options Options) *interpreter {
	i := &interpreter{
		Rand:    rand.New(rand.NewSource(rand.Int63())),
		Options: options,
	}
	i.setupParser(i.createRunSet())

	return i
}

func (i *interpreter) createRunSet() *runset {
	return NewRunSet(i)
}
<commit_msg>Can now generate random code. All right.<commit_after>package spogoto

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
)

// Interpreter interprets Spogoto code.
type Interpreter interface {
	RandInt() int64
	RandFloat() float64
	Run(string) RunSet
}

type Options struct {
	MaxInstructions int64
}

var DefaultOptions = Options{
	MaxInstructions: 100,
}

type interpreter struct {
	Rand    *rand.Rand
	Parser  *Parser
	Options Options
}

func (i *interpreter) Run(code string) (r RunSet) {
	r = i.createRunSet()
	instructions := i.Parser.Parse(code)
	inCount := int64(len(instructions))
	r.Cursor().Instructions = instructions
	for r.Cursor().Position < inCount {
		instruction := instructions[r.Cursor().Position]
		t := instruction.Type
		fn := instruction.Function
		if t == "" {
			// No type so noop
			r.Cursor().Position++
			continue
		}

		if fn == "" {
			// Literal type
			r.Stack(t).PushLiteral(instruction.Value)
		} else if t == "cursor" {
			r.CursorCommand(fn)
		} else {
			// It's calling a function
			r.Stack(t).Call(fn, r, i)
		}

		r.Cursor().Position++
		r.IncrementInstructionCount()
		if r.InstructionCount() > i.Options.MaxInstructions {
			break
		}
	}

	return r
}

func (i *interpreter) RandomCode(length int64) string {
	var code []string
	var k int64
	for k = 0; k < length; k++ {
		code = append(code, i.RandomInstruction())
	}

	return strings.Join(code, " ")
}

// RandInt generates a random integer between 0 and 9
func (i *interpreter) RandInt() int64 {
	return i.Rand.Int63n(10)
}

// RandFloat generates a random float number between 0 and 1
func (i *interpreter) RandFloat() float64 {
	return i.Rand.Float64()
}

func (i *interpreter) RandomInstruction() string {
	if i.RandFloat() < 0.3 {
		return i.RandomLiteral([]string{"integer", "float", "boolean"}[i.Rand.Int63n(3)])
	} else {
		return i.RandomSymbol()
	}
}

// RandomLiteral
func (i *interpreter) RandomLiteral(t string) string {
	switch t {
	case "integer":
		return i.randomInteger()
	case "float":
		return i.randomFloat()
	default:
		return i.randomBoolean()
	}
}

func (i *interpreter) RandomSymbol() string {
	symbols := i.Parser.Symbols()
	idx := i.Rand.Int63n(int64(len(symbols)))
	return symbols[idx]
}

func (i *interpreter) randomBoolean() string {
	if i.RandFloat() > 0.5 {
		return "true"
	} else {
		return "false"
	}
}

func (i *interpreter) randomInteger() string {
	var sign = ""
	if i.RandFloat() > 0.5 {
		sign = "-"
	}
	return fmt.Sprintf("%s%d", sign, i.RandInt())
}

func (i *interpreter) randomFloat() string {
	var sign = ""
	if i.RandFloat() > 0.5 {
		sign = "-"
	}
	return fmt.Sprintf("%s%f", sign, i.RandFloat())
}

func (i *interpreter) setupParser(r RunSet) {
	p := NewParser()
	for t, stack := range r.DataStacks() {
		for fn, _ := range stack.Functions() {
			p.RegisterFunction(t, fn)
		}
	}

	for fnc, _ := range r.CursorCommands() {
		p.RegisterFunction("cursor", fnc)
	}

	i.Parser = p
}

// NewInterpreter constructs a new Intepreter.
func NewInterpreter(options Options) *interpreter {
	i := &interpreter{
		Rand:    rand.New(rand.NewSource(time.Now().UnixNano())),
		Options: options,
	}
	i.setupParser(i.createRunSet())

	return i
}

func (i *interpreter) createRunSet() *runset {
	return NewRunSet(i)
}
<|endoftext|>


<commit_before>package keys

import (
	"fmt"

	"github.com/kezhuw/leveldb/internal/endian"
)

type Sequence uint64

const (
	MaxSequence Sequence = (1 << 56) - 1
)

func (seq Sequence) Add(n uint64) Sequence {
	return seq + Sequence(n)
}

const (
	TagLen = 8
)

func PutTag(buf []byte, seq Sequence, kind Kind) {
	tag := PackTag(seq, kind)
	endian.PutUint64(buf, tag)
}

func PackTag(seq Sequence, kind Kind) uint64 {
	return (uint64(seq) << 8) | uint64(kind)
}

func ToInternalKey(key []byte) (InternalKey, bool) {
	if len(key) < TagLen {
		return nil, false
	}
	return key, true
}

func MakeInternalKey(buf []byte, key []byte, seq Sequence, kind Kind) InternalKey {
	copy(buf, key)
	PutTag(buf[len(key):], seq, kind)
	return InternalKey(buf)
}

func NewInternalKey(key []byte, seq Sequence, kind Kind) InternalKey {
	buf := make([]byte, len(key)+8)
	return MakeInternalKey(buf, key, seq, kind)
}

type InternalKey []byte

func (ikey InternalKey) Dup() InternalKey {
	dup := make([]byte, len(ikey))
	copy(dup, ikey)
	return dup
}

func (ikey InternalKey) UserKey() []byte {
	i := len(ikey) - 8
	return ikey[:i:i]
}

func (ikey InternalKey) Tag() uint64 {
	i := len(ikey) - 8
	return endian.Uint64(ikey[i:])
}

func (ikey InternalKey) Split() ([]byte, Sequence, Kind) {
	i := len(ikey) - 8
	seq := endian.Uint64(ikey[i:])
	return ikey[:i:i], Sequence(seq >> 8), Kind(seq & 0xFF)
}

func (ikey InternalKey) Split2() ([]byte, Sequence) {
	i := len(ikey) - 8
	return ikey[:i:i], Sequence(endian.Uint64(ikey[i:]))
}

type Kind int

const (
	Delete  = 0
	Value   = 1
	maxKind = Value
	Seek    = maxKind
)

func (k Kind) String() string {
	switch k {
	case Value:
		return "value setting"
	case Delete:
		return "value deletion"
	}
	return fmt.Sprintf("kind: %d", k)
}

type ParsedInternalKey struct {
	UserKey  []byte
	Kind     Kind
	Sequence Sequence
}

func (k *ParsedInternalKey) Append(dst []byte) []byte {
	var buf [8]byte
	endian.PutUint64(buf[:], k.Tag())
	dst = append(dst, k.UserKey...)
	return append(dst, buf[:]...)
}

func (k *ParsedInternalKey) Parse(key []byte) bool {
	i := len(key) - 8
	if i < 0 {
		return false
	}
	tag := endian.Uint64(key[i:])
	k.UserKey = key[:i:i]
	k.Kind = Kind(tag & 0xff)
	k.Sequence = Sequence(tag >> 8)
	return k.Kind <= maxKind
}

func (k *ParsedInternalKey) Tag() uint64 {
	return (uint64(k.Sequence) << 8) | uint64(k.Kind)
}
<commit_msg>Replace magic number with consts<commit_after>package keys

import (
	"fmt"

	"github.com/kezhuw/leveldb/internal/endian"
)

type Sequence uint64

const (
	MaxSequence Sequence = (1 << 56) - 1
)

func (seq Sequence) Add(n uint64) Sequence {
	return seq + Sequence(n)
}

const (
	TagLen   = 8
	kindBits = 8
)

func PutTag(buf []byte, seq Sequence, kind Kind) {
	tag := PackTag(seq, kind)
	endian.PutUint64(buf, tag)
}

func PackTag(seq Sequence, kind Kind) uint64 {
	return (uint64(seq) << kindBits) | uint64(kind)
}

func ToInternalKey(key []byte) (InternalKey, bool) {
	if len(key) < TagLen {
		return nil, false
	}
	return key, true
}

func MakeInternalKey(buf []byte, key []byte, seq Sequence, kind Kind) InternalKey {
	copy(buf, key)
	PutTag(buf[len(key):], seq, kind)
	return InternalKey(buf)
}

func NewInternalKey(key []byte, seq Sequence, kind Kind) InternalKey {
	buf := make([]byte, len(key)+TagLen)
	return MakeInternalKey(buf, key, seq, kind)
}

type InternalKey []byte

func (ikey InternalKey) Dup() InternalKey {
	dup := make([]byte, len(ikey))
	copy(dup, ikey)
	return dup
}

func (ikey InternalKey) UserKey() []byte {
	i := len(ikey) - TagLen
	return ikey[:i:i]
}

func (ikey InternalKey) Tag() uint64 {
	i := len(ikey) - TagLen
	return endian.Uint64(ikey[i:])
}

func (ikey InternalKey) Split() ([]byte, Sequence, Kind) {
	i := len(ikey) - TagLen
	seq := endian.Uint64(ikey[i:])
	return ikey[:i:i], Sequence(seq >> kindBits), Kind(seq & 0xFF)
}

func (ikey InternalKey) Split2() ([]byte, Sequence) {
	i := len(ikey) - TagLen
	return ikey[:i:i], Sequence(endian.Uint64(ikey[i:]))
}

type Kind int

const (
	Delete  = 0
	Value   = 1
	maxKind = Value
	Seek    = maxKind
)

func (k Kind) String() string {
	switch k {
	case Value:
		return "value setting"
	case Delete:
		return "value deletion"
	}
	return fmt.Sprintf("kind: %d", k)
}

type ParsedInternalKey struct {
	UserKey  []byte
	Kind     Kind
	Sequence Sequence
}

func (k *ParsedInternalKey) Append(dst []byte) []byte {
	var buf [TagLen]byte
	PutTag(buf[:], k.Sequence, k.Kind)
	dst = append(dst, k.UserKey...)
	return append(dst, buf[:]...)
}

func (k *ParsedInternalKey) Parse(key []byte) bool {
	i := len(key) - TagLen
	if i < 0 {
		return false
	}
	tag := endian.Uint64(key[i:])
	k.UserKey = key[:i:i]
	k.Kind = Kind(tag & 0xff)
	k.Sequence = Sequence(tag >> kindBits)
	return k.Kind <= maxKind
}

func (k *ParsedInternalKey) Tag() uint64 {
	return PackTag(k.Sequence, k.Kind)
}
<|endoftext|>


<commit_before>package registry

import (
	"net/http"

	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/registry"
	"github.com/cozy/cozy-stack/web/middlewares"
	webpermissions "github.com/cozy/cozy-stack/web/permissions"
	"github.com/cozy/echo"
)

type authType int

const (
	authed authType = iota
	perms
)

func proxyReq(auth authType, cacheControl registry.CacheControl) echo.HandlerFunc {
	return func(c echo.Context) error {
		i := middlewares.GetInstance(c)
		switch auth {
		case authed:
			if !middlewares.IsLoggedIn(c) {
				return echo.NewHTTPError(http.StatusForbidden)
			}
		case perms:
			pdoc, err := webpermissions.GetPermission(c)
			if err != nil || pdoc.Type != permissions.TypeWebapp {
				return echo.NewHTTPError(http.StatusForbidden)
			}
		default:
			panic("unknown authType")
		}
		registries, err := i.Registries()
		if err != nil {
			return err
		}
		req := c.Request()
		resp, err := registry.Proxy(req, registries, cacheControl)
		if err != nil {
			return err
		}
		defer resp.Body.Close()
		contentType := resp.Header.Get("content-type")
		return c.Stream(resp.StatusCode, contentType, resp.Body)
	}
}

func proxyListReq(c echo.Context) error {
	i := middlewares.GetInstance(c)
	pdoc, err := webpermissions.GetPermission(c)
	if err != nil || pdoc.Type != permissions.TypeWebapp {
		return echo.NewHTTPError(http.StatusForbidden)
	}
	registries, err := i.Registries()
	if err != nil {
		return err
	}
	req := c.Request()
	list, err := registry.ProxyList(req, registries)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, list)
}

// Routes sets the routing for the registry
func Routes(router *echo.Group) {
	router.GET("", proxyListReq)
	router.GET("/", proxyListReq)
	router.GET("/:app", proxyReq(perms, registry.WithCache))
	router.GET("/:app/icon", proxyReq(authed, registry.NoCache))
	router.GET("/:app/screenshots/:filename", proxyReq(authed, registry.NoCache))
	router.GET("/:app/:version/icon", proxyReq(authed, registry.NoCache))
	router.GET("/:app/:version/screenshots/:filename", proxyReq(authed, registry.NoCache))
	router.GET("/:app/:version", proxyReq(perms, registry.WithCache))
	router.GET("/:app/:channel/latest", proxyReq(perms, registry.WithCache))
}
<commit_msg>Allow full path for application screenshots<commit_after>package registry

import (
	"net/http"

	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/registry"
	"github.com/cozy/cozy-stack/web/middlewares"
	webpermissions "github.com/cozy/cozy-stack/web/permissions"
	"github.com/cozy/echo"
)

type authType int

const (
	authed authType = iota
	perms
)

func proxyReq(auth authType, cacheControl registry.CacheControl) echo.HandlerFunc {
	return func(c echo.Context) error {
		i := middlewares.GetInstance(c)
		switch auth {
		case authed:
			if !middlewares.IsLoggedIn(c) {
				return echo.NewHTTPError(http.StatusForbidden)
			}
		case perms:
			pdoc, err := webpermissions.GetPermission(c)
			if err != nil || pdoc.Type != permissions.TypeWebapp {
				return echo.NewHTTPError(http.StatusForbidden)
			}
		default:
			panic("unknown authType")
		}
		registries, err := i.Registries()
		if err != nil {
			return err
		}
		req := c.Request()
		resp, err := registry.Proxy(req, registries, cacheControl)
		if err != nil {
			return err
		}
		defer resp.Body.Close()
		contentType := resp.Header.Get("content-type")
		return c.Stream(resp.StatusCode, contentType, resp.Body)
	}
}

func proxyListReq(c echo.Context) error {
	i := middlewares.GetInstance(c)
	pdoc, err := webpermissions.GetPermission(c)
	if err != nil || pdoc.Type != permissions.TypeWebapp {
		return echo.NewHTTPError(http.StatusForbidden)
	}
	registries, err := i.Registries()
	if err != nil {
		return err
	}
	req := c.Request()
	list, err := registry.ProxyList(req, registries)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, list)
}

// Routes sets the routing for the registry
func Routes(router *echo.Group) {
	router.GET("", proxyListReq)
	router.GET("/", proxyListReq)
	router.GET("/:app", proxyReq(perms, registry.WithCache))
	router.GET("/:app/icon", proxyReq(authed, registry.NoCache))
	router.GET("/:app/screenshots/*", proxyReq(authed, registry.NoCache))
	router.GET("/:app/:version/icon", proxyReq(authed, registry.NoCache))
	router.GET("/:app/:version/screenshots/*", proxyReq(authed, registry.NoCache))
	router.GET("/:app/:version", proxyReq(perms, registry.WithCache))
	router.GET("/:app/:channel/latest", proxyReq(perms, registry.WithCache))
}
<|endoftext|>


<commit_before>package account

import (
	"context"
	"database/sql"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"chain/database/pg"
	"chain/errors"
	"chain/protocol"
	"chain/protocol/bc"
	"chain/protocol/state"
	"chain/sync/idempotency"
)

var (
	// ErrInsufficient indicates the account doesn't contain enough
	// units of the requested asset to satisfy the reservation.
	// New units must be deposited into the account in order to
	// satisfy the request; change will not be sufficient.
	ErrInsufficient = errors.New("reservation found insufficient funds")

	// ErrReserved indicates that a reservation could not be
	// satisfied because some of the outputs were already reserved.
	// When those reservations are finalized into a transaction
	// (and no other transaction spends funds from the account),
	// new change outputs will be created
	// in sufficient amounts to satisfy the request.
	ErrReserved = errors.New("reservation found outputs already reserved")
)

// utxo describes an individual account utxo.
type utxo struct {
	bc.Outpoint
	bc.AssetAmount
	ControlProgram []byte

	AccountID           string
	ControlProgramIndex uint64

	confirmedIn, blockPos uint64
}

func (u *utxo) source() source {
	return source{AssetID: u.AssetID, AccountID: u.AccountID}
}

// source describes the criteria to use when selecting UTXOs.
type source struct {
	AssetID   bc.AssetID
	AccountID string
}

// reservation describes a reservation of a set of UTXOs belonging
// to a particular account. Reservations are immutable.
type reservation struct {
	ID          uint64
	Source      source
	UTXOs       []*utxo
	Change      uint64
	Expiry      time.Time
	ClientToken *string
}

func newReserver(db pg.DB, c *protocol.Chain) *reserver {
	return &reserver{
		c:            c,
		db:           db,
		reservations: make(map[uint64]*reservation),
		sources:      make(map[source]*sourceReserver),
	}
}

// reserver implements a utxo reserver that stores reservations
// in-memory. It relies on the account_utxos table for the source of
// truth of valid UTXOs but tracks which of those UTXOs are reserved
// in-memory.
//
// To reduce latency and prevent deadlock, no two mutexes (either on
// reserver or sourceReserver) should be held at the same time
//
// reserver ensures idempotency of reservations until the reservation
// expiration.
type reserver struct {
	c                 *protocol.Chain
	db                pg.DB
	nextReservationID uint64
	idempotency       idempotency.Group

	reservationsMu sync.Mutex
	reservations   map[uint64]*reservation

	sourcesMu sync.Mutex
	sources   map[source]*sourceReserver
}

// Reserve selects and reserves UTXOs according to the critera provided
// in source. The resulting reservation expires at exp.
func (re *reserver) Reserve(ctx context.Context, src source, amount uint64, clientToken *string, exp time.Time) (*reservation, error) {
	if clientToken == nil {
		return re.reserve(ctx, src, amount, clientToken, exp)
	}

	untypedRes, err := re.idempotency.Once(*clientToken, func() (interface{}, error) {
		return re.reserve(ctx, src, amount, clientToken, exp)
	})
	return untypedRes.(*reservation), err
}

func (re *reserver) reserve(ctx context.Context, src source, amount uint64, clientToken *string, exp time.Time) (res *reservation, err error) {
	sourceReserver := re.source(src)

	// Try to reserve the right amount.
	rid := atomic.AddUint64(&re.nextReservationID, 1)
	reserved, total, err := sourceReserver.reserve(ctx, rid, amount)
	if err != nil {
		return nil, err
	}

	res = &reservation{
		ID:          rid,
		Source:      src,
		UTXOs:       reserved,
		Expiry:      exp,
		ClientToken: clientToken,
	}

	// Save the successful reservation.
	re.reservationsMu.Lock()
	defer re.reservationsMu.Unlock()
	re.reservations[rid] = res

	// Make change if necessary
	if total > amount {
		res.Change = total - amount
	}
	return res, nil
}

// ReserveUTXO reserves a specific utxo for spending. The resulting
// reservation expires at exp.
func (re *reserver) ReserveUTXO(ctx context.Context, out bc.Outpoint, clientToken *string, exp time.Time) (*reservation, error) {
	if clientToken == nil {
		return re.reserveUTXO(ctx, out, exp, nil)
	}

	untypedRes, err := re.idempotency.Once(*clientToken, func() (interface{}, error) {
		return re.reserveUTXO(ctx, out, exp, clientToken)
	})
	return untypedRes.(*reservation), err
}

func (re *reserver) reserveUTXO(ctx context.Context, out bc.Outpoint, exp time.Time, clientToken *string) (*reservation, error) {
	u, err := findSpecificUTXO(ctx, re.db, out)
	if err != nil {
		return nil, err
	}
	if !re.checkUTXO(u) {
		return nil, pg.ErrUserInputNotFound
	}

	rid := atomic.AddUint64(&re.nextReservationID, 1)
	err = re.source(u.source()).reserveUTXO(rid, u)
	if err != nil {
		return nil, err
	}

	res := &reservation{
		ID:          rid,
		Source:      u.source(),
		UTXOs:       []*utxo{u},
		Expiry:      exp,
		ClientToken: clientToken,
	}
	re.reservationsMu.Lock()
	re.reservations[rid] = res
	re.reservationsMu.Unlock()
	return res, nil
}

// Cancel makes a best-effort attempt at canceling the reservation with
// the provided ID.
func (re *reserver) Cancel(ctx context.Context, rid uint64) error {
	re.reservationsMu.Lock()
	res, ok := re.reservations[rid]
	delete(re.reservations, rid)
	re.reservationsMu.Unlock()
	if !ok {
		return fmt.Errorf("couldn't find reservation %d", rid)
	}
	re.source(res.Source).cancel(res)
	if res.ClientToken != nil {
		re.idempotency.Forget(*res.ClientToken)
	}
	return nil
}

// ExpireReservations cleans up all reservations that have expired,
// making their UTXOs available for reservation again.
func (re *reserver) ExpireReservations(ctx context.Context) error {
	// Remove records of any reservations that have expired.
	now := time.Now()
	var canceled []*reservation
	re.reservationsMu.Lock()
	for rid, res := range re.reservations {
		if res.Expiry.Before(now) {
			canceled = append(canceled, res)
			delete(re.reservations, rid)
		}
	}
	re.reservationsMu.Unlock()

	// If we removed any expired reservations, update the corresponding
	// source reservers.
	for _, res := range canceled {
		re.source(res.Source).cancel(res)
		if res.ClientToken != nil {
			re.idempotency.Forget(*res.ClientToken)
		}
	}

	// TODO(jackson): Cleanup any source reservers that don't have
	// anything reserved. It'll be a little tricky because of our
	// locking scheme.
	return nil
}

func (re *reserver) checkUTXO(u *utxo) bool {
	_, s := re.c.State()
	return s.Tree.ContainsKey(state.OutputKey(u.Outpoint))
}

func (re *reserver) source(src source) *sourceReserver {
	re.sourcesMu.Lock()
	defer re.sourcesMu.Unlock()

	sr, ok := re.sources[src]
	if ok {
		return sr
	}

	sr = &sourceReserver{
		db:       re.db,
		src:      src,
		validFn:  re.checkUTXO,
		cached:   make(map[bc.Outpoint]*utxo),
		reserved: make(map[bc.Outpoint]uint64),
	}
	re.sources[src] = sr
	return sr
}

type sourceReserver struct {
	db         pg.DB
	src        source
	validFn    func(u *utxo) bool
	lastHeight uint64
	lastIndex  uint64

	mu       sync.Mutex
	cached   map[bc.Outpoint]*utxo
	reserved map[bc.Outpoint]uint64
}

func (sr *sourceReserver) reserve(ctx context.Context, rid uint64, amount uint64) ([]*utxo, uint64, error) {
	var reserved, unavailable uint64
	var reservedUTXOs []*utxo

	// First try to reserve using only cached UTXOs.
	sr.mu.Lock()
	for o, u := range sr.cached {
		// If the UTXO is already reserved, skip it.
		if _, ok := sr.reserved[u.Outpoint]; ok {
			continue
		}
		// Cached utxos aren't guaranteed to still be valid; they may
		// have been spent. Verify that that the outputs are still in
		// the state tree.
		if !sr.validFn(u) {
			delete(sr.cached, o)
			continue
		}

		reserved += u.Amount
		reservedUTXOs = append(reservedUTXOs, u)
		if reserved >= amount {
			break
		}
	}
	if reserved >= amount {
		// We've found enough to satisfy the request.
		for _, u := range reservedUTXOs {
			sr.reserved[u.Outpoint] = rid
		}
		sr.mu.Unlock()
		return reservedUTXOs, reserved, nil
	}
	sr.mu.Unlock()
	reserved = 0
	reservedUTXOs = nil

	// Find the set of UTXOs that match this source.
	utxos, err := findMatchingUTXOs(ctx, sr.db, sr.src, sr.lastHeight, sr.lastIndex)
	if err != nil {
		return nil, 0, err
	}

	sr.mu.Lock()
	defer sr.mu.Unlock()
	for _, u := range utxos {
		sr.cached[u.Outpoint] = u
		sr.lastHeight, sr.lastIndex = u.confirmedIn, u.blockPos
	}

	for _, utxo := range utxos {
		// If the utxo is already reserved, skip it.
		if _, ok := sr.reserved[utxo.Outpoint]; ok {
			unavailable += utxo.Amount
			continue
		}

		// This utxo is available for the taking.
		reserved += utxo.Amount
		reservedUTXOs = append(reservedUTXOs, utxo)
		if reserved >= amount {
			break
		}
	}
	if reserved+unavailable < amount {
		// Even if everything was available, this account wouldn't have
		// enough to satisfy the request.
		return nil, 0, ErrInsufficient
	}
	if reserved < amount {
		// The account has enough for the request, but some is tied up in
		// other reservations.
		return nil, 0, ErrReserved
	}

	// We've found enough to satisfy the request.
	for _, u := range reservedUTXOs {
		sr.reserved[u.Outpoint] = rid
	}
	return reservedUTXOs, reserved, nil
}

func (sr *sourceReserver) reserveUTXO(rid uint64, utxo *utxo) error {
	sr.mu.Lock()
	defer sr.mu.Unlock()

	_, isReserved := sr.reserved[utxo.Outpoint]
	if isReserved {
		return ErrReserved
	}

	sr.reserved[utxo.Outpoint] = rid
	return nil
}

func (sr *sourceReserver) cancel(res *reservation) {
	sr.mu.Lock()
	defer sr.mu.Unlock()
	for _, utxo := range res.UTXOs {
		delete(sr.reserved, utxo.Outpoint)
	}
}

func findMatchingUTXOs(ctx context.Context, db pg.DB, src source, height, pos uint64) ([]*utxo, error) {
	const q = `
		SELECT tx_hash, index, amount, control_program_index, control_program, confirmed_in, block_pos
		FROM account_utxos
		WHERE account_id = $1 AND asset_id = $2 AND (confirmed_in, block_pos) > ($3, $4)
		ORDER BY confirmed_in ASC, block_pos ASC
	`
	var utxos []*utxo
	err := pg.ForQueryRows(ctx, db, q, src.AccountID, src.AssetID, height, pos,
		func(txHash bc.Hash, index uint32, amount uint64, cpIndex uint64, controlProg []byte, confirmedIn, blockPos uint64) {
			utxos = append(utxos, &utxo{
				Outpoint: bc.Outpoint{
					Hash:  txHash,
					Index: index,
				},
				AssetAmount: bc.AssetAmount{
					Amount:  amount,
					AssetID: src.AssetID,
				},
				ControlProgram:      controlProg,
				AccountID:           src.AccountID,
				ControlProgramIndex: cpIndex,
				confirmedIn:         confirmedIn,
				blockPos:            blockPos,
			})
		})
	if err != nil {
		return nil, errors.Wrap(err)
	}
	return utxos, nil
}

func findSpecificUTXO(ctx context.Context, db pg.DB, out bc.Outpoint) (*utxo, error) {
	const q = `
		SELECT account_id, asset_id, amount, control_program_index, control_program
		FROM account_utxos
		WHERE tx_hash = $1 AND index = $2
	`
	u := new(utxo)
	err := db.QueryRow(ctx, q, out.Hash, out.Index).Scan(&u.AccountID, &u.AssetID, &u.Amount, &u.ControlProgramIndex, &u.ControlProgram)
	if err == sql.ErrNoRows {
		return nil, pg.ErrUserInputNotFound
	} else if err != nil {
		return nil, errors.Wrap(err)
	}
	u.Outpoint = out
	return u, nil
}
<commit_msg>core/account: fix paged reservation<commit_after>package account

import (
	"context"
	"database/sql"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"chain/database/pg"
	"chain/errors"
	"chain/protocol"
	"chain/protocol/bc"
	"chain/protocol/state"
	"chain/sync/idempotency"
)

var (
	// ErrInsufficient indicates the account doesn't contain enough
	// units of the requested asset to satisfy the reservation.
	// New units must be deposited into the account in order to
	// satisfy the request; change will not be sufficient.
	ErrInsufficient = errors.New("reservation found insufficient funds")

	// ErrReserved indicates that a reservation could not be
	// satisfied because some of the outputs were already reserved.
	// When those reservations are finalized into a transaction
	// (and no other transaction spends funds from the account),
	// new change outputs will be created
	// in sufficient amounts to satisfy the request.
	ErrReserved = errors.New("reservation found outputs already reserved")
)

// utxo describes an individual account utxo.
type utxo struct {
	bc.Outpoint
	bc.AssetAmount
	ControlProgram []byte

	AccountID           string
	ControlProgramIndex uint64

	confirmedIn, blockPos uint64
}

func (u *utxo) source() source {
	return source{AssetID: u.AssetID, AccountID: u.AccountID}
}

// source describes the criteria to use when selecting UTXOs.
type source struct {
	AssetID   bc.AssetID
	AccountID string
}

// reservation describes a reservation of a set of UTXOs belonging
// to a particular account. Reservations are immutable.
type reservation struct {
	ID          uint64
	Source      source
	UTXOs       []*utxo
	Change      uint64
	Expiry      time.Time
	ClientToken *string
}

func newReserver(db pg.DB, c *protocol.Chain) *reserver {
	return &reserver{
		c:            c,
		db:           db,
		reservations: make(map[uint64]*reservation),
		sources:      make(map[source]*sourceReserver),
	}
}

// reserver implements a utxo reserver that stores reservations
// in-memory. It relies on the account_utxos table for the source of
// truth of valid UTXOs but tracks which of those UTXOs are reserved
// in-memory.
//
// To reduce latency and prevent deadlock, no two mutexes (either on
// reserver or sourceReserver) should be held at the same time
//
// reserver ensures idempotency of reservations until the reservation
// expiration.
type reserver struct {
	c                 *protocol.Chain
	db                pg.DB
	nextReservationID uint64
	idempotency       idempotency.Group

	reservationsMu sync.Mutex
	reservations   map[uint64]*reservation

	sourcesMu sync.Mutex
	sources   map[source]*sourceReserver
}

// Reserve selects and reserves UTXOs according to the critera provided
// in source. The resulting reservation expires at exp.
func (re *reserver) Reserve(ctx context.Context, src source, amount uint64, clientToken *string, exp time.Time) (*reservation, error) {
	if clientToken == nil {
		return re.reserve(ctx, src, amount, clientToken, exp)
	}

	untypedRes, err := re.idempotency.Once(*clientToken, func() (interface{}, error) {
		return re.reserve(ctx, src, amount, clientToken, exp)
	})
	return untypedRes.(*reservation), err
}

func (re *reserver) reserve(ctx context.Context, src source, amount uint64, clientToken *string, exp time.Time) (res *reservation, err error) {
	sourceReserver := re.source(src)

	// Try to reserve the right amount.
	rid := atomic.AddUint64(&re.nextReservationID, 1)
	reserved, total, err := sourceReserver.reserve(ctx, rid, amount)
	if err != nil {
		return nil, err
	}

	res = &reservation{
		ID:          rid,
		Source:      src,
		UTXOs:       reserved,
		Expiry:      exp,
		ClientToken: clientToken,
	}

	// Save the successful reservation.
	re.reservationsMu.Lock()
	defer re.reservationsMu.Unlock()
	re.reservations[rid] = res

	// Make change if necessary
	if total > amount {
		res.Change = total - amount
	}
	return res, nil
}

// ReserveUTXO reserves a specific utxo for spending. The resulting
// reservation expires at exp.
func (re *reserver) ReserveUTXO(ctx context.Context, out bc.Outpoint, clientToken *string, exp time.Time) (*reservation, error) {
	if clientToken == nil {
		return re.reserveUTXO(ctx, out, exp, nil)
	}

	untypedRes, err := re.idempotency.Once(*clientToken, func() (interface{}, error) {
		return re.reserveUTXO(ctx, out, exp, clientToken)
	})
	return untypedRes.(*reservation), err
}

func (re *reserver) reserveUTXO(ctx context.Context, out bc.Outpoint, exp time.Time, clientToken *string) (*reservation, error) {
	u, err := findSpecificUTXO(ctx, re.db, out)
	if err != nil {
		return nil, err
	}
	if !re.checkUTXO(u) {
		return nil, pg.ErrUserInputNotFound
	}

	rid := atomic.AddUint64(&re.nextReservationID, 1)
	err = re.source(u.source()).reserveUTXO(rid, u)
	if err != nil {
		return nil, err
	}

	res := &reservation{
		ID:          rid,
		Source:      u.source(),
		UTXOs:       []*utxo{u},
		Expiry:      exp,
		ClientToken: clientToken,
	}
	re.reservationsMu.Lock()
	re.reservations[rid] = res
	re.reservationsMu.Unlock()
	return res, nil
}

// Cancel makes a best-effort attempt at canceling the reservation with
// the provided ID.
func (re *reserver) Cancel(ctx context.Context, rid uint64) error {
	re.reservationsMu.Lock()
	res, ok := re.reservations[rid]
	delete(re.reservations, rid)
	re.reservationsMu.Unlock()
	if !ok {
		return fmt.Errorf("couldn't find reservation %d", rid)
	}
	re.source(res.Source).cancel(res)
	if res.ClientToken != nil {
		re.idempotency.Forget(*res.ClientToken)
	}
	return nil
}

// ExpireReservations cleans up all reservations that have expired,
// making their UTXOs available for reservation again.
func (re *reserver) ExpireReservations(ctx context.Context) error {
	// Remove records of any reservations that have expired.
	now := time.Now()
	var canceled []*reservation
	re.reservationsMu.Lock()
	for rid, res := range re.reservations {
		if res.Expiry.Before(now) {
			canceled = append(canceled, res)
			delete(re.reservations, rid)
		}
	}
	re.reservationsMu.Unlock()

	// If we removed any expired reservations, update the corresponding
	// source reservers.
	for _, res := range canceled {
		re.source(res.Source).cancel(res)
		if res.ClientToken != nil {
			re.idempotency.Forget(*res.ClientToken)
		}
	}

	// TODO(jackson): Cleanup any source reservers that don't have
	// anything reserved. It'll be a little tricky because of our
	// locking scheme.
	return nil
}

func (re *reserver) checkUTXO(u *utxo) bool {
	_, s := re.c.State()
	return s.Tree.ContainsKey(state.OutputKey(u.Outpoint))
}

func (re *reserver) source(src source) *sourceReserver {
	re.sourcesMu.Lock()
	defer re.sourcesMu.Unlock()

	sr, ok := re.sources[src]
	if ok {
		return sr
	}

	sr = &sourceReserver{
		db:       re.db,
		src:      src,
		validFn:  re.checkUTXO,
		cached:   make(map[bc.Outpoint]*utxo),
		reserved: make(map[bc.Outpoint]uint64),
	}
	re.sources[src] = sr
	return sr
}

type sourceReserver struct {
	db         pg.DB
	src        source
	validFn    func(u *utxo) bool
	lastHeight uint64
	lastIndex  uint64

	mu       sync.Mutex
	cached   map[bc.Outpoint]*utxo
	reserved map[bc.Outpoint]uint64
}

func (sr *sourceReserver) reserve(ctx context.Context, rid uint64, amount uint64) ([]*utxo, uint64, error) {
	reservedUTXOs, reservedAmount, err := sr.reserveFromCache(rid, amount)
	if err == nil {
		return reservedUTXOs, reservedAmount, nil
	}

	// Find the set of UTXOs that match this source.
	utxos, err := findMatchingUTXOs(ctx, sr.db, sr.src, sr.lastHeight, sr.lastIndex)
	if err != nil {
		return nil, 0, err
	}

	sr.mu.Lock()
	for _, u := range utxos {
		sr.cached[u.Outpoint] = u
		sr.lastHeight, sr.lastIndex = u.confirmedIn, u.blockPos
	}
	sr.mu.Unlock()

	return sr.reserveFromCache(rid, amount)
}

func (sr *sourceReserver) reserveFromCache(rid uint64, amount uint64) ([]*utxo, uint64, error) {
	var (
		reserved, unavailable uint64
		reservedUTXOs         []*utxo
	)
	sr.mu.Lock()
	defer sr.mu.Unlock()

	for o, u := range sr.cached {
		// If the UTXO is already reserved, skip it.
		if _, ok := sr.reserved[u.Outpoint]; ok {
			unavailable += u.Amount
			continue
		}
		// Cached utxos aren't guaranteed to still be valid; they may
		// have been spent. Verify that that the outputs are still in
		// the state tree.
		if !sr.validFn(u) {
			delete(sr.cached, o)
			continue
		}

		reserved += u.Amount
		reservedUTXOs = append(reservedUTXOs, u)
		if reserved >= amount {
			break
		}
	}
	if reserved+unavailable < amount {
		// Even if everything was available, this account wouldn't have
		// enough to satisfy the request.
		return nil, 0, ErrInsufficient
	}
	if reserved < amount {
		// The account has enough for the request, but some is tied up in
		// other reservations.
		return nil, 0, ErrReserved
	}

	// We've found enough to satisfy the request.
	for _, u := range reservedUTXOs {
		sr.reserved[u.Outpoint] = rid
	}

	return reservedUTXOs, reserved, nil
}

func (sr *sourceReserver) reserveUTXO(rid uint64, utxo *utxo) error {
	sr.mu.Lock()
	defer sr.mu.Unlock()

	_, isReserved := sr.reserved[utxo.Outpoint]
	if isReserved {
		return ErrReserved
	}

	sr.reserved[utxo.Outpoint] = rid
	return nil
}

func (sr *sourceReserver) cancel(res *reservation) {
	sr.mu.Lock()
	defer sr.mu.Unlock()
	for _, utxo := range res.UTXOs {
		delete(sr.reserved, utxo.Outpoint)
	}
}

func findMatchingUTXOs(ctx context.Context, db pg.DB, src source, height, pos uint64) ([]*utxo, error) {
	const q = `
		SELECT tx_hash, index, amount, control_program_index, control_program, confirmed_in, block_pos
		FROM account_utxos
		WHERE account_id = $1 AND asset_id = $2 AND (confirmed_in, block_pos) > ($3, $4)
		ORDER BY confirmed_in ASC, block_pos ASC
	`
	var utxos []*utxo
	err := pg.ForQueryRows(ctx, db, q, src.AccountID, src.AssetID, height, pos,
		func(txHash bc.Hash, index uint32, amount uint64, cpIndex uint64, controlProg []byte, confirmedIn, blockPos uint64) {
			utxos = append(utxos, &utxo{
				Outpoint: bc.Outpoint{
					Hash:  txHash,
					Index: index,
				},
				AssetAmount: bc.AssetAmount{
					Amount:  amount,
					AssetID: src.AssetID,
				},
				ControlProgram:      controlProg,
				AccountID:           src.AccountID,
				ControlProgramIndex: cpIndex,
				confirmedIn:         confirmedIn,
				blockPos:            blockPos,
			})
		})
	if err != nil {
		return nil, errors.Wrap(err)
	}
	return utxos, nil
}

func findSpecificUTXO(ctx context.Context, db pg.DB, out bc.Outpoint) (*utxo, error) {
	const q = `
		SELECT account_id, asset_id, amount, control_program_index, control_program
		FROM account_utxos
		WHERE tx_hash = $1 AND index = $2
	`
	u := new(utxo)
	err := db.QueryRow(ctx, q, out.Hash, out.Index).Scan(&u.AccountID, &u.AssetID, &u.Amount, &u.ControlProgramIndex, &u.ControlProgram)
	if err == sql.ErrNoRows {
		return nil, pg.ErrUserInputNotFound
	} else if err != nil {
		return nil, errors.Wrap(err)
	}
	u.Outpoint = out
	return u, nil
}
<|endoftext|>


<commit_before>package renderer

import (
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

type RenderOpts struct {
	Url       string
	Width     string
	Height    string
	SessionId string
}

func RenderToPng(params *RenderOpts) (string, error) {
	log.Info("PhantomRenderer::renderToPng url %v", params.Url)
	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "phantomjs"))
	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
	pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20)))
	pngPath = pngPath + ".png"

	cmd := exec.Command(binPath, "--ignore-ssl-errors=true", scriptPath, "url="+params.Url, "width="+params.Width,
		"height="+params.Height, "png="+pngPath, "cookiename="+setting.SessionOptions.CookieName,
		"domain="+setting.Domain, "sessionid="+params.SessionId)
	stdout, err := cmd.StdoutPipe()

	if err != nil {
		return "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		return "", err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stdout, stderr)

	done := make(chan error)
	go func() {
		cmd.Wait()
		close(done)
	}()

	select {
	case <-time.After(10 * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			log.Error(4, "failed to kill: %v", err)
		}
	case <-done:
	}

	return pngPath, nil
}
<commit_msg>Render panel images with any SSL protocol<commit_after>package renderer

import (
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

type RenderOpts struct {
	Url       string
	Width     string
	Height    string
	SessionId string
}

func RenderToPng(params *RenderOpts) (string, error) {
	log.Info("PhantomRenderer::renderToPng url %v", params.Url)
	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "phantomjs"))
	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
	pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20)))
	pngPath = pngPath + ".png"

	cmd := exec.Command(binPath, "--ignore-ssl-errors=true", "--ssl-protocol=any", scriptPath, "url="+params.Url, "width="+params.Width,
		"height="+params.Height, "png="+pngPath, "cookiename="+setting.SessionOptions.CookieName,
		"domain="+setting.Domain, "sessionid="+params.SessionId)
	stdout, err := cmd.StdoutPipe()

	if err != nil {
		return "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		return "", err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stdout, stderr)

	done := make(chan error)
	go func() {
		cmd.Wait()
		close(done)
	}()

	select {
	case <-time.After(10 * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			log.Error(4, "failed to kill: %v", err)
		}
	case <-done:
	}

	return pngPath, nil
}
<|endoftext|>