<commit_before>package hypervisor

import (
	"fmt"
	"net"
	"sync"

	"github.com/hyperhq/runv/api"
	hyperstartapi "github.com/hyperhq/runv/hyperstart/api/json"
	"github.com/hyperhq/runv/hypervisor/network"
)

const (
	MAX_NIC = int(^uint(0) >> 1) // Eth is network card, while lo is alias, what's the maximum for each? same?
	// let upper level logic care about the restriction. here is just an upbond.
)

type NetworkContext struct {
	*api.SandboxConfig

	sandbox *VmContext

	ports []*api.PortDescription
	eth   map[int]*InterfaceCreated
	lo    map[string]*InterfaceCreated

	idMap map[string]*InterfaceCreated // a secondary index for both eth and lo, for lo, the hostdevice is empty

	slotLock *sync.RWMutex
}

func NewNetworkContext() *NetworkContext {
	return &NetworkContext{
		ports:    []*api.PortDescription{},
		eth:      make(map[int]*InterfaceCreated),
		lo:       make(map[string]*InterfaceCreated),
		idMap:    make(map[string]*InterfaceCreated),
		slotLock: &sync.RWMutex{},
	}
}

func (nc *NetworkContext) sandboxInfo() *hyperstartapi.Pod {

	vmSpec := NewVmSpec()

	vmSpec.Hostname = nc.Hostname
	vmSpec.Dns = nc.Dns
	vmSpec.PortmappingWhiteLists = &hyperstartapi.PortmappingWhiteList{
		InternalNetworks: nc.Neighbors.InternalNetworks,
		ExternalNetworks: nc.Neighbors.ExternalNetworks,
	}

	return vmSpec
}

func (nc *NetworkContext) applySlot() int {
	for i := 0; i <= MAX_NIC; i++ {
		if _, ok := nc.eth[i]; !ok {
			nc.eth[i] = nil
			return i
		}
	}

	return -1
}

func (nc *NetworkContext) freeSlot(slot int) {
	if inf, ok := nc.eth[slot]; !ok {
		nc.sandbox.Log(WARNING, "Freeing an unoccupied eth slot %d", slot)
		return
	} else {
		if _, ok := nc.idMap[inf.Id]; ok {
			delete(nc.idMap, inf.Id)
		}
		nc.sandbox.Log(DEBUG, "Free slot %d of eth", slot)
		delete(nc.eth, slot)
	}
}

func (nc *NetworkContext) addInterface(inf *api.InterfaceDescription, result chan api.Result) {
	if inf.Lo {
		if inf.Ip == "" {
			estr := fmt.Sprintf("creating an interface without an IP address: %#v", inf)
			nc.sandbox.Log(ERROR, estr)
			result <- NewSpecError(inf.Id, estr)
			return
		}
		i := &InterfaceCreated{
			Id:         inf.Id,
			DeviceName: "lo",
			IpAddr:     inf.Ip,
			NetMask:    "255.255.255.255",
		}
		nc.lo[inf.Ip] = i
		nc.idMap[inf.Id] = i

		result <- &api.ResultBase{
			Id:      inf.Id,
			Success: true,
		}
		return
	}

	var devChan chan VmEvent = make(chan VmEvent, 1)

	go func() {
		nc.slotLock.Lock()
		defer nc.slotLock.Unlock()

		idx := nc.applySlot()
		if idx < 0 {
			estr := fmt.Sprintf("no available ethernet slot for interface %#v", inf)
			nc.sandbox.Log(ERROR, estr)
			result <- NewBusyError(inf.Id, estr)
			close(devChan)
			return
		}

		nc.configureInterface(idx, nc.sandbox.nextPciAddr(), fmt.Sprintf("eth%d", idx), inf, devChan)
	}()

	go func() {
		ev, ok := <-devChan
		if !ok {
			nc.sandbox.Log(ERROR, "chan closed while waiting network inserted event: %#v", ev)
			return
		}
		// ev might be DeviceInsert failed, or inserted
		if fe, ok := ev.(*DeviceFailed); ok {
			if inf, ok := fe.Session.(*InterfaceCreated); ok {
				nc.netdevInsertFailed(inf.Index, inf.DeviceName)
				nc.sandbox.Log(ERROR, "interface creation failed: %#v", inf)
			} else if inf, ok := fe.Session.(*NetDevInsertedEvent); ok {
				nc.netdevInsertFailed(inf.Index, inf.DeviceName)
				nc.sandbox.Log(ERROR, "interface creation failed: %#v", inf)
			}
			result <- fe
			return
		} else if ni, ok := ev.(*NetDevInsertedEvent); ok {
			nc.sandbox.Log(DEBUG, "nic insert success: %s", ni.Id)
			result <- ni
			return
		}
		nc.sandbox.Log(ERROR, "got unknown event while waiting network inserted event: %#v", ev)
		result <- NewDeviceError(inf.Id, "unknown event")
	}()
}

func (nc *NetworkContext) removeInterface(id string, result chan api.Result) {
	if inf, ok := nc.idMap[id]; !ok {
		nc.sandbox.Log(WARNING, "trying remove a non-exist interface %s", id)
		result <- api.NewResultBase(id, true, "not exist")
		return
	} else if inf.HostDevice == "" { // a virtual interface
		delete(nc.idMap, id)
		delete(nc.lo, inf.IpAddr)
		result <- api.NewResultBase(id, true, "")
		return
	} else {
		nc.slotLock.Lock()
		defer nc.slotLock.Unlock()

		if _, ok := nc.eth[inf.Index]; !ok {
			delete(nc.idMap, id)
			nc.sandbox.Log(INFO, "non-configured network device %d remove failed", inf.Index)
			result <- api.NewResultBase(id, true, "not configured eth")
			return
		}

		var devChan chan VmEvent = make(chan VmEvent, 1)

		nc.sandbox.Log(DEBUG, "remove network card %d: %s", inf.Index, inf.IpAddr)
		nc.sandbox.DCtx.RemoveNic(nc.sandbox, inf, &NetDevRemovedEvent{Index: inf.Index}, devChan)

		go func() {
			ev, ok := <-devChan
			if !ok {
				return
			}

			success := true
			message := ""

			if fe, ok := ev.(*DeviceFailed); ok {
				success = false
				message = "unplug failed"
				if inf, ok := fe.Session.(*NetDevRemovedEvent); ok {
					nc.sandbox.Log(ERROR, "interface remove failed: %#v", inf)
				}
			}

			nc.slotLock.Lock()
			defer nc.slotLock.Unlock()
			nc.freeSlot(inf.Index)
			nc.cleanupInf(inf)

			result <- api.NewResultBase(id, success, message)
		}()
	}
}

func (nc *NetworkContext) netdevInsertFailed(idx int, name string) {
	nc.slotLock.Lock()
	defer nc.slotLock.Unlock()

	if _, ok := nc.eth[idx]; !ok {
		nc.sandbox.Log(INFO, "network device %d (%s) insert failed before configured", idx, name)
		return
	}

	nc.sandbox.Log(INFO, "network device %d (%s) insert failed", idx, name)
	nc.freeSlot(idx)
}

func (nc *NetworkContext) configureInterface(index, pciAddr int, name string, inf *api.InterfaceDescription, result chan<- VmEvent) {
	var (
		err      error
		settings *network.Settings
	)

	if HDriver.BuildinNetwork() {
		/* VBox doesn't support join to bridge */
		settings, err = nc.sandbox.DCtx.ConfigureNetwork(nc.sandbox.Id, "", inf)
	} else {
		settings, err = network.Configure(nc.sandbox.Id, "", false, inf)
	}

	if err != nil {
		nc.sandbox.Log(ERROR, "interface creating failed: %v", err.Error())
		session := &InterfaceCreated{Id: inf.Id, Index: index, PCIAddr: pciAddr, DeviceName: name}
		result <- &DeviceFailed{Session: session}
		return
	}

	created, err := interfaceGot(inf.Id, index, pciAddr, name, settings)
	if err != nil {
		result <- &DeviceFailed{Session: created}
		return
	}

	h := &HostNicInfo{
		Id:      created.Id,
		Fd:      uint64(created.Fd.Fd()),
		Device:  created.HostDevice,
		Mac:     created.MacAddr,
		Bridge:  created.Bridge,
		Gateway: created.Bridge,
	}
	g := &GuestNicInfo{
		Device:  created.DeviceName,
		Ipaddr:  created.IpAddr,
		Index:   created.Index,
		Busaddr: created.PCIAddr,
	}

	nc.eth[index] = created
	nc.idMap[created.Id] = created
	nc.sandbox.DCtx.AddNic(nc.sandbox, h, g, result)
}

func (nc *NetworkContext) cleanupInf(inf *InterfaceCreated) {
	if !HDriver.BuildinNetwork() && inf.Fd != nil {
		network.Close(inf.Fd)
		inf.Fd = nil
	}

}

func (nc *NetworkContext) getInterface(id string) *InterfaceCreated {
	nc.slotLock.RLock()
	defer nc.slotLock.RUnlock()

	inf, ok := nc.idMap[id]
	if ok {
		return inf
	}
	return nil
}

func (nc *NetworkContext) getIpAddrs() []string {
	nc.slotLock.RLock()
	defer nc.slotLock.RUnlock()

	res := []string{}
	for _, inf := range nc.eth {
		res = append(res, inf.IpAddr)
	}

	return res
}

func (nc *NetworkContext) getRoutes() []hyperstartapi.Route {
	nc.slotLock.RLock()
	nc.slotLock.RUnlock()
	routes := []hyperstartapi.Route{}

	for _, inf := range nc.idMap {
		for _, r := range inf.RouteTable {
			routes = append(routes, hyperstartapi.Route{
				Dest:    r.Destination,
				Gateway: r.Gateway,
				Device:  inf.DeviceName,
			})
		}
	}

	return routes
}

func (nc *NetworkContext) close() {
	nc.slotLock.Lock()
	defer nc.slotLock.Unlock()

	for _, inf := range nc.eth {
		nc.cleanupInf(inf)
	}
	nc.eth = map[int]*InterfaceCreated{}
	nc.lo = map[string]*InterfaceCreated{}
	nc.idMap = map[string]*InterfaceCreated{}
}

func interfaceGot(id string, index int, pciAddr int, name string, inf *network.Settings) (*InterfaceCreated, error) {
	ip, nw, err := net.ParseCIDR(fmt.Sprintf("%s/%d", inf.IPAddress, inf.IPPrefixLen))
	if err != nil {
		return &InterfaceCreated{Index: index, PCIAddr: pciAddr, DeviceName: name}, err
	}
	var tmp []byte = nw.Mask
	var mask net.IP = tmp

	rt := []*RouteRule{}
	/* Route rule is generated automaticly on first interface,
	 * or generated on the gateway configured interface. */
	if (index == 0 && inf.Automatic) || (!inf.Automatic && inf.Gateway != "") {
		rt = append(rt, &RouteRule{
			Destination: "0.0.0.0/0",
			Gateway:     inf.Gateway, ViaThis: true,
		})
	}

	return &InterfaceCreated{
		Id:         id,
		Index:      index,
		PCIAddr:    pciAddr,
		Bridge:     inf.Bridge,
		HostDevice: inf.Device,
		DeviceName: name,
		Fd:         inf.File,
		MacAddr:    inf.Mac,
		IpAddr:     ip.String(),
		NetMask:    mask.String(),
		RouteTable: rt,
	}, nil
}
<commit_msg>simple fix for refactor<commit_after>package hypervisor

import (
	"fmt"
	"net"
	"sync"

	"github.com/hyperhq/runv/api"
	hyperstartapi "github.com/hyperhq/runv/hyperstart/api/json"
	"github.com/hyperhq/runv/hypervisor/network"
)

const (
	MAX_NIC = int(^uint(0) >> 1) // Eth is network card, while lo is alias, what's the maximum for each? same?
	// let upper level logic care about the restriction. here is just an upbond.
)

type NetworkContext struct {
	*api.SandboxConfig

	sandbox *VmContext

	ports []*api.PortDescription
	eth   map[int]*InterfaceCreated
	lo    map[string]*InterfaceCreated

	idMap map[string]*InterfaceCreated // a secondary index for both eth and lo, for lo, the hostdevice is empty

	slotLock *sync.RWMutex
}

func NewNetworkContext() *NetworkContext {
	return &NetworkContext{
		ports:    []*api.PortDescription{},
		eth:      make(map[int]*InterfaceCreated),
		lo:       make(map[string]*InterfaceCreated),
		idMap:    make(map[string]*InterfaceCreated),
		slotLock: &sync.RWMutex{},
	}
}

func (nc *NetworkContext) sandboxInfo() *hyperstartapi.Pod {

	vmSpec := NewVmSpec()

	vmSpec.Hostname = nc.Hostname
	vmSpec.Dns = nc.Dns
	if nc.Neighbors != nil {
		vmSpec.PortmappingWhiteLists = &hyperstartapi.PortmappingWhiteList{
			InternalNetworks: nc.Neighbors.InternalNetworks,
			ExternalNetworks: nc.Neighbors.ExternalNetworks,
		}
	}

	return vmSpec
}

func (nc *NetworkContext) applySlot() int {
	for i := 0; i <= MAX_NIC; i++ {
		if _, ok := nc.eth[i]; !ok {
			nc.eth[i] = nil
			return i
		}
	}

	return -1
}

func (nc *NetworkContext) freeSlot(slot int) {
	if inf, ok := nc.eth[slot]; !ok {
		nc.sandbox.Log(WARNING, "Freeing an unoccupied eth slot %d", slot)
		return
	} else {
		if _, ok := nc.idMap[inf.Id]; ok {
			delete(nc.idMap, inf.Id)
		}
		nc.sandbox.Log(DEBUG, "Free slot %d of eth", slot)
		delete(nc.eth, slot)
	}
}

func (nc *NetworkContext) addInterface(inf *api.InterfaceDescription, result chan api.Result) {
	if inf.Lo {
		if inf.Ip == "" {
			estr := fmt.Sprintf("creating an interface without an IP address: %#v", inf)
			nc.sandbox.Log(ERROR, estr)
			result <- NewSpecError(inf.Id, estr)
			return
		}
		i := &InterfaceCreated{
			Id:         inf.Id,
			DeviceName: "lo",
			IpAddr:     inf.Ip,
			NetMask:    "255.255.255.255",
		}
		nc.lo[inf.Ip] = i
		nc.idMap[inf.Id] = i

		result <- &api.ResultBase{
			Id:      inf.Id,
			Success: true,
		}
		return
	}

	var devChan chan VmEvent = make(chan VmEvent, 1)

	go func() {
		nc.slotLock.Lock()
		defer nc.slotLock.Unlock()

		idx := nc.applySlot()
		if idx < 0 {
			estr := fmt.Sprintf("no available ethernet slot for interface %#v", inf)
			nc.sandbox.Log(ERROR, estr)
			result <- NewBusyError(inf.Id, estr)
			close(devChan)
			return
		}

		nc.configureInterface(idx, nc.sandbox.nextPciAddr(), fmt.Sprintf("eth%d", idx), inf, devChan)
	}()

	go func() {
		ev, ok := <-devChan
		if !ok {
			nc.sandbox.Log(ERROR, "chan closed while waiting network inserted event: %#v", ev)
			return
		}
		// ev might be DeviceInsert failed, or inserted
		if fe, ok := ev.(*DeviceFailed); ok {
			if inf, ok := fe.Session.(*InterfaceCreated); ok {
				nc.netdevInsertFailed(inf.Index, inf.DeviceName)
				nc.sandbox.Log(ERROR, "interface creation failed: %#v", inf)
			} else if inf, ok := fe.Session.(*NetDevInsertedEvent); ok {
				nc.netdevInsertFailed(inf.Index, inf.DeviceName)
				nc.sandbox.Log(ERROR, "interface creation failed: %#v", inf)
			}
			result <- fe
			return
		} else if ni, ok := ev.(*NetDevInsertedEvent); ok {
			nc.sandbox.Log(DEBUG, "nic insert success: %s", ni.Id)
			result <- ni
			return
		}
		nc.sandbox.Log(ERROR, "got unknown event while waiting network inserted event: %#v", ev)
		result <- NewDeviceError(inf.Id, "unknown event")
	}()
}

func (nc *NetworkContext) removeInterface(id string, result chan api.Result) {
	if inf, ok := nc.idMap[id]; !ok {
		nc.sandbox.Log(WARNING, "trying remove a non-exist interface %s", id)
		result <- api.NewResultBase(id, true, "not exist")
		return
	} else if inf.HostDevice == "" { // a virtual interface
		delete(nc.idMap, id)
		delete(nc.lo, inf.IpAddr)
		result <- api.NewResultBase(id, true, "")
		return
	} else {
		nc.slotLock.Lock()
		defer nc.slotLock.Unlock()

		if _, ok := nc.eth[inf.Index]; !ok {
			delete(nc.idMap, id)
			nc.sandbox.Log(INFO, "non-configured network device %d remove failed", inf.Index)
			result <- api.NewResultBase(id, true, "not configured eth")
			return
		}

		var devChan chan VmEvent = make(chan VmEvent, 1)

		nc.sandbox.Log(DEBUG, "remove network card %d: %s", inf.Index, inf.IpAddr)
		nc.sandbox.DCtx.RemoveNic(nc.sandbox, inf, &NetDevRemovedEvent{Index: inf.Index}, devChan)

		go func() {
			ev, ok := <-devChan
			if !ok {
				return
			}

			success := true
			message := ""

			if fe, ok := ev.(*DeviceFailed); ok {
				success = false
				message = "unplug failed"
				if inf, ok := fe.Session.(*NetDevRemovedEvent); ok {
					nc.sandbox.Log(ERROR, "interface remove failed: %#v", inf)
				}
			}

			nc.slotLock.Lock()
			defer nc.slotLock.Unlock()
			nc.freeSlot(inf.Index)
			nc.cleanupInf(inf)

			result <- api.NewResultBase(id, success, message)
		}()
	}
}

func (nc *NetworkContext) netdevInsertFailed(idx int, name string) {
	nc.slotLock.Lock()
	defer nc.slotLock.Unlock()

	if _, ok := nc.eth[idx]; !ok {
		nc.sandbox.Log(INFO, "network device %d (%s) insert failed before configured", idx, name)
		return
	}

	nc.sandbox.Log(INFO, "network device %d (%s) insert failed", idx, name)
	nc.freeSlot(idx)
}

func (nc *NetworkContext) configureInterface(index, pciAddr int, name string, inf *api.InterfaceDescription, result chan<- VmEvent) {
	var (
		err      error
		settings *network.Settings
	)

	if HDriver.BuildinNetwork() {
		/* VBox doesn't support join to bridge */
		settings, err = nc.sandbox.DCtx.ConfigureNetwork(nc.sandbox.Id, "", inf)
	} else {
		settings, err = network.Configure(nc.sandbox.Id, "", false, inf)
	}

	if err != nil {
		nc.sandbox.Log(ERROR, "interface creating failed: %v", err.Error())
		session := &InterfaceCreated{Id: inf.Id, Index: index, PCIAddr: pciAddr, DeviceName: name}
		result <- &DeviceFailed{Session: session}
		return
	}

	created, err := interfaceGot(inf.Id, index, pciAddr, name, settings)
	if err != nil {
		result <- &DeviceFailed{Session: created}
		return
	}

	h := &HostNicInfo{
		Id:      created.Id,
		Fd:      uint64(created.Fd.Fd()),
		Device:  created.HostDevice,
		Mac:     created.MacAddr,
		Bridge:  created.Bridge,
		Gateway: created.Bridge,
	}
	g := &GuestNicInfo{
		Device:  created.DeviceName,
		Ipaddr:  created.IpAddr,
		Index:   created.Index,
		Busaddr: created.PCIAddr,
	}

	nc.eth[index] = created
	nc.idMap[created.Id] = created
	nc.sandbox.DCtx.AddNic(nc.sandbox, h, g, result)
}

func (nc *NetworkContext) cleanupInf(inf *InterfaceCreated) {
	if !HDriver.BuildinNetwork() && inf.Fd != nil {
		network.Close(inf.Fd)
		inf.Fd = nil
	}

}

func (nc *NetworkContext) getInterface(id string) *InterfaceCreated {
	nc.slotLock.RLock()
	defer nc.slotLock.RUnlock()

	inf, ok := nc.idMap[id]
	if ok {
		return inf
	}
	return nil
}

func (nc *NetworkContext) getIpAddrs() []string {
	nc.slotLock.RLock()
	defer nc.slotLock.RUnlock()

	res := []string{}
	for _, inf := range nc.eth {
		res = append(res, inf.IpAddr)
	}

	return res
}

func (nc *NetworkContext) getRoutes() []hyperstartapi.Route {
	nc.slotLock.RLock()
	defer nc.slotLock.RUnlock()
	routes := []hyperstartapi.Route{}

	for _, inf := range nc.idMap {
		for _, r := range inf.RouteTable {
			routes = append(routes, hyperstartapi.Route{
				Dest:    r.Destination,
				Gateway: r.Gateway,
				Device:  inf.DeviceName,
			})
		}
	}

	return routes
}

func (nc *NetworkContext) close() {
	nc.slotLock.Lock()
	defer nc.slotLock.Unlock()

	for _, inf := range nc.eth {
		nc.cleanupInf(inf)
	}
	nc.eth = map[int]*InterfaceCreated{}
	nc.lo = map[string]*InterfaceCreated{}
	nc.idMap = map[string]*InterfaceCreated{}
}

func interfaceGot(id string, index int, pciAddr int, name string, inf *network.Settings) (*InterfaceCreated, error) {
	ip, nw, err := net.ParseCIDR(fmt.Sprintf("%s/%d", inf.IPAddress, inf.IPPrefixLen))
	if err != nil {
		return &InterfaceCreated{Index: index, PCIAddr: pciAddr, DeviceName: name}, err
	}
	var tmp []byte = nw.Mask
	var mask net.IP = tmp

	rt := []*RouteRule{}
	/* Route rule is generated automaticly on first interface,
	 * or generated on the gateway configured interface. */
	if (index == 0 && inf.Automatic) || (!inf.Automatic && inf.Gateway != "") {
		rt = append(rt, &RouteRule{
			Destination: "0.0.0.0/0",
			Gateway:     inf.Gateway, ViaThis: true,
		})
	}

	return &InterfaceCreated{
		Id:         id,
		Index:      index,
		PCIAddr:    pciAddr,
		Bridge:     inf.Bridge,
		HostDevice: inf.Device,
		DeviceName: name,
		Fd:         inf.File,
		MacAddr:    inf.Mac,
		IpAddr:     ip.String(),
		NetMask:    mask.String(),
		RouteTable: rt,
	}, nil
}
<|endoftext|>


<commit_before>// THE AUTOGENERATED LICENSE. ALL THE RIGHTS ARE RESERVED BY ROBOTS.

// WARNING: This file has automatically been generated on Sat, 05 Nov 2016 19:40:00 MSK.
// By https://git.io/cgogen. DO NOT EDIT.

package android

/*
#cgo LDFLAGS: -landroid -llog
#include <android/api-level.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/configuration.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/log.h>
#include <android/looper.h>
#include <android/native_activity.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <android/obb.h>
#include <android/rect.h>
#include <android/sensor.h>
#include <android/storage_manager.h>
#include <android/tts.h>
#include <android/window.h>
#include <stdlib.h>
#include "cgo_helpers.h"
*/
import "C"
import "unsafe"

// AssetManager as declared in android/asset_manager.h:28
type AssetManager C.AAssetManager

// AssetDir as declared in android/asset_manager.h:31
type AssetDir C.AAssetDir

// Asset as declared in android/asset_manager.h:34
type Asset C.AAsset

// Jobject type as declared in include/jni.h:98
type Jobject unsafe.Pointer

// JNINativeMethod as declared in include/jni.h:147
type JNINativeMethod C.JNINativeMethod

// JNIEnv as declared in include/jni.h:157
type JNIEnv C.JNIEnv

// JavaVM as declared in include/jni.h:158
type JavaVM C.JavaVM

// JavaVMAttachArgs as declared in include/jni.h:1106
type JavaVMAttachArgs C.JavaVMAttachArgs

// JavaVMOption as declared in include/jni.h:1115
type JavaVMOption C.JavaVMOption

// JavaVMInitArgs as declared in include/jni.h:1123
type JavaVMInitArgs C.JavaVMInitArgs

// Configuration as declared in android/configuration.h:27
type Configuration C.AConfiguration

// Looper as declared in android/looper.h:39
type Looper C.ALooper

// LooperCallbackFunc type as declared in android/looper.h:159
type LooperCallbackFunc func(fd int32, events int32, data unsafe.Pointer) int32

// NativeActivity as declared in android/native_activity.h:108
type NativeActivity struct {
	Callbacks        *NativeActivityCallbacks
	Vm               *JavaVM
	Env              *JNIEnv
	Clazz            Jobject
	InternalDataPath string
	ExternalDataPath string
	SdkVersion       int32
	Instance         unsafe.Pointer
	AssetManager     *AssetManager
	ObbPath          string
	ref2cc295bf      *C.ANativeActivity
	allocs2cc295bf   interface{}
}

// NativeActivityCallbacks as declared in android/native_activity.h:225
type NativeActivityCallbacks C.ANativeActivityCallbacks

// NativeActivityCreateFunc type as declared in android/native_activity.h:235
type NativeActivityCreateFunc func(activity *NativeActivity, savedState unsafe.Pointer, savedStateSize uint32)

// InputEvent as declared in android/input.h:136
type InputEvent C.AInputEvent

// InputQueue as declared in android/input.h:803
type InputQueue C.AInputQueue

// NativeWindow as declared in android/native_window.h:36
type NativeWindow C.ANativeWindow

// NativeWindowBuffer as declared in android/native_window.h:57
type NativeWindowBuffer struct {
	Width          int32
	Height         int32
	Stride         int32
	Format         int32
	Bits           unsafe.Pointer
	Reserved       [6]uint32
	ref3db2646c    *C.ANativeWindow_Buffer
	allocs3db2646c interface{}
}

// Rect as declared in android/rect.h:35
type Rect struct {
	Left           int32
	Top            int32
	Right          int32
	Bottom         int32
	ref9511c547    *C.ARect
	allocs9511c547 interface{}
}

// ObbInfo as declared in android/obb.h:28
type ObbInfo C.AObbInfo

// SensorVector as declared in android/sensor.h:119
type SensorVector struct {
	Status         byte
	Reserved       [3]byte
	refdc35e822    *C.ASensorVector
	allocsdc35e822 interface{}
}

// MetaDataEvent as declared in android/sensor.h:124
type MetaDataEvent struct {
	What           int32
	Sensor         int32
	ref4f7ec3e5    *C.AMetaDataEvent
	allocs4f7ec3e5 interface{}
}

// UncalibratedEvent as declared in android/sensor.h:143
type UncalibratedEvent C.AUncalibratedEvent

// HeartRateEvent as declared in android/sensor.h:148
type HeartRateEvent struct {
	Bpm            float32
	Status         byte
	ref1342bcf5    *C.AHeartRateEvent
	allocs1342bcf5 interface{}
}

// SensorEvent as declared in android/sensor.h:181
type SensorEvent struct {
	Version        int32
	Sensor         int32
	Type           int32
	Reserved0      int32
	Timestamp      int64
	Flags          uint32
	Reserved1      [3]int32
	refb7a6c7dc    *C.ASensorEvent
	allocsb7a6c7dc interface{}
}

// SensorManager as declared in android/sensor.h:184
type SensorManager C.ASensorManager

// SensorEventQueue as declared in android/sensor.h:187
type SensorEventQueue C.ASensorEventQueue

// Sensor as declared in android/sensor.h:190
type Sensor C.ASensor

// SensorRef as declared in android/sensor.h:191
type SensorRef C.ASensor

// SensorList as declared in android/sensor.h:192
type SensorList C.ASensorRef

// StorageManager as declared in android/storage_manager.h:28
type StorageManager C.AStorageManager

// StorageManagerObbCallbackFunc type as declared in android/storage_manager.h:98
type StorageManagerObbCallbackFunc func(filename string, state int32, data unsafe.Pointer)
<commit_msg>Add Ptr() for NativeWindow to fetch its uintptr.<commit_after>// THE AUTOGENERATED LICENSE. ALL THE RIGHTS ARE RESERVED BY ROBOTS.

// WARNING: This file has automatically been generated on Sat, 05 Nov 2016 22:31:36 MSK.
// By https://git.io/cgogen. DO NOT EDIT.

package android

/*
#cgo LDFLAGS: -landroid -llog
#include <android/api-level.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/configuration.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/log.h>
#include <android/looper.h>
#include <android/native_activity.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <android/obb.h>
#include <android/rect.h>
#include <android/sensor.h>
#include <android/storage_manager.h>
#include <android/tts.h>
#include <android/window.h>
#include <stdlib.h>
#include "cgo_helpers.h"
*/
import "C"
import "unsafe"

// AssetManager as declared in android/asset_manager.h:28
type AssetManager C.AAssetManager

// AssetDir as declared in android/asset_manager.h:31
type AssetDir C.AAssetDir

// Asset as declared in android/asset_manager.h:34
type Asset C.AAsset

// Jobject type as declared in include/jni.h:98
type Jobject unsafe.Pointer

// JNINativeMethod as declared in include/jni.h:147
type JNINativeMethod C.JNINativeMethod

// JNIEnv as declared in include/jni.h:157
type JNIEnv C.JNIEnv

// JavaVM as declared in include/jni.h:158
type JavaVM C.JavaVM

// JavaVMAttachArgs as declared in include/jni.h:1106
type JavaVMAttachArgs C.JavaVMAttachArgs

// JavaVMOption as declared in include/jni.h:1115
type JavaVMOption C.JavaVMOption

// JavaVMInitArgs as declared in include/jni.h:1123
type JavaVMInitArgs C.JavaVMInitArgs

// Configuration as declared in android/configuration.h:27
type Configuration C.AConfiguration

// Looper as declared in android/looper.h:39
type Looper C.ALooper

// LooperCallbackFunc type as declared in android/looper.h:159
type LooperCallbackFunc func(fd int32, events int32, data unsafe.Pointer) int32

// NativeActivity as declared in android/native_activity.h:108
type NativeActivity struct {
	Callbacks        *NativeActivityCallbacks
	Vm               *JavaVM
	Env              *JNIEnv
	Clazz            Jobject
	InternalDataPath string
	ExternalDataPath string
	SdkVersion       int32
	Instance         unsafe.Pointer
	AssetManager     *AssetManager
	ObbPath          string
	ref2cc295bf      *C.ANativeActivity
	allocs2cc295bf   interface{}
}

// NativeActivityCallbacks as declared in android/native_activity.h:225
type NativeActivityCallbacks C.ANativeActivityCallbacks

// NativeActivityCreateFunc type as declared in android/native_activity.h:235
type NativeActivityCreateFunc func(activity *NativeActivity, savedState unsafe.Pointer, savedStateSize uint32)

// InputEvent as declared in android/input.h:136
type InputEvent C.AInputEvent

// InputQueue as declared in android/input.h:803
type InputQueue C.AInputQueue

// NativeWindow as declared in android/native_window.h:36
type NativeWindow C.ANativeWindow

func (n *NativeWindow) Ptr() uintptr {
	return uintptr(unsafe.Pointer(n))
}

// NativeWindowBuffer as declared in android/native_window.h:57
type NativeWindowBuffer struct {
	Width          int32
	Height         int32
	Stride         int32
	Format         int32
	Bits           unsafe.Pointer
	Reserved       [6]uint32
	ref3db2646c    *C.ANativeWindow_Buffer
	allocs3db2646c interface{}
}

// Rect as declared in android/rect.h:35
type Rect struct {
	Left           int32
	Top            int32
	Right          int32
	Bottom         int32
	ref9511c547    *C.ARect
	allocs9511c547 interface{}
}

// ObbInfo as declared in android/obb.h:28
type ObbInfo C.AObbInfo

// SensorVector as declared in android/sensor.h:119
type SensorVector struct {
	Status         byte
	Reserved       [3]byte
	refdc35e822    *C.ASensorVector
	allocsdc35e822 interface{}
}

// MetaDataEvent as declared in android/sensor.h:124
type MetaDataEvent struct {
	What           int32
	Sensor         int32
	ref4f7ec3e5    *C.AMetaDataEvent
	allocs4f7ec3e5 interface{}
}

// UncalibratedEvent as declared in android/sensor.h:143
type UncalibratedEvent C.AUncalibratedEvent

// HeartRateEvent as declared in android/sensor.h:148
type HeartRateEvent struct {
	Bpm            float32
	Status         byte
	ref1342bcf5    *C.AHeartRateEvent
	allocs1342bcf5 interface{}
}

// SensorEvent as declared in android/sensor.h:181
type SensorEvent struct {
	Version        int32
	Sensor         int32
	Type           int32
	Reserved0      int32
	Timestamp      int64
	Flags          uint32
	Reserved1      [3]int32
	refb7a6c7dc    *C.ASensorEvent
	allocsb7a6c7dc interface{}
}

// SensorManager as declared in android/sensor.h:184
type SensorManager C.ASensorManager

// SensorEventQueue as declared in android/sensor.h:187
type SensorEventQueue C.ASensorEventQueue

// Sensor as declared in android/sensor.h:190
type Sensor C.ASensor

// SensorRef as declared in android/sensor.h:191
type SensorRef C.ASensor

// SensorList as declared in android/sensor.h:192
type SensorList C.ASensorRef

// StorageManager as declared in android/storage_manager.h:28
type StorageManager C.AStorageManager

// StorageManagerObbCallbackFunc type as declared in android/storage_manager.h:98
type StorageManagerObbCallbackFunc func(filename string, state int32, data unsafe.Pointer)
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"runtime"
	"strconv"
	"syscall"

	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/projectatomic/buildah/unshare"
	"github.com/projectatomic/buildah/util"
	"github.com/sirupsen/logrus"
	"github.com/syndtr/gocapability/capability"
	"github.com/urfave/cli"
)

const (
	// startedInUserNS is an environment variable that, if set, means that we shouldn't try
	// to create and enter a new user namespace and then re-exec ourselves.
	startedInUserNS = "_BUILDAH_STARTED_IN_USERNS"
)

var (
	unshareDescription = "Runs a command in a modified user namespace"
	unshareCommand     = cli.Command{
		Name:           "unshare",
		Usage:          "Run a command in a modified user namespace",
		Description:    unshareDescription,
		Action:         unshareCmd,
		ArgsUsage:      "[COMMAND [ARGS [...]]]",
		SkipArgReorder: true,
	}
)

type runnable interface {
	Run() error
}

func bailOnError(err error, format string, a ...interface{}) {
	if err != nil {
		if format != "" {
			logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
		} else {
			logrus.Errorf("%v", err)
		}
		cli.OsExiter(1)
	}
}

func maybeReexecUsingUserNamespace(c *cli.Context, evenForRoot bool) {
	// If we've already been through this once, no need to try again.
	if os.Getenv(startedInUserNS) != "" {
		return
	}

	// If this is one of the commands that doesn't need this indirection, skip it.
	if c.NArg() == 0 {
		return
	}
	switch c.Args()[0] {
	case "help", "version":
		return
	}

	// Figure out who we are.
	me, err := user.Current()
	bailOnError(err, "error determining current user")
	uidNum, err := strconv.ParseUint(me.Uid, 10, 32)
	bailOnError(err, "error parsing current UID %s", me.Uid)
	gidNum, err := strconv.ParseUint(me.Gid, 10, 32)
	bailOnError(err, "error parsing current GID %s", me.Gid)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// ID mappings to use to reexec ourselves.
	var uidmap, gidmap []specs.LinuxIDMapping
	if uidNum != 0 || evenForRoot {
		// Read the set of ID mappings that we're allowed to use.  Each
		// range in /etc/subuid and /etc/subgid file is a starting host
		// ID and a range size.
		uidmap, gidmap, err = util.GetSubIDMappings(me.Username, me.Username)
		bailOnError(err, "error reading allowed ID mappings")
		if len(uidmap) == 0 {
			logrus.Warnf("Found no UID ranges set aside for user %q in /etc/subuid.", me.Username)
		}
		if len(gidmap) == 0 {
			logrus.Warnf("Found no GID ranges set aside for user %q in /etc/subgid.", me.Username)
		}
		// Map our UID and GID, then the subuid and subgid ranges,
		// consecutively, starting at 0, to get the mappings to use for
		// a copy of ourselves.
		uidmap = append([]specs.LinuxIDMapping{{HostID: uint32(uidNum), ContainerID: 0, Size: 1}}, uidmap...)
		gidmap = append([]specs.LinuxIDMapping{{HostID: uint32(gidNum), ContainerID: 0, Size: 1}}, gidmap...)
		var rangeStart uint32
		for i := range uidmap {
			uidmap[i].ContainerID = rangeStart
			rangeStart += uidmap[i].Size
		}
		rangeStart = 0
		for i := range gidmap {
			gidmap[i].ContainerID = rangeStart
			rangeStart += gidmap[i].Size
		}
	} else {
		// If we have CAP_SYS_ADMIN, then we don't need to create a new namespace in order to be able
		// to use unshare(), so don't bother creating a new user namespace at this point.
		capabilities, err := capability.NewPid(0)
		bailOnError(err, "error reading the current capabilities sets")
		if capabilities.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) {
			return
		}
		// Read the set of ID mappings that we're currently using.
		uidmap, gidmap, err = util.GetHostIDMappings("")
		bailOnError(err, "error reading current ID mappings")
		// Just reuse them.
		for i := range uidmap {
			uidmap[i].HostID = uidmap[i].ContainerID
		}
		for i := range gidmap {
			gidmap[i].HostID = gidmap[i].ContainerID
		}
	}

	var moreArgs []string
	// Add args to change the global defaults.
	if uidNum != 0 {
		if !c.GlobalIsSet("storage-driver") || !c.GlobalIsSet("root") || !c.GlobalIsSet("runroot") {
			logrus.Infof("Running without privileges, assuming arguments:")
			if !c.GlobalIsSet("storage-driver") {
				defaultStorageDriver := "vfs"
				logrus.Infof(" --storage-driver %q", defaultStorageDriver)
				moreArgs = append(moreArgs, "--storage-driver", defaultStorageDriver)
			}
			if !c.GlobalIsSet("root") {
				defaultRoot, err := util.UnsharedRootPath(me.HomeDir)
				bailOnError(err, "")
				logrus.Infof(" --root %q", defaultRoot)
				moreArgs = append(moreArgs, "--root", defaultRoot)
			}
			if !c.GlobalIsSet("runroot") {
				defaultRunroot, err := util.UnsharedRunrootPath(me.Uid)
				bailOnError(err, "")
				logrus.Infof(" --runroot %q", defaultRunroot)
				moreArgs = append(moreArgs, "--runroot", defaultRunroot)
			}
		}
	}

	// Unlike most uses of reexec or unshare, we're using a name that
	// _won't_ be recognized as a registered reexec handler, since we
	// _want_ to fall through reexec.Init() to the normal main().
	cmd := unshare.Command(append(append([]string{"buildah-in-a-user-namespace"}, moreArgs...), os.Args[1:]...)...)

	// If, somehow, we don't become UID 0 in our child, indicate that the child shouldn't try again.
	err = os.Setenv(startedInUserNS, "1")
	bailOnError(err, "error setting %s=1 in environment", startedInUserNS)

	// Set the default isolation type to use the "chroot" method.
	if _, ok := os.LookupEnv("BUILDAH_ISOLATION"); !ok {
		if err = os.Setenv("BUILDAH_ISOLATION", "chroot"); err != nil {
			logrus.Errorf("error setting BUILDAH_ISOLATION=chroot in environment: %v", err)
			os.Exit(1)
		}
	}

	// Reuse our stdio.
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Set up a new user namespace with the ID mapping.
	cmd.UnshareFlags = syscall.CLONE_NEWUSER
	cmd.UseNewuidmap = uidNum != 0
	cmd.UidMappings = uidmap
	cmd.UseNewgidmap = uidNum != 0
	cmd.GidMappings = gidmap
	cmd.GidMappingsEnableSetgroups = true

	// Finish up.
	logrus.Debugf("running %+v with environment %+v, UID map %+v, and GID map %+v", cmd.Cmd.Args, os.Environ(), cmd.UidMappings, cmd.GidMappings)
	execRunnable(cmd)
}

// execRunnable runs the specified unshare command, captures its exit status,
// and exits with the same status.
func execRunnable(cmd runnable) {
	if err := cmd.Run(); err != nil {
		if exitError, ok := errors.Cause(err).(*exec.ExitError); ok {
			if exitError.ProcessState.Exited() {
				if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
					if waitStatus.Exited() {
						logrus.Errorf("%v", exitError)
						os.Exit(waitStatus.ExitStatus())
					}
					if waitStatus.Signaled() {
						logrus.Errorf("%v", exitError)
						os.Exit(int(waitStatus.Signal()) + 128)
					}
				}
			}
		}
		logrus.Errorf("%v", err)
		logrus.Errorf("(unable to determine exit status)")
		os.Exit(1)
	}
	os.Exit(0)
}

// unshareCmd execs whatever using the ID mappings that we want to use for ourselves
func unshareCmd(c *cli.Context) error {
	// force reexec using the configured ID mappings
	maybeReexecUsingUserNamespace(c, true)
	// exec the specified command, if there is one
	args := c.Args()
	if len(args) < 1 {
		// try to exec the shell, if one's set
		shell, shellSet := os.LookupEnv("SHELL")
		if !shellSet {
			logrus.Errorf("no command specified")
			os.Exit(1)
		}
		args = []string{shell}
	}
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Env = append(os.Environ(), "USER=root", "USERNAME=root", "GROUP=root", "LOGNAME=root", "UID=0", "GID=0")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	execRunnable(cmd)
	os.Exit(1)
	return nil
}
<commit_msg>Set BUILDAH_ISOLATION=rootless when running unprivileged<commit_after>// +build linux

package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"runtime"
	"strconv"
	"syscall"

	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/projectatomic/buildah/unshare"
	"github.com/projectatomic/buildah/util"
	"github.com/sirupsen/logrus"
	"github.com/syndtr/gocapability/capability"
	"github.com/urfave/cli"
)

const (
	// startedInUserNS is an environment variable that, if set, means that we shouldn't try
	// to create and enter a new user namespace and then re-exec ourselves.
	startedInUserNS = "_BUILDAH_STARTED_IN_USERNS"
)

var (
	unshareDescription = "Runs a command in a modified user namespace"
	unshareCommand     = cli.Command{
		Name:           "unshare",
		Usage:          "Run a command in a modified user namespace",
		Description:    unshareDescription,
		Action:         unshareCmd,
		ArgsUsage:      "[COMMAND [ARGS [...]]]",
		SkipArgReorder: true,
	}
)

type runnable interface {
	Run() error
}

func bailOnError(err error, format string, a ...interface{}) {
	if err != nil {
		if format != "" {
			logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
		} else {
			logrus.Errorf("%v", err)
		}
		cli.OsExiter(1)
	}
}

func maybeReexecUsingUserNamespace(c *cli.Context, evenForRoot bool) {
	// If we've already been through this once, no need to try again.
	if os.Getenv(startedInUserNS) != "" {
		return
	}

	// If this is one of the commands that doesn't need this indirection, skip it.
	if c.NArg() == 0 {
		return
	}
	switch c.Args()[0] {
	case "help", "version":
		return
	}

	// Figure out who we are.
	me, err := user.Current()
	bailOnError(err, "error determining current user")
	uidNum, err := strconv.ParseUint(me.Uid, 10, 32)
	bailOnError(err, "error parsing current UID %s", me.Uid)
	gidNum, err := strconv.ParseUint(me.Gid, 10, 32)
	bailOnError(err, "error parsing current GID %s", me.Gid)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// ID mappings to use to reexec ourselves.
	var uidmap, gidmap []specs.LinuxIDMapping
	if uidNum != 0 || evenForRoot {
		// Read the set of ID mappings that we're allowed to use.  Each
		// range in /etc/subuid and /etc/subgid file is a starting host
		// ID and a range size.
		uidmap, gidmap, err = util.GetSubIDMappings(me.Username, me.Username)
		bailOnError(err, "error reading allowed ID mappings")
		if len(uidmap) == 0 {
			logrus.Warnf("Found no UID ranges set aside for user %q in /etc/subuid.", me.Username)
		}
		if len(gidmap) == 0 {
			logrus.Warnf("Found no GID ranges set aside for user %q in /etc/subgid.", me.Username)
		}
		// Map our UID and GID, then the subuid and subgid ranges,
		// consecutively, starting at 0, to get the mappings to use for
		// a copy of ourselves.
		uidmap = append([]specs.LinuxIDMapping{{HostID: uint32(uidNum), ContainerID: 0, Size: 1}}, uidmap...)
		gidmap = append([]specs.LinuxIDMapping{{HostID: uint32(gidNum), ContainerID: 0, Size: 1}}, gidmap...)
		var rangeStart uint32
		for i := range uidmap {
			uidmap[i].ContainerID = rangeStart
			rangeStart += uidmap[i].Size
		}
		rangeStart = 0
		for i := range gidmap {
			gidmap[i].ContainerID = rangeStart
			rangeStart += gidmap[i].Size
		}
	} else {
		// If we have CAP_SYS_ADMIN, then we don't need to create a new namespace in order to be able
		// to use unshare(), so don't bother creating a new user namespace at this point.
		capabilities, err := capability.NewPid(0)
		bailOnError(err, "error reading the current capabilities sets")
		if capabilities.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) {
			return
		}
		// Read the set of ID mappings that we're currently using.
		uidmap, gidmap, err = util.GetHostIDMappings("")
		bailOnError(err, "error reading current ID mappings")
		// Just reuse them.
		for i := range uidmap {
			uidmap[i].HostID = uidmap[i].ContainerID
		}
		for i := range gidmap {
			gidmap[i].HostID = gidmap[i].ContainerID
		}
	}

	var moreArgs []string
	// Add args to change the global defaults.
	if uidNum != 0 {
		if !c.GlobalIsSet("storage-driver") || !c.GlobalIsSet("root") || !c.GlobalIsSet("runroot") {
			logrus.Infof("Running without privileges, assuming arguments:")
			if !c.GlobalIsSet("storage-driver") {
				defaultStorageDriver := "vfs"
				logrus.Infof(" --storage-driver %q", defaultStorageDriver)
				moreArgs = append(moreArgs, "--storage-driver", defaultStorageDriver)
			}
			if !c.GlobalIsSet("root") {
				defaultRoot, err := util.UnsharedRootPath(me.HomeDir)
				bailOnError(err, "")
				logrus.Infof(" --root %q", defaultRoot)
				moreArgs = append(moreArgs, "--root", defaultRoot)
			}
			if !c.GlobalIsSet("runroot") {
				defaultRunroot, err := util.UnsharedRunrootPath(me.Uid)
				bailOnError(err, "")
				logrus.Infof(" --runroot %q", defaultRunroot)
				moreArgs = append(moreArgs, "--runroot", defaultRunroot)
			}
		}
	}

	// Unlike most uses of reexec or unshare, we're using a name that
	// _won't_ be recognized as a registered reexec handler, since we
	// _want_ to fall through reexec.Init() to the normal main().
	cmd := unshare.Command(append(append([]string{"buildah-in-a-user-namespace"}, moreArgs...), os.Args[1:]...)...)

	// If, somehow, we don't become UID 0 in our child, indicate that the child shouldn't try again.
	err = os.Setenv(startedInUserNS, "1")
	bailOnError(err, "error setting %s=1 in environment", startedInUserNS)

	// Set the default isolation type to use the "rootless" method.
	if _, present := os.LookupEnv("BUILDAH_ISOLATION"); !present {
		if err = os.Setenv("BUILDAH_ISOLATION", "rootless"); err != nil {
			logrus.Errorf("error setting BUILDAH_ISOLATION=rootless in environment: %v", err)
			os.Exit(1)
		}
	}

	// Reuse our stdio.
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Set up a new user namespace with the ID mapping.
	cmd.UnshareFlags = syscall.CLONE_NEWUSER
	cmd.UseNewuidmap = uidNum != 0
	cmd.UidMappings = uidmap
	cmd.UseNewgidmap = uidNum != 0
	cmd.GidMappings = gidmap
	cmd.GidMappingsEnableSetgroups = true

	// Finish up.
	logrus.Debugf("running %+v with environment %+v, UID map %+v, and GID map %+v", cmd.Cmd.Args, os.Environ(), cmd.UidMappings, cmd.GidMappings)
	execRunnable(cmd)
}

// execRunnable runs the specified unshare command, captures its exit status,
// and exits with the same status.
func execRunnable(cmd runnable) {
	if err := cmd.Run(); err != nil {
		if exitError, ok := errors.Cause(err).(*exec.ExitError); ok {
			if exitError.ProcessState.Exited() {
				if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
					if waitStatus.Exited() {
						logrus.Errorf("%v", exitError)
						os.Exit(waitStatus.ExitStatus())
					}
					if waitStatus.Signaled() {
						logrus.Errorf("%v", exitError)
						os.Exit(int(waitStatus.Signal()) + 128)
					}
				}
			}
		}
		logrus.Errorf("%v", err)
		logrus.Errorf("(unable to determine exit status)")
		os.Exit(1)
	}
	os.Exit(0)
}

// unshareCmd execs whatever using the ID mappings that we want to use for ourselves
func unshareCmd(c *cli.Context) error {
	// force reexec using the configured ID mappings
	maybeReexecUsingUserNamespace(c, true)
	// exec the specified command, if there is one
	args := c.Args()
	if len(args) < 1 {
		// try to exec the shell, if one's set
		shell, shellSet := os.LookupEnv("SHELL")
		if !shellSet {
			logrus.Errorf("no command specified")
			os.Exit(1)
		}
		args = []string{shell}
	}
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Env = append(os.Environ(), "USER=root", "USERNAME=root", "GROUP=root", "LOGNAME=root", "UID=0", "GID=0")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	execRunnable(cmd)
	os.Exit(1)
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"bytes"
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/mvdan/basedir"
	"github.com/mvdan/fdroidcl"
)

var cmdUpdate = &Command{
	UsageLine: "update",
	Short:     "Update the index",
}

func init() {
	cmdUpdate.Run = runUpdate
}

func runUpdate(args []string) {
	if err := updateIndex(); err != nil {
		log.Fatalf("Could not update index: %v", err)
	}
}

func updateIndex() error {
	url := fmt.Sprintf("%s/%s", repoURL, "index.jar")
	if err := downloadEtag(url, indexPath(repoName), nil); err != nil {
		return err
	}
	return nil
}

func respEtag(resp *http.Response) string {
	etags, e := resp.Header["Etag"]
	if !e || len(etags) == 0 {
		return ""
	}
	return etags[0]
}

func downloadEtag(url, path string, sum []byte) error {
	fmt.Printf("Downloading %s... ", url)
	defer fmt.Println()
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)

	etagPath := path + "-etag"
	if _, err := os.Stat(path); err == nil {
		etag, _ := ioutil.ReadFile(etagPath)
		req.Header.Add("If-None-Match", string(etag))
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusNotModified {
		fmt.Printf("not modified")
		return nil
	}
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	if sum == nil {
		_, err := io.Copy(f, resp.Body)
		if err != nil {
			return err
		}
	} else {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		got := sha256.Sum256(data)
		if !bytes.Equal(sum, got[:]) {
			return errors.New("sha256 mismatch")
		}
		if _, err := f.Write(data); err != nil {
			return err
		}
	}
	if err := ioutil.WriteFile(etagPath, []byte(respEtag(resp)), 0644); err != nil {
		return err
	}
	fmt.Printf("done")
	return nil
}

func indexPath(name string) string {
	cache, err := basedir.Cache()
	if err != nil {
		log.Fatalf("Could not determine cache dir: %v", err)
	}
	return filepath.Join(appSubdir(cache), repoName+".jar")
}

func appSubdir(dir string, subdir ...string) string {
	elems := append([]string{dir, "fdroidcl"}, subdir...)
	p := filepath.Join(elems...)
	if err := os.MkdirAll(p, 0755); err != nil {
		log.Fatalf("Could not create app dir: %v", err)
	}
	return p
}

func mustLoadIndex() *fdroidcl.Index {
	p := indexPath(repoName)
	f, err := os.Open(p)
	if err != nil {
		log.Fatalf("Could not open index file: %v", err)
	}
	stat, err := f.Stat()
	if err != nil {
		log.Fatalf("Could not stat index file: %v", err)
	}
	//pubkey, err := hex.DecodeString(repoPubkey)
	//if err != nil {
	//	log.Fatalf("Could not decode public key: %v", err)
	//}
	index, err := fdroidcl.LoadIndexJar(f, stat.Size(), nil)
	if err != nil {
		log.Fatalf("Could not load index: %v", err)
	}
	return index
}
<commit_msg>Properly close files after downloading them<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"bytes"
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/mvdan/basedir"
	"github.com/mvdan/fdroidcl"
)

var cmdUpdate = &Command{
	UsageLine: "update",
	Short:     "Update the index",
}

func init() {
	cmdUpdate.Run = runUpdate
}

func runUpdate(args []string) {
	if err := updateIndex(); err != nil {
		log.Fatalf("Could not update index: %v", err)
	}
}

func updateIndex() error {
	url := fmt.Sprintf("%s/%s", repoURL, "index.jar")
	if err := downloadEtag(url, indexPath(repoName), nil); err != nil {
		return err
	}
	return nil
}

func respEtag(resp *http.Response) string {
	etags, e := resp.Header["Etag"]
	if !e || len(etags) == 0 {
		return ""
	}
	return etags[0]
}

func downloadEtag(url, path string, sum []byte) error {
	fmt.Printf("Downloading %s... ", url)
	defer fmt.Println()
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)

	etagPath := path + "-etag"
	if _, err := os.Stat(path); err == nil {
		etag, _ := ioutil.ReadFile(etagPath)
		req.Header.Add("If-None-Match", string(etag))
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusNotModified {
		fmt.Printf("not modified")
		return nil
	}
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	if sum == nil {
		_, err := io.Copy(f, resp.Body)
		if err != nil {
			return err
		}
	} else {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		got := sha256.Sum256(data)
		if !bytes.Equal(sum, got[:]) {
			return errors.New("sha256 mismatch")
		}
		if _, err := f.Write(data); err != nil {
			return err
		}
	}
	if err := ioutil.WriteFile(etagPath, []byte(respEtag(resp)), 0644); err != nil {
		return err
	}
	fmt.Printf("done")
	return nil
}

func indexPath(name string) string {
	cache, err := basedir.Cache()
	if err != nil {
		log.Fatalf("Could not determine cache dir: %v", err)
	}
	return filepath.Join(appSubdir(cache), repoName+".jar")
}

func appSubdir(dir string, subdir ...string) string {
	elems := append([]string{dir, "fdroidcl"}, subdir...)
	p := filepath.Join(elems...)
	if err := os.MkdirAll(p, 0755); err != nil {
		log.Fatalf("Could not create app dir: %v", err)
	}
	return p
}

func mustLoadIndex() *fdroidcl.Index {
	p := indexPath(repoName)
	f, err := os.Open(p)
	if err != nil {
		log.Fatalf("Could not open index file: %v", err)
	}
	stat, err := f.Stat()
	if err != nil {
		log.Fatalf("Could not stat index file: %v", err)
	}
	//pubkey, err := hex.DecodeString(repoPubkey)
	//if err != nil {
	//	log.Fatalf("Could not decode public key: %v", err)
	//}
	index, err := fdroidcl.LoadIndexJar(f, stat.Size(), nil)
	if err != nil {
		log.Fatalf("Could not load index: %v", err)
	}
	return index
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/golib/signal"
)

var (
	stats *peekStats
)

type peekStats struct {
	MsgCountPerSecond metrics.Meter
	MsgBytesPerSecond metrics.Meter
}

func newPeekStats() *peekStats {
	this := &peekStats{
		MsgCountPerSecond: metrics.NewMeter(),
		MsgBytesPerSecond: metrics.NewMeter(),
	}

	metrics.Register("msg.count.per.second", this.MsgCountPerSecond)
	metrics.Register("msg.bytes.per.second", this.MsgBytesPerSecond)
	return this
}

func (this *peekStats) start() {
	metrics.Log(metrics.DefaultRegistry, time.Second*10,
		log.New(os.Stdout, "metrics: ", log.Lmicroseconds))
}

type Peek struct {
	Ui  cli.Ui
	Cmd string

	offset   int64
	lastN    int64 // peek the most recent N messages
	colorize bool
	limit    int
	quit     chan struct{}
	once     sync.Once
	column   string
	pretty   bool
	bodyOnly bool
}

func (this *Peek) Run(args []string) (exitCode int) {
	var (
		cluster      string
		zone         string
		topicPattern string
		partitionId  int
		wait         time.Duration
		silence      bool
	)
	cmdFlags := flag.NewFlagSet("peek", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&cluster, "c", "", "")
	cmdFlags.StringVar(&topicPattern, "t", "", "")
	cmdFlags.IntVar(&partitionId, "p", 0, "")
	cmdFlags.BoolVar(&this.colorize, "color", true, "")
	cmdFlags.Int64Var(&this.lastN, "last", -1, "")
	cmdFlags.BoolVar(&this.pretty, "pretty", false, "")
	cmdFlags.IntVar(&this.limit, "n", -1, "")
	cmdFlags.StringVar(&this.column, "col", "", "")
	cmdFlags.Int64Var(&this.offset, "offset", sarama.OffsetNewest, "")
	cmdFlags.BoolVar(&silence, "s", false, "")
	cmdFlags.DurationVar(&wait, "d", time.Hour, "")
	cmdFlags.BoolVar(&this.bodyOnly, "body", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if this.pretty {
		this.bodyOnly = true
	}

	this.quit = make(chan struct{})

	if silence {
		stats := newPeekStats()
		go stats.start()
	}

	zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
	msgChan := make(chan *sarama.ConsumerMessage, 20000) // msg aggerator channel
	if cluster == "" {
		zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
			this.consumeCluster(zkcluster, topicPattern, partitionId, msgChan)
		})
	} else {
		zkcluster := zkzone.NewCluster(cluster)
		this.consumeCluster(zkcluster, topicPattern, partitionId, msgChan)
	}

	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Printf("received signal: %s", strings.ToUpper(sig.String()))
		log.Println("quiting...")

		this.once.Do(func() {
			close(this.quit)
		})
	}, syscall.SIGINT, syscall.SIGTERM)

	var (
		startAt = time.Now()
		msg     *sarama.ConsumerMessage
		total   int
		bytesN  int64
	)

	var (
		j          map[string]string
		prettyJSON bytes.Buffer
	)

LOOP:
	for {
		if time.Since(startAt) >= wait {
			this.Ui.Output(fmt.Sprintf("Total: %s msgs, %s, elapsed: %s",
				gofmt.Comma(int64(total)), gofmt.ByteSize(bytesN), time.Since(startAt)))
			elapsed := time.Since(startAt).Seconds()
			if elapsed > 1. {
				this.Ui.Output(fmt.Sprintf("Speed: %d/s", total/int(elapsed)))
				if total > 0 {
					this.Ui.Output(fmt.Sprintf("Size : %s/msg", gofmt.ByteSize(bytesN/int64(total))))
				}
			}

			return
		}

		select {
		case <-this.quit:
			this.Ui.Output(fmt.Sprintf("Total: %s msgs, %s, elapsed: %s",
				gofmt.Comma(int64(total)), gofmt.ByteSize(bytesN), time.Since(startAt)))
			elapsed := time.Since(startAt).Seconds()
			if elapsed > 1. {
				this.Ui.Output(fmt.Sprintf("Speed: %d/s", total/int(elapsed)))
				if total > 0 {
					this.Ui.Output(fmt.Sprintf("Size : %s/msg", gofmt.ByteSize(bytesN/int64(total))))
				}
			}

			return

		case <-time.After(time.Second):
			continue

		case msg = <-msgChan:
			if silence {
				stats.MsgCountPerSecond.Mark(1)
				stats.MsgBytesPerSecond.Mark(int64(len(msg.Value)))
			} else {
				if this.column != "" {
					if err := json.Unmarshal(msg.Value, &j); err != nil {
						this.Ui.Error(err.Error())
					} else {
						if this.bodyOnly {
							if this.pretty {
								if err = json.Indent(&prettyJSON, []byte(j[this.column]), "", "    "); err != nil {
									fmt.Println(err.Error())
								} else {
									fmt.Println(string(prettyJSON.Bytes()))
								}
							} else {
								fmt.Println(j[this.column])
							}
						} else if this.colorize {
							this.Ui.Output(fmt.Sprintf("%s/%d %s k:%s v:%s",
								color.Green(msg.Topic), msg.Partition,
								gofmt.Comma(msg.Offset), string(msg.Key), j[this.column]))
						} else {
							// colored UI will have invisible chars output
							fmt.Println(fmt.Sprintf("%s/%d %s k:%s v:%s",
								msg.Topic, msg.Partition,
								gofmt.Comma(msg.Offset), string(msg.Key), j[this.column]))
						}
					}

				} else {
					if this.bodyOnly {
						if this.pretty {
							json.Indent(&prettyJSON, msg.Value, "", "    ")
							fmt.Println(string(prettyJSON.Bytes()))
						} else {
							fmt.Println(string(msg.Value))
						}
					} else if this.colorize {
						this.Ui.Output(fmt.Sprintf("%s/%d %s k:%s, v:%s",
							color.Green(msg.Topic), msg.Partition,
							gofmt.Comma(msg.Offset), string(msg.Key), string(msg.Value)))
					} else {
						// colored UI will have invisible chars output
						fmt.Println(fmt.Sprintf("%s/%d %s k:%s, v:%s",
							msg.Topic, msg.Partition,
							gofmt.Comma(msg.Offset), string(msg.Key), string(msg.Value)))
					}
				}
			}

			total++
			bytesN += int64(len(msg.Value))

			if this.limit > 0 && total >= this.limit {
				break LOOP

			}
			if this.lastN > 0 && total >= int(this.lastN) {
				break LOOP
			}

		}
	}

	return
}

func (this *Peek) consumeCluster(zkcluster *zk.ZkCluster, topicPattern string,
	partitionId int, msgChan chan *sarama.ConsumerMessage) {
	brokerList := zkcluster.BrokerList()
	if len(brokerList) == 0 {
		return
	}
	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		this.Ui.Output(err.Error())
		return
	}
	//defer kfk.Close() // FIXME how to close it

	topics, err := kfk.Topics()
	if err != nil {
		this.Ui.Output(err.Error())
		return
	}

	for _, t := range topics {
		if patternMatched(t, topicPattern) {
			go this.simpleConsumeTopic(zkcluster, kfk, t, int32(partitionId), msgChan)
		}
	}

}

func (this *Peek) simpleConsumeTopic(zkcluster *zk.ZkCluster, kfk sarama.Client, topic string, partitionId int32,
	msgCh chan *sarama.ConsumerMessage) {
	consumer, err := sarama.NewConsumerFromClient(kfk)
	if err != nil {
		panic(err)
	}
	defer consumer.Close()

	if partitionId == -1 {
		// all partitions
		partitions, err := kfk.Partitions(topic)
		if err != nil {
			panic(err)
		}

		for _, p := range partitions {
			offset := this.offset
			if this.lastN > 0 {
				latestOffset, err := kfk.GetOffset(topic, p, sarama.OffsetNewest)
				swallow(err)

				oldestOffset, err := kfk.GetOffset(topic, p, sarama.OffsetOldest)
				swallow(err)

				offset = latestOffset - this.lastN
				if offset < oldestOffset {
					offset = oldestOffset
				}

				if offset == 0 {
					// no message in store
					return
				}
			}

			go this.consumePartition(zkcluster, kfk, consumer, topic, p, msgCh, offset)
		}

	} else {
		offset := this.offset
		if this.lastN > 0 {
			latestOffset, err := kfk.GetOffset(topic, partitionId, sarama.OffsetNewest)
			swallow(err)
			offset = latestOffset - this.lastN
			if offset < 0 {
				offset = sarama.OffsetOldest
			}
		}
		this.consumePartition(zkcluster, kfk, consumer, topic, partitionId, msgCh, offset)
	}

}

func (this *Peek) consumePartition(zkcluster *zk.ZkCluster, kfk sarama.Client, consumer sarama.Consumer,
	topic string, partitionId int32, msgCh chan *sarama.ConsumerMessage, offset int64) {
	p, err := consumer.ConsumePartition(topic, partitionId, offset)
	if err != nil {
		this.Ui.Error(fmt.Sprintf("%s %s/%d: offset=%d %v", zkcluster.Name(), topic, partitionId, offset, err))
		return
	}
	defer p.Close()

	n := int64(0)
	for {
		select {
		case <-this.quit:
			return

		case msg := <-p.Messages():
			msgCh <- msg

			n++
			if this.lastN > 0 && n >= this.lastN {
				return
			}
		}
	}
}

func (*Peek) Synopsis() string {
	return "Peek kafka cluster messages ongoing from any offset"
}

func (this *Peek) Help() string {
	help := fmt.Sprintf(`
Usage: %s peek [options]

    %s

Options:

    -z zone
      Default %s

    -c cluster

    -t topic pattern

    -p partition id
      -1 will peek all partitions of a topic

    -pretty
      Pretty print the json message body

    -col json column name
      Will json decode message and extract specified column value only

    -last n
      Peek the most recent N messages

    -offset message offset value
      -1 OffsetNewest, -2 OffsetOldest.
      You can specify your own offset.
      Default -1(OffsetNewest)

    -n count
      Limit how many messages to consume

    -d duration
      Limit how long to keep peeking
      e,g. -d 5m

    -body
      Only display message body

    -s
      Silence mode, only display statastics instead of message content

    -color
      Enable colorized output
`, this.Cmd, this.Synopsis(), ctx.ZkDefaultZone())
	return strings.TrimSpace(help)
}
<commit_msg>add exact matching of topic for 'gk peek'<commit_after>package command

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
	"github.com/funkygao/golib/gofmt"
	"github.com/funkygao/golib/signal"
)

var (
	stats *peekStats
)

type peekStats struct {
	MsgCountPerSecond metrics.Meter
	MsgBytesPerSecond metrics.Meter
}

func newPeekStats() *peekStats {
	this := &peekStats{
		MsgCountPerSecond: metrics.NewMeter(),
		MsgBytesPerSecond: metrics.NewMeter(),
	}

	metrics.Register("msg.count.per.second", this.MsgCountPerSecond)
	metrics.Register("msg.bytes.per.second", this.MsgBytesPerSecond)
	return this
}

func (this *peekStats) start() {
	metrics.Log(metrics.DefaultRegistry, time.Second*10,
		log.New(os.Stdout, "metrics: ", log.Lmicroseconds))
}

type Peek struct {
	Ui  cli.Ui
	Cmd string

	offset   int64
	lastN    int64 // peek the most recent N messages
	colorize bool
	limit    int
	quit     chan struct{}
	once     sync.Once
	column   string
	pretty   bool
	bodyOnly bool
}

func (this *Peek) Run(args []string) (exitCode int) {
	var (
		cluster      string
		zone         string
		topicPattern string
		topicName    string
		partitionId  int
		wait         time.Duration
		silence      bool
	)
	cmdFlags := flag.NewFlagSet("peek", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&cluster, "c", "", "")
	cmdFlags.StringVar(&topicPattern, "t", "", "")
	cmdFlags.StringVar(&topicName, "tt", "", "")
	cmdFlags.IntVar(&partitionId, "p", 0, "")
	cmdFlags.BoolVar(&this.colorize, "color", true, "")
	cmdFlags.Int64Var(&this.lastN, "last", -1, "")
	cmdFlags.BoolVar(&this.pretty, "pretty", false, "")
	cmdFlags.IntVar(&this.limit, "n", -1, "")
	cmdFlags.StringVar(&this.column, "col", "", "")
	cmdFlags.Int64Var(&this.offset, "offset", sarama.OffsetNewest, "")
	cmdFlags.BoolVar(&silence, "s", false, "")
	cmdFlags.DurationVar(&wait, "d", time.Hour, "")
	cmdFlags.BoolVar(&this.bodyOnly, "body", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if this.pretty {
		this.bodyOnly = true
	}

	this.quit = make(chan struct{})

	if silence {
		stats := newPeekStats()
		go stats.start()
	}

	zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
	msgChan := make(chan *sarama.ConsumerMessage, 20000) // msg aggerator channel
	if cluster == "" {
		zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
			this.consumeCluster(zkcluster, topicName, topicPattern, partitionId, msgChan)
		})
	} else {
		zkcluster := zkzone.NewCluster(cluster)
		this.consumeCluster(zkcluster, topicName, topicPattern, partitionId, msgChan)
	}

	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Printf("received signal: %s", strings.ToUpper(sig.String()))
		log.Println("quiting...")

		this.once.Do(func() {
			close(this.quit)
		})
	}, syscall.SIGINT, syscall.SIGTERM)

	var (
		startAt = time.Now()
		msg     *sarama.ConsumerMessage
		total   int
		bytesN  int64
	)

	var (
		j          map[string]interface{}
		prettyJSON bytes.Buffer
	)

LOOP:
	for {
		if time.Since(startAt) >= wait {
			this.Ui.Output(fmt.Sprintf("Total: %s msgs, %s, elapsed: %s",
				gofmt.Comma(int64(total)), gofmt.ByteSize(bytesN), time.Since(startAt)))
			elapsed := time.Since(startAt).Seconds()
			if elapsed > 1. {
				this.Ui.Output(fmt.Sprintf("Speed: %d/s", total/int(elapsed)))
				if total > 0 {
					this.Ui.Output(fmt.Sprintf("Size : %s/msg", gofmt.ByteSize(bytesN/int64(total))))
				}
			}

			return
		}

		select {
		case <-this.quit:
			this.Ui.Output(fmt.Sprintf("Total: %s msgs, %s, elapsed: %s",
				gofmt.Comma(int64(total)), gofmt.ByteSize(bytesN), time.Since(startAt)))
			elapsed := time.Since(startAt).Seconds()
			if elapsed > 1. {
				this.Ui.Output(fmt.Sprintf("Speed: %d/s", total/int(elapsed)))
				if total > 0 {
					this.Ui.Output(fmt.Sprintf("Size : %s/msg", gofmt.ByteSize(bytesN/int64(total))))
				}
			}

			return

		case <-time.After(time.Second):
			continue

		case msg = <-msgChan:
			if silence {
				stats.MsgCountPerSecond.Mark(1)
				stats.MsgBytesPerSecond.Mark(int64(len(msg.Value)))
			} else {
				if this.column != "" {
					if err := json.Unmarshal(msg.Value, &j); err != nil {
						this.Ui.Error(err.Error())
					} else {
						if this.bodyOnly {
							if this.pretty {
								if err = json.Indent(&prettyJSON, []byte(j[this.column].(string)), "", "    "); err != nil {
									fmt.Println(err.Error())
								} else {
									fmt.Println(string(prettyJSON.Bytes()))
								}
							} else {
								fmt.Println(j[this.column].(string))
							}
						} else if this.colorize {
							this.Ui.Output(fmt.Sprintf("%s/%d %s k:%s v:%s",
								color.Green(msg.Topic), msg.Partition,
								gofmt.Comma(msg.Offset), string(msg.Key), j[this.column].(string)))
						} else {
							// colored UI will have invisible chars output
							fmt.Println(fmt.Sprintf("%s/%d %s k:%s v:%s",
								msg.Topic, msg.Partition,
								gofmt.Comma(msg.Offset), string(msg.Key), j[this.column].(string)))
						}
					}

				} else {
					if this.bodyOnly {
						if this.pretty {
							json.Indent(&prettyJSON, msg.Value, "", "    ")
							fmt.Println(string(prettyJSON.Bytes()))
						} else {
							fmt.Println(string(msg.Value))
						}
					} else if this.colorize {
						this.Ui.Output(fmt.Sprintf("%s/%d %s k:%s, v:%s",
							color.Green(msg.Topic), msg.Partition,
							gofmt.Comma(msg.Offset), string(msg.Key), string(msg.Value)))
					} else {
						// colored UI will have invisible chars output
						fmt.Println(fmt.Sprintf("%s/%d %s k:%s, v:%s",
							msg.Topic, msg.Partition,
							gofmt.Comma(msg.Offset), string(msg.Key), string(msg.Value)))
					}
				}
			}

			total++
			bytesN += int64(len(msg.Value))

			if this.limit > 0 && total >= this.limit {
				break LOOP
			}
			if this.lastN > 0 && total >= int(this.lastN) {
				break LOOP
			}

		}
	}

	return
}

func (this *Peek) consumeCluster(zkcluster *zk.ZkCluster, topicName, topicPattern string,
	partitionId int, msgChan chan *sarama.ConsumerMessage) {
	brokerList := zkcluster.BrokerList()
	if len(brokerList) == 0 {
		return
	}
	kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
	if err != nil {
		this.Ui.Output(err.Error())
		return
	}
	//defer kfk.Close() // FIXME how to close it

	topics, err := kfk.Topics()
	if err != nil {
		this.Ui.Output(err.Error())
		return
	}

	for _, t := range topics {
		if topicName != "" && topicName != t {
			continue
		}
		if patternMatched(t, topicPattern) {
			go this.simpleConsumeTopic(zkcluster, kfk, t, int32(partitionId), msgChan)
		}
	}

}

func (this *Peek) simpleConsumeTopic(zkcluster *zk.ZkCluster, kfk sarama.Client, topic string, partitionId int32,
	msgCh chan *sarama.ConsumerMessage) {
	consumer, err := sarama.NewConsumerFromClient(kfk)
	if err != nil {
		panic(err)
	}
	defer consumer.Close()

	if partitionId == -1 {
		// all partitions
		partitions, err := kfk.Partitions(topic)
		if err != nil {
			panic(err)
		}

		for _, p := range partitions {
			offset := this.offset
			if this.lastN > 0 {
				latestOffset, err := kfk.GetOffset(topic, p, sarama.OffsetNewest)
				swallow(err)

				oldestOffset, err := kfk.GetOffset(topic, p, sarama.OffsetOldest)
				swallow(err)

				offset = latestOffset - this.lastN
				if offset < oldestOffset {
					offset = oldestOffset
				}

				if offset == 0 {
					// no message in store
					return
				}
			}

			go this.consumePartition(zkcluster, kfk, consumer, topic, p, msgCh, offset)
		}

	} else {
		offset := this.offset
		if this.lastN > 0 {
			latestOffset, err := kfk.GetOffset(topic, partitionId, sarama.OffsetNewest)
			swallow(err)
			offset = latestOffset - this.lastN
			if offset < 0 {
				offset = sarama.OffsetOldest
			}
		}
		this.consumePartition(zkcluster, kfk, consumer, topic, partitionId, msgCh, offset)
	}

}

func (this *Peek) consumePartition(zkcluster *zk.ZkCluster, kfk sarama.Client, consumer sarama.Consumer,
	topic string, partitionId int32, msgCh chan *sarama.ConsumerMessage, offset int64) {
	p, err := consumer.ConsumePartition(topic, partitionId, offset)
	if err != nil {
		this.Ui.Error(fmt.Sprintf("%s %s/%d: offset=%d %v", zkcluster.Name(), topic, partitionId, offset, err))
		return
	}
	defer p.Close()

	n := int64(0)
	for {
		select {
		case <-this.quit:
			return

		case msg := <-p.Messages():
			msgCh <- msg

			n++
			if this.lastN > 0 && n >= this.lastN {
				return
			}
		}
	}
}

func (*Peek) Synopsis() string {
	return "Peek kafka cluster messages ongoing from any offset"
}

func (this *Peek) Help() string {
	help := fmt.Sprintf(`
Usage: %s peek [options]

    %s

Options:

    -z zone
      Default %s

    -c cluster

    -t topic pattern

    -tt topic name
      Exact(instead of pattern) matching of topic name

    -p partition id
      -1 will peek all partitions of a topic

    -pretty
      Pretty print the json message body

    -col json column name
      Will json decode message and extract specified column value only

    -last n
      Peek the most recent N messages

    -offset message offset value
      -1 OffsetNewest, -2 OffsetOldest.
      You can specify your own offset.
      Default -1(OffsetNewest)

    -n count
      Limit how many messages to consume

    -d duration
      Limit how long to keep peeking
      e,g. -d 5m

    -body
      Only display message body

    -s
      Silence mode, only display statastics instead of message content

    -color
      Enable colorized output
`, this.Cmd, this.Synopsis(), ctx.ZkDefaultZone())
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gobs/args"
	"github.com/gobs/cmd"
	"github.com/gobs/httpclient"
	"github.com/gobs/jsonpath"
	"github.com/gobs/simplejson"

	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	completion_words = []string{}
	env              = map[string]string{}
)

func CompletionFunction(text, line string) (matches []string) {
	// for the "ls" command we let readline show real file names
	if strings.HasPrefix(line, "ls ") {
		return
	}

	// for all other commands, we pick from our list of completion words
	for _, w := range completion_words {
		if strings.HasPrefix(w, text) {
			matches = append(matches, w)
		}
	}

	return
}

func request(client *httpclient.HttpClient, method, params string, print bool) *httpclient.HttpResponse {
	env["error"] = ""
	env["body"] = ""

	options := []httpclient.RequestOption{client.Method(method)}
	args := args.ParseArgs(params)

	if len(args.Arguments) > 0 {
		options = append(options, client.Path(args.Arguments[0]))
	}

	if len(args.Arguments) > 1 {
		data := strings.Join(args.Arguments[1:], " ")
		options = append(options, client.Body(strings.NewReader(data)))
	}

	if len(args.Options) > 0 {
		options = append(options, client.StringParams(args.Options))
	}

	res, err := client.SendRequest(options...)
	if err == nil {
		err = res.ResponseError()
	}
	if err != nil {
		fmt.Println("ERROR:", err)
		env["error"] = err.Error()
	}

	body := res.Content()
	if len(body) > 0 && print {
		if strings.Contains(res.Header.Get("Content-Type"), "json") {
			jbody, err := simplejson.LoadBytes(body)
			if err != nil {
				fmt.Println(err)
			} else {
				printJson(jbody.Data())
			}
		} else {
			fmt.Println(string(body))
		}
	}

	env["body"] = string(body)
	return res
}

func headerName(s string) string {
	s = strings.ToLower(s)
	parts := strings.Split(s, "-")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[0:1]) + p[1:]
		}
	}
	return strings.Join(parts, "-")
}

func unquote(s string) string {
	if res, err := strconv.Unquote(strings.TrimSpace(s)); err == nil {
		return res
	}

	return s
}

func printJson(v interface{}) {
	fmt.Println(simplejson.MustDumpString(v, simplejson.Indent("  ")))
}

func main() {
	var interrupted bool
	var client = httpclient.NewHttpClient("")

	client.UserAgent = "httpclient/0.1"

	commander := &cmd.Cmd{
		HistoryFile: ".httpclient_history",
		Complete:    CompletionFunction,
		EnableShell: true,
		Interrupt:   func(sig os.Signal) bool { interrupted = true; return false },
	}

	commander.Init()

	commander.Vars = env
	commander.SetVar("print", true)

	commander.Add(cmd.Command{
		"base",
		`base [url]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := url.Parse(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.BaseURL = val
				commander.Prompt = fmt.Sprintf("%v> ", client.BaseURL)
				if !commander.GetBoolVar("print") {
					return
				}
			}

			fmt.Println("base", client.BaseURL)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"insecure",
		`insecure [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.AllowInsecure(val)
			}

			// assume if there is a transport, it's because we set AllowInsecure
			fmt.Println("insecure", client.GetTransport() != nil)

			return
		},
		nil})

	commander.Add(cmd.Command{
		"timeout",
		`timeout [duration]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := time.ParseDuration(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.SetTimeout(val)
			}

			fmt.Println("timeout", client.GetTimeout())
			return
		},
		nil})

	commander.Add(cmd.Command{
		"verbose",
		`verbose [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.Verbose = val
			}

			fmt.Println("Verbose", client.Verbose)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"agent",
		`agent user-agent-string`,
		func(line string) (stop bool) {
			if line != "" {
				client.UserAgent = line
			}

			fmt.Println("User-Agent:", client.UserAgent)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"header",
		`header [name [value]]`,
		func(line string) (stop bool) {
			if line == "" {
				if len(client.Headers) == 0 {
					fmt.Println("No headers")
				} else {
					fmt.Println("Headers:")
					for k, v := range client.Headers {
						fmt.Printf("  %v: %v\n", k, v)
					}
				}

				return
			}

			parts := args.GetArgsN(line, 2)
			name := headerName(parts[0])

			if len(parts) == 2 {
				client.Headers[name] = unquote(parts[1])
				if !commander.GetBoolVar("print") {
					return
				}
			}

			fmt.Printf("%v: %v\n", name, client.Headers[name])
			return
		},
		nil})

	commander.Add(cmd.Command{"head",
		`
                head [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			res := request(client, "head", line, false)
			if res != nil {
				printJson(res.Header)
			}
			return
		},
		nil})

	commander.Add(cmd.Command{"get",
		`
                get [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "get", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"post",
		`
                post [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "post", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"put",
		`
                put [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "put", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"delete",
		`
                delete [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "delete", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{
		"jsonpath",
		`jsonpath path {json}`,
		func(line string) (stop bool) {
			parts := args.GetArgsN(line, 2)
			if len(parts) != 2 {
				fmt.Println("use: jsonpath path {json}")
				env["error"] = "invalid-usage"
				return
			}

			path := parts[0]
			if !strings.HasPrefix(path, "$.") {
				path = "$." + path
			}

			jbody, err := simplejson.LoadString(parts[1])
			if err != nil {
				fmt.Println("json:", err)
				env["error"] = err.Error()
				return
			}

			jp := jsonpath.NewProcessor()
			if !jp.Parse(path) {
				env["error"] = fmt.Sprintf("failed to parse %q", path)
				return // syntax error
			}

			res := jp.Process(jbody)
			if commander.GetBoolVar("print") {
				printJson(res)
			}
			env["error"] = ""
			env["json"] = unquote(simplejson.MustDumpString(res))
			return
		},
		nil})

	commander.Add(cmd.Command{
		"format",
		`format object`,
		func(line string) (stop bool) {
			jbody, err := simplejson.LoadString(line)
			if err != nil {
				fmt.Println("json:", err)
				env["error"] = err.Error()
				return
			}

			printJson(jbody.Data())
			return
		},
		nil})
	commander.Add(cmd.Command{
		"exit",
		`exit script`,
		func(line string) (stop bool) {
			fmt.Println("goodbye!")
			return true
		},
		nil})

	commander.Commands["set"] = commander.Commands["var"]

	switch len(os.Args) {
	case 1: // program name only
		break

	case 2: // one arg - expect URL or @filename
		cmd := os.Args[1]
		if !strings.HasPrefix(cmd, "@") {
			cmd = "base " + cmd
		}

		commander.OneCmd(cmd)

	default:
		fmt.Println("usage:", os.Args[0], "[base-url]")
		return
	}

	commander.CmdLoop()
}
<commit_msg>Added options to jsonpath (-e -c) and shortened prompt<commit_after>package main

import (
	"github.com/gobs/args"
	"github.com/gobs/cmd"
	"github.com/gobs/httpclient"
	"github.com/gobs/jsonpath"
	"github.com/gobs/simplejson"

	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	completion_words = []string{}
	env              = map[string]string{}
)

func CompletionFunction(text, line string) (matches []string) {
	// for the "ls" command we let readline show real file names
	if strings.HasPrefix(line, "ls ") {
		return
	}

	// for all other commands, we pick from our list of completion words
	for _, w := range completion_words {
		if strings.HasPrefix(w, text) {
			matches = append(matches, w)
		}
	}

	return
}

func request(client *httpclient.HttpClient, method, params string, print bool) *httpclient.HttpResponse {
	env["error"] = ""
	env["body"] = ""

	options := []httpclient.RequestOption{client.Method(method)}
	args := args.ParseArgs(params)

	if len(args.Arguments) > 0 {
		options = append(options, client.Path(args.Arguments[0]))
	}

	if len(args.Arguments) > 1 {
		data := strings.Join(args.Arguments[1:], " ")
		options = append(options, client.Body(strings.NewReader(data)))
	}

	if len(args.Options) > 0 {
		options = append(options, client.StringParams(args.Options))
	}

	res, err := client.SendRequest(options...)
	if err == nil {
		err = res.ResponseError()
	}
	if err != nil {
		fmt.Println("ERROR:", err)
		env["error"] = err.Error()
	}

	body := res.Content()
	if len(body) > 0 && print {
		if strings.Contains(res.Header.Get("Content-Type"), "json") {
			jbody, err := simplejson.LoadBytes(body)
			if err != nil {
				fmt.Println(err)
			} else {
				printJson(jbody.Data())
			}
		} else {
			fmt.Println(string(body))
		}
	}

	env["body"] = string(body)
	return res
}

func headerName(s string) string {
	s = strings.ToLower(s)
	parts := strings.Split(s, "-")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[0:1]) + p[1:]
		}
	}
	return strings.Join(parts, "-")
}

func unquote(s string) string {
	if res, err := strconv.Unquote(strings.TrimSpace(s)); err == nil {
		return res
	}

	return s
}

func printJson(v interface{}) {
	fmt.Println(simplejson.MustDumpString(v, simplejson.Indent("  ")))
}

func main() {
	var interrupted bool
	var client = httpclient.NewHttpClient("")

	client.UserAgent = "httpclient/0.1"

	commander := &cmd.Cmd{
		HistoryFile: ".httpclient_history",
		Complete:    CompletionFunction,
		EnableShell: true,
		Interrupt:   func(sig os.Signal) bool { interrupted = true; return false },
	}

	commander.Init()

	commander.Vars = env
	commander.SetVar("print", true)

	commander.Add(cmd.Command{
		"base",
		`base [url]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := url.Parse(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.BaseURL = val
				commander.SetPrompt(fmt.Sprintf("%v> ", client.BaseURL), 40)
				if !commander.GetBoolVar("print") {
					return
				}
			}

			fmt.Println("base", client.BaseURL)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"insecure",
		`insecure [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.AllowInsecure(val)
			}

			// assume if there is a transport, it's because we set AllowInsecure
			fmt.Println("insecure", client.GetTransport() != nil)

			return
		},
		nil})

	commander.Add(cmd.Command{
		"timeout",
		`timeout [duration]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := time.ParseDuration(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.SetTimeout(val)
			}

			fmt.Println("timeout", client.GetTimeout())
			return
		},
		nil})

	commander.Add(cmd.Command{
		"verbose",
		`verbose [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.Verbose = val
			}

			fmt.Println("Verbose", client.Verbose)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"agent",
		`agent user-agent-string`,
		func(line string) (stop bool) {
			if line != "" {
				client.UserAgent = line
			}

			fmt.Println("User-Agent:", client.UserAgent)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"header",
		`header [name [value]]`,
		func(line string) (stop bool) {
			if line == "" {
				if len(client.Headers) == 0 {
					fmt.Println("No headers")
				} else {
					fmt.Println("Headers:")
					for k, v := range client.Headers {
						fmt.Printf("  %v: %v\n", k, v)
					}
				}

				return
			}

			parts := args.GetArgsN(line, 2)
			name := headerName(parts[0])

			if len(parts) == 2 {
				client.Headers[name] = unquote(parts[1])
				if !commander.GetBoolVar("print") {
					return
				}
			}

			fmt.Printf("%v: %v\n", name, client.Headers[name])
			return
		},
		nil})

	commander.Add(cmd.Command{"head",
		`
                head [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			res := request(client, "head", line, false)
			if res != nil {
				printJson(res.Header)
			}
			return
		},
		nil})

	commander.Add(cmd.Command{"get",
		`
                get [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "get", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"post",
		`
                post [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "post", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"put",
		`
                put [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "put", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"delete",
		`
                delete [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "delete", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{
		"jsonpath",
		`jsonpath [-e] [-c] path {json}`,
		func(line string) (stop bool) {
			var joptions jsonpath.ProcessOptions

			options, line := args.GetOptions(line)
			for _, o := range options {
				if o == "-e" || o == "--enhanced" {
					joptions |= jsonpath.Enhanced
				} else if o == "-c" || o == "--collapse" {
					joptions |= jsonpath.Collapse
				} else {
					line = "" // to force an error
					break
				}
			}

			parts := args.GetArgsN(line, 2)
			if len(parts) != 2 {
				fmt.Println("use: jsonpath [-e|--enhanced] path {json}")
				env["error"] = "invalid-usage"
				return
			}

			path := parts[0]
			if !strings.HasPrefix(path, "$.") {
				path = "$." + path
			}

			jbody, err := simplejson.LoadString(parts[1])
			if err != nil {
				fmt.Println("json:", err)
				env["error"] = err.Error()
				return
			}

			jp := jsonpath.NewProcessor()
			if !jp.Parse(path) {
				env["error"] = fmt.Sprintf("failed to parse %q", path)
				return // syntax error
			}

			res := jp.Process(jbody, joptions)
			if commander.GetBoolVar("print") {
				printJson(res)
			}
			env["error"] = ""
			env["json"] = unquote(simplejson.MustDumpString(res))
			return
		},
		nil})

	commander.Add(cmd.Command{
		"format",
		`format object`,
		func(line string) (stop bool) {
			jbody, err := simplejson.LoadString(line)
			if err != nil {
				fmt.Println("json:", err)
				env["error"] = err.Error()
				return
			}

			printJson(jbody.Data())
			return
		},
		nil})
	commander.Add(cmd.Command{
		"exit",
		`exit script`,
		func(line string) (stop bool) {
			fmt.Println("goodbye!")
			return true
		},
		nil})

	commander.Commands["set"] = commander.Commands["var"]

	switch len(os.Args) {
	case 1: // program name only
		break

	case 2: // one arg - expect URL or @filename
		cmd := os.Args[1]
		if !strings.HasPrefix(cmd, "@") {
			cmd = "base " + cmd
		}

		commander.OneCmd(cmd)

	default:
		fmt.Println("usage:", os.Args[0], "[base-url]")
		return
	}

	commander.CmdLoop()
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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.
*/
// The kubelet binary is responsible for maintaining a set of containers on a particular host VM.
// It sync's data from both configuration file as well as from a quorum of etcd servers.
// It then queries Docker to see what is currently running.  It synchronizes the configuration data,
// with the running set of containers by starting or stopping Docker containers.
package main

import (
	"flag"
	"log"
	"math/rand"
	"os"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	"github.com/coreos/go-etcd/etcd"
	"github.com/fsouza/go-dockerclient"
)

var (
	file               = flag.String("config", "", "Path to the config file")
	etcd_servers       = flag.String("etcd_servers", "", "Url of etcd servers in the cluster")
	syncFrequency      = flag.Duration("sync_frequency", 10*time.Second, "Max seconds between synchronizing running containers and config")
	fileCheckFrequency = flag.Duration("file_check_frequency", 20*time.Second, "Seconds between checking file for new data")
	httpCheckFrequency = flag.Duration("http_check_frequency", 20*time.Second, "Seconds between checking http for new data")
	manifest_url       = flag.String("manifest_url", "", "URL for accessing the container manifest")
	address            = flag.String("address", "127.0.0.1", "The address for the info server to serve on")
	port               = flag.Uint("port", 10250, "The port for the info server to serve on")
)

const dockerBinary = "/usr/bin/docker"

func main() {
	flag.Parse()
	rand.Seed(time.Now().UTC().UnixNano())

	// Set up logger for etcd client
	etcd.SetLogger(log.New(os.Stderr, "etcd ", log.LstdFlags))

	endpoint := "unix:///var/run/docker.sock"
	dockerClient, err := docker.NewClient(endpoint)
	if err != nil {
		log.Fatal("Couldn't connnect to docker.")
	}

	my_kubelet := kubelet.Kubelet{
		DockerClient:       dockerClient,
		FileCheckFrequency: *fileCheckFrequency,
		SyncFrequency:      *syncFrequency,
		HTTPCheckFrequency: *httpCheckFrequency,
	}
	my_kubelet.RunKubelet(*file, *manifest_url, *etcd_servers, *address, *port)
}
<commit_msg>Change flag.Duration descriptions to be unit-free.<commit_after>/*
Copyright 2014 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.
*/
// The kubelet binary is responsible for maintaining a set of containers on a particular host VM.
// It sync's data from both configuration file as well as from a quorum of etcd servers.
// It then queries Docker to see what is currently running.  It synchronizes the configuration data,
// with the running set of containers by starting or stopping Docker containers.
package main

import (
	"flag"
	"log"
	"math/rand"
	"os"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	"github.com/coreos/go-etcd/etcd"
	"github.com/fsouza/go-dockerclient"
)

var (
	file               = flag.String("config", "", "Path to the config file")
	etcd_servers       = flag.String("etcd_servers", "", "Url of etcd servers in the cluster")
	syncFrequency      = flag.Duration("sync_frequency", 10*time.Second, "Max period between synchronizing running containers and config")
	fileCheckFrequency = flag.Duration("file_check_frequency", 20*time.Second, "Duration between checking file for new data")
	httpCheckFrequency = flag.Duration("http_check_frequency", 20*time.Second, "Duration between checking http for new data")
	manifest_url       = flag.String("manifest_url", "", "URL for accessing the container manifest")
	address            = flag.String("address", "127.0.0.1", "The address for the info server to serve on")
	port               = flag.Uint("port", 10250, "The port for the info server to serve on")
)

const dockerBinary = "/usr/bin/docker"

func main() {
	flag.Parse()
	rand.Seed(time.Now().UTC().UnixNano())

	// Set up logger for etcd client
	etcd.SetLogger(log.New(os.Stderr, "etcd ", log.LstdFlags))

	endpoint := "unix:///var/run/docker.sock"
	dockerClient, err := docker.NewClient(endpoint)
	if err != nil {
		log.Fatal("Couldn't connnect to docker.")
	}

	my_kubelet := kubelet.Kubelet{
		DockerClient:       dockerClient,
		FileCheckFrequency: *fileCheckFrequency,
		SyncFrequency:      *syncFrequency,
		HTTPCheckFrequency: *httpCheckFrequency,
	}
	my_kubelet.RunKubelet(*file, *manifest_url, *etcd_servers, *address, *port)
}
<|endoftext|>