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

package runtime

import "unsafe"

func runtime_init()
func main_init()
func main_main()

// The main goroutine.
func main() {
	g := getg()

	// Racectx of m0->g0 is used only as the parent of the main goroutine.
	// It must not be used for anything else.
	g.m.g0.racectx = 0

	// Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
	// Using decimal instead of binary GB and MB because
	// they look nicer in the stack overflow failure message.
	if ptrSize == 8 {
		maxstacksize = 1000000000
	} else {
		maxstacksize = 250000000
	}

	systemstack(newsysmon)

	// Lock the main goroutine onto this, the main OS thread,
	// during initialization.  Most programs won't care, but a few
	// do require certain calls to be made by the main thread.
	// Those can arrange for main.main to run in the main thread
	// by calling runtime.LockOSThread during initialization
	// to preserve the lock.
	lockOSThread()

	if g.m != &m0 {
		gothrow("runtime.main not on m0")
	}

	runtime_init() // must be before defer

	// Defer unlock so that runtime.Goexit during init does the unlock too.
	needUnlock := true
	defer func() {
		if needUnlock {
			unlockOSThread()
		}
	}()

	memstats.enablegc = true // now that runtime is initialized, GC is okay

	if iscgo {
		if _cgo_thread_start == nil {
			gothrow("_cgo_thread_start missing")
		}
		if _cgo_malloc == nil {
			gothrow("_cgo_malloc missing")
		}
		if _cgo_free == nil {
			gothrow("_cgo_free missing")
		}
		if _cgo_setenv == nil {
			gothrow("_cgo_setenv missing")
		}
		if _cgo_unsetenv == nil {
			gothrow("_cgo_unsetenv missing")
		}
	}

	main_init()

	needUnlock = false
	unlockOSThread()

	main_main()
	if raceenabled {
		racefini()
	}

	// Make racy client program work: if panicking on
	// another goroutine at the same time as main returns,
	// let the other goroutine finish printing the panic trace.
	// Once it does, it will exit. See issue 3934.
	if panicking != 0 {
		gopark(nil, nil, "panicwait")
	}

	exit(0)
	for {
		var x *int32
		*x = 0
	}
}

// start forcegc helper goroutine
func init() {
	go forcegchelper()
}

func forcegchelper() {
	forcegc.g = getg()
	forcegc.g.issystem = true
	for {
		lock(&forcegc.lock)
		if forcegc.idle != 0 {
			gothrow("forcegc: phase error")
		}
		atomicstore(&forcegc.idle, 1)
		goparkunlock(&forcegc.lock, "force gc (idle)")
		// this goroutine is explicitly resumed by sysmon
		if debug.gctrace > 0 {
			println("GC forced")
		}
		gogc(1)
	}
}

//go:nosplit

// Gosched yields the processor, allowing other goroutines to run.  It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched() {
	mcall(gosched_m)
}

// Puts the current goroutine into a waiting state and calls unlockf.
// If unlockf returns false, the goroutine is resumed.
func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string) {
	mp := acquirem()
	gp := mp.curg
	status := readgstatus(gp)
	if status != _Grunning && status != _Gscanrunning {
		gothrow("gopark: bad g status")
	}
	mp.waitlock = lock
	mp.waitunlockf = *(*unsafe.Pointer)(unsafe.Pointer(&unlockf))
	gp.waitreason = reason
	releasem(mp)
	// can't do anything that might move the G between Ms here.
	mcall(park_m)
}

// Puts the current goroutine into a waiting state and unlocks the lock.
// The goroutine can be made runnable again by calling goready(gp).
func goparkunlock(lock *mutex, reason string) {
	gopark(parkunlock_c, unsafe.Pointer(lock), reason)
}

func goready(gp *g) {
	systemstack(func() {
		ready(gp)
	})
}

//go:nosplit
func acquireSudog() *sudog {
	c := gomcache()
	s := c.sudogcache
	if s != nil {
		if s.elem != nil {
			gothrow("acquireSudog: found s.elem != nil in cache")
		}
		c.sudogcache = s.next
		s.next = nil
		return s
	}

	// Delicate dance: the semaphore implementation calls
	// acquireSudog, acquireSudog calls new(sudog),
	// new calls malloc, malloc can call the garbage collector,
	// and the garbage collector calls the semaphore implementation
	// in stoptheworld.
	// Break the cycle by doing acquirem/releasem around new(sudog).
	// The acquirem/releasem increments m.locks during new(sudog),
	// which keeps the garbage collector from being invoked.
	mp := acquirem()
	p := new(sudog)
	releasem(mp)
	return p
}

//go:nosplit
func releaseSudog(s *sudog) {
	if s.elem != nil {
		gothrow("runtime: sudog with non-nil elem")
	}
	if s.selectdone != nil {
		gothrow("runtime: sudog with non-nil selectdone")
	}
	if s.next != nil {
		gothrow("runtime: sudog with non-nil next")
	}
	if s.prev != nil {
		gothrow("runtime: sudog with non-nil prev")
	}
	if s.waitlink != nil {
		gothrow("runtime: sudog with non-nil waitlink")
	}
	gp := getg()
	if gp.param != nil {
		gothrow("runtime: releaseSudog with non-nil gp.param")
	}
	c := gomcache()
	s.next = c.sudogcache
	c.sudogcache = s
}

// funcPC returns the entry PC of the function f.
// It assumes that f is a func value. Otherwise the behavior is undefined.
//go:nosplit
func funcPC(f interface{}) uintptr {
	return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
}

// called from assembly
func badmcall(fn func(*g)) {
	gothrow("runtime: mcall called on m->g0 stack")
}

func badmcall2(fn func(*g)) {
	gothrow("runtime: mcall function returned")
}

func badreflectcall() {
	panic("runtime: arg size to reflect.call more than 1GB")
}

func lockedOSThread() bool {
	gp := getg()
	return gp.lockedm != nil && gp.m.lockedg != nil
}

func newP() *p {
	return new(p)
}

func newM() *m {
	return new(m)
}

func newG() *g {
	return new(g)
}

var (
	allgs    []*g
	allglock mutex
)

func allgadd(gp *g) {
	if readgstatus(gp) == _Gidle {
		gothrow("allgadd: bad status Gidle")
	}

	lock(&allglock)
	allgs = append(allgs, gp)
	allg = &allgs[0]
	allglen = uintptr(len(allgs))
	unlock(&allglock)
}
<commit_msg>[dev.cc] runtime: windows does not use _cgo_setenv and _cgo_unsetenv<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import "unsafe"

func runtime_init()
func main_init()
func main_main()

// The main goroutine.
func main() {
	g := getg()

	// Racectx of m0->g0 is used only as the parent of the main goroutine.
	// It must not be used for anything else.
	g.m.g0.racectx = 0

	// Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
	// Using decimal instead of binary GB and MB because
	// they look nicer in the stack overflow failure message.
	if ptrSize == 8 {
		maxstacksize = 1000000000
	} else {
		maxstacksize = 250000000
	}

	systemstack(newsysmon)

	// Lock the main goroutine onto this, the main OS thread,
	// during initialization.  Most programs won't care, but a few
	// do require certain calls to be made by the main thread.
	// Those can arrange for main.main to run in the main thread
	// by calling runtime.LockOSThread during initialization
	// to preserve the lock.
	lockOSThread()

	if g.m != &m0 {
		gothrow("runtime.main not on m0")
	}

	runtime_init() // must be before defer

	// Defer unlock so that runtime.Goexit during init does the unlock too.
	needUnlock := true
	defer func() {
		if needUnlock {
			unlockOSThread()
		}
	}()

	memstats.enablegc = true // now that runtime is initialized, GC is okay

	if iscgo {
		if _cgo_thread_start == nil {
			gothrow("_cgo_thread_start missing")
		}
		if _cgo_malloc == nil {
			gothrow("_cgo_malloc missing")
		}
		if _cgo_free == nil {
			gothrow("_cgo_free missing")
		}
		if GOOS != "windows" {
			if _cgo_setenv == nil {
				gothrow("_cgo_setenv missing")
			}
			if _cgo_unsetenv == nil {
				gothrow("_cgo_unsetenv missing")
			}
		}
	}

	main_init()

	needUnlock = false
	unlockOSThread()

	main_main()
	if raceenabled {
		racefini()
	}

	// Make racy client program work: if panicking on
	// another goroutine at the same time as main returns,
	// let the other goroutine finish printing the panic trace.
	// Once it does, it will exit. See issue 3934.
	if panicking != 0 {
		gopark(nil, nil, "panicwait")
	}

	exit(0)
	for {
		var x *int32
		*x = 0
	}
}

// start forcegc helper goroutine
func init() {
	go forcegchelper()
}

func forcegchelper() {
	forcegc.g = getg()
	forcegc.g.issystem = true
	for {
		lock(&forcegc.lock)
		if forcegc.idle != 0 {
			gothrow("forcegc: phase error")
		}
		atomicstore(&forcegc.idle, 1)
		goparkunlock(&forcegc.lock, "force gc (idle)")
		// this goroutine is explicitly resumed by sysmon
		if debug.gctrace > 0 {
			println("GC forced")
		}
		gogc(1)
	}
}

//go:nosplit

// Gosched yields the processor, allowing other goroutines to run.  It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched() {
	mcall(gosched_m)
}

// Puts the current goroutine into a waiting state and calls unlockf.
// If unlockf returns false, the goroutine is resumed.
func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string) {
	mp := acquirem()
	gp := mp.curg
	status := readgstatus(gp)
	if status != _Grunning && status != _Gscanrunning {
		gothrow("gopark: bad g status")
	}
	mp.waitlock = lock
	mp.waitunlockf = *(*unsafe.Pointer)(unsafe.Pointer(&unlockf))
	gp.waitreason = reason
	releasem(mp)
	// can't do anything that might move the G between Ms here.
	mcall(park_m)
}

// Puts the current goroutine into a waiting state and unlocks the lock.
// The goroutine can be made runnable again by calling goready(gp).
func goparkunlock(lock *mutex, reason string) {
	gopark(parkunlock_c, unsafe.Pointer(lock), reason)
}

func goready(gp *g) {
	systemstack(func() {
		ready(gp)
	})
}

//go:nosplit
func acquireSudog() *sudog {
	c := gomcache()
	s := c.sudogcache
	if s != nil {
		if s.elem != nil {
			gothrow("acquireSudog: found s.elem != nil in cache")
		}
		c.sudogcache = s.next
		s.next = nil
		return s
	}

	// Delicate dance: the semaphore implementation calls
	// acquireSudog, acquireSudog calls new(sudog),
	// new calls malloc, malloc can call the garbage collector,
	// and the garbage collector calls the semaphore implementation
	// in stoptheworld.
	// Break the cycle by doing acquirem/releasem around new(sudog).
	// The acquirem/releasem increments m.locks during new(sudog),
	// which keeps the garbage collector from being invoked.
	mp := acquirem()
	p := new(sudog)
	releasem(mp)
	return p
}

//go:nosplit
func releaseSudog(s *sudog) {
	if s.elem != nil {
		gothrow("runtime: sudog with non-nil elem")
	}
	if s.selectdone != nil {
		gothrow("runtime: sudog with non-nil selectdone")
	}
	if s.next != nil {
		gothrow("runtime: sudog with non-nil next")
	}
	if s.prev != nil {
		gothrow("runtime: sudog with non-nil prev")
	}
	if s.waitlink != nil {
		gothrow("runtime: sudog with non-nil waitlink")
	}
	gp := getg()
	if gp.param != nil {
		gothrow("runtime: releaseSudog with non-nil gp.param")
	}
	c := gomcache()
	s.next = c.sudogcache
	c.sudogcache = s
}

// funcPC returns the entry PC of the function f.
// It assumes that f is a func value. Otherwise the behavior is undefined.
//go:nosplit
func funcPC(f interface{}) uintptr {
	return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
}

// called from assembly
func badmcall(fn func(*g)) {
	gothrow("runtime: mcall called on m->g0 stack")
}

func badmcall2(fn func(*g)) {
	gothrow("runtime: mcall function returned")
}

func badreflectcall() {
	panic("runtime: arg size to reflect.call more than 1GB")
}

func lockedOSThread() bool {
	gp := getg()
	return gp.lockedm != nil && gp.m.lockedg != nil
}

func newP() *p {
	return new(p)
}

func newM() *m {
	return new(m)
}

func newG() *g {
	return new(g)
}

var (
	allgs    []*g
	allglock mutex
)

func allgadd(gp *g) {
	if readgstatus(gp) == _Gidle {
		gothrow("allgadd: bad status Gidle")
	}

	lock(&allglock)
	allgs = append(allgs, gp)
	allg = &allgs[0]
	allglen = uintptr(len(allgs))
	unlock(&allglock)
}
<|endoftext|>


<commit_before>package service

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/s3"
)

const (
	minStorageInterval = time.Second * 120
	maxStorageInterval = time.Second * 240

	expireTimeout = time.Minute * 15
)

type bytesReadCloser struct {
	*bytes.Reader
}

func (brc bytesReadCloser) Read(b []byte) (int, error) {
	return brc.Reader.Read(b)
}

func (bytesReadCloser) Close() (err error) {
	return
}

func randomStorageInterval() time.Duration {
	return randomDuration(minStorageInterval, maxStorageInterval)
}

func initStorage(local *localNode, remotes *remoteNodes, notify <-chan struct{}, reply chan<- []*net.UDPAddr, credData []byte, region, bucket, prefix string, dryRun bool, log *Log) (err error) {
	if prefix != "" && !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	localKey := prefix + local.ipAddr

	creds, err := parseCredentials(credData)
	if err != nil {
		return
	}

	var client *s3.S3

	if !dryRun {
		client = s3.New(&aws.Config{
			Credentials: creds,
			Region:      region,
		})
	}

	if err = updateStorage(local, client, bucket, localKey, log); err != nil {
		return
	}

	if err = scanStorage(local, remotes, reply, client, bucket, prefix, log); err != nil {
		return
	}

	go storageLoop(local, remotes, notify, reply, client, bucket, prefix, localKey, log)

	return
}

func storageLoop(local *localNode, remotes *remoteNodes, notify <-chan struct{}, reply chan<- []*net.UDPAddr, client *s3.S3, bucket, prefix, localKey string, log *Log) {
	timer := time.NewTimer(randomStorageInterval())

	for {
		var scan bool

		select {
		case <-notify:
			scan = false

		case <-timer.C:
			timer.Reset(randomStorageInterval())
			scan = true
		}

		if err := updateStorage(local, client, bucket, localKey, log); err != nil {
			log.Error(err)
		}

		if scan {
			if err := scanStorage(local, remotes, reply, client, bucket, prefix, log); err != nil {
				log.Error(err)
			}
		}
	}
}

func updateStorage(local *localNode, client *s3.S3, bucket, key string, log *Log) (err error) {
	log.Debug("updating S3")

	data, err := local.marshalForStorage()
	if err != nil {
		panic(err)
	}

	err = putObject(client, bucket, key, data, "application/json")
	return
}

func scanStorage(local *localNode, remotes *remoteNodes, reply chan<- []*net.UDPAddr, client *s3.S3, bucket, prefix string, log *Log) (err error) {
	log.Debug("scanning S3")

	objects := listObjects(client, bucket, prefix, log)
	if objects == nil {
		return
	}

	var loadKeys []*string
	var deleteKeys []*string

	expireThreshold := time.Now().Add(-expireTimeout)

	for object := range objects {
		ipAddr := (*object.Key)[len(prefix):]
		if ipAddr == "" || ipAddr == local.ipAddr {
			continue
		}

		if ip := net.ParseIP(ipAddr); ip == nil {
			log.Errorf("bad S3 key: %s", *object.Key)
			continue
		} else if !ip.IsGlobalUnicast() {
			log.Errorf("bad IP address in S3: %s", *object.Key)
			continue
		}

		if object.LastModified.After(expireThreshold) {
			if remotes.updatable(ipAddr, *object.LastModified) {
				loadKeys = append(loadKeys, object.Key)
			}
		} else {
			deleteKeys = append(deleteKeys, object.Key)
		}
	}

	for _, key := range deleteKeys {
		ipAddr := (*key)[len(prefix):]

		log.Infof("deleting %s from S3", ipAddr)

		if err := deleteObject(client, bucket, key); err != nil {
			log.Errorf("S3 DeleteObject: %s", err)
		}
	}

	var newAddrs []*net.UDPAddr

	for _, key := range loadKeys {
		ipAddr := (*key)[len(prefix):]

		log.Debugf("loading %s from S3", ipAddr)

		if output, err := getObject(client, bucket, key); err == nil {
			node := new(Node)
			err := json.NewDecoder(output.Body).Decode(node)
			output.Body.Close()
			if err != nil {
				log.Errorf("S3: %s: %s", ipAddr, err)
				continue
			}

			node.IPAddr = ipAddr
			node.TimeNs = output.LastModified.UnixNano()

			if newAddr := remotes.update(node, local, log); newAddr != nil {
				newAddrs = append(newAddrs, newAddr)
			}
		} else {
			log.Errorf("S3 GetObject: %s", err)
		}
	}

	if len(newAddrs) > 0 {
		reply <- newAddrs
	}

	remotes.expire(expireThreshold, local, log)

	return
}

func parseCredentials(data []byte) (creds aws.CredentialsProvider, err error) {
	var accessKey string
	var secretKey string

	if data != nil {
		fields := strings.Fields(strings.TrimSpace(string(data)))
		if len(fields) != 2 {
			err = errors.New("bad AWS credentials file format")
			return
		}

		accessKey = fields[0]
		secretKey = fields[1]
	}

	creds = aws.DetectCreds(accessKey, secretKey, "")
	return
}

func putObject(client *s3.S3, bucket string, key string, body []byte, contentType string) (err error) {
	contentLength := int64(len(body))

	request := &s3.PutObjectInput{
		Body:          bytesReadCloser{bytes.NewReader(body)},
		Bucket:        &bucket,
		ContentLength: &contentLength,
		ContentType:   &contentType,
		Key:           &key,
	}

	if client == nil {
		return
	}

	if _, err = client.PutObject(request); err != nil {
		err = fmt.Errorf("S3 PutObject: %s", err)
	}
	return
}

func getObject(client *s3.S3, bucket string, key *string) (output *s3.GetObjectOutput, err error) {
	request := &s3.GetObjectInput{
		Bucket: &bucket,
		Key:    key,
	}

	return client.GetObject(request)
}

func deleteObject(client *s3.S3, bucket string, key *string) (err error) {
	request := &s3.DeleteObjectInput{
		Bucket: &bucket,
		Key:    key,
	}

	_, err = client.DeleteObject(request)
	return
}

func listObjects(client *s3.S3, bucket, prefix string, log *Log) (channel chan *s3.Object) {
	request := &s3.ListObjectsInput{
		Bucket: &bucket,
		Prefix: &prefix,
	}

	channel = make(chan *s3.Object)

	if client == nil {
		close(channel)
		return
	}

	output, err := client.ListObjects(request)
	if err != nil {
		log.Errorf("S3 ListObjects: %s", err)
		return
	}

	go func() {
		defer close(channel)

		for {
			for i := range output.Contents {
				object := output.Contents[i]
				channel <- object
				request.Marker = object.Key
			}

			if !*output.IsTruncated {
				break
			}

			if output, err = client.ListObjects(request); err != nil {
				log.Errorf("S3 ListObjects: %s", err)
				break
			}
		}
	}()

	return
}
<commit_msg>service: fix S3 listing error handling<commit_after>package service

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/s3"
)

const (
	minStorageInterval = time.Second * 120
	maxStorageInterval = time.Second * 240

	expireTimeout = time.Minute * 15
)

type bytesReadCloser struct {
	*bytes.Reader
}

func (brc bytesReadCloser) Read(b []byte) (int, error) {
	return brc.Reader.Read(b)
}

func (bytesReadCloser) Close() (err error) {
	return
}

func randomStorageInterval() time.Duration {
	return randomDuration(minStorageInterval, maxStorageInterval)
}

func initStorage(local *localNode, remotes *remoteNodes, notify <-chan struct{}, reply chan<- []*net.UDPAddr, credData []byte, region, bucket, prefix string, dryRun bool, log *Log) (err error) {
	if prefix != "" && !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	localKey := prefix + local.ipAddr

	creds, err := parseCredentials(credData)
	if err != nil {
		return
	}

	var client *s3.S3

	if !dryRun {
		client = s3.New(&aws.Config{
			Credentials: creds,
			Region:      region,
		})
	}

	if err = updateStorage(local, client, bucket, localKey, log); err != nil {
		return
	}

	if err = scanStorage(local, remotes, reply, client, bucket, prefix, log); err != nil {
		return
	}

	go storageLoop(local, remotes, notify, reply, client, bucket, prefix, localKey, log)

	return
}

func storageLoop(local *localNode, remotes *remoteNodes, notify <-chan struct{}, reply chan<- []*net.UDPAddr, client *s3.S3, bucket, prefix, localKey string, log *Log) {
	timer := time.NewTimer(randomStorageInterval())

	for {
		var scan bool

		select {
		case <-notify:
			scan = false

		case <-timer.C:
			timer.Reset(randomStorageInterval())
			scan = true
		}

		if err := updateStorage(local, client, bucket, localKey, log); err != nil {
			log.Error(err)
		}

		if scan {
			if err := scanStorage(local, remotes, reply, client, bucket, prefix, log); err != nil {
				log.Error(err)
			}
		}
	}
}

func updateStorage(local *localNode, client *s3.S3, bucket, key string, log *Log) (err error) {
	log.Debug("updating S3")

	data, err := local.marshalForStorage()
	if err != nil {
		panic(err)
	}

	err = putObject(client, bucket, key, data, "application/json")
	return
}

func scanStorage(local *localNode, remotes *remoteNodes, reply chan<- []*net.UDPAddr, client *s3.S3, bucket, prefix string, log *Log) (err error) {
	log.Debug("scanning S3")

	objects, err := listObjects(client, bucket, prefix, log)
	if err != nil {
		return
	}

	var loadKeys []*string
	var deleteKeys []*string

	expireThreshold := time.Now().Add(-expireTimeout)

	for object := range objects {
		ipAddr := (*object.Key)[len(prefix):]
		if ipAddr == "" || ipAddr == local.ipAddr {
			continue
		}

		if ip := net.ParseIP(ipAddr); ip == nil {
			log.Errorf("bad S3 key: %s", *object.Key)
			continue
		} else if !ip.IsGlobalUnicast() {
			log.Errorf("bad IP address in S3: %s", *object.Key)
			continue
		}

		if object.LastModified.After(expireThreshold) {
			if remotes.updatable(ipAddr, *object.LastModified) {
				loadKeys = append(loadKeys, object.Key)
			}
		} else {
			deleteKeys = append(deleteKeys, object.Key)
		}
	}

	for _, key := range deleteKeys {
		ipAddr := (*key)[len(prefix):]

		log.Infof("deleting %s from S3", ipAddr)

		if err := deleteObject(client, bucket, key); err != nil {
			log.Errorf("S3 DeleteObject: %s", err)
		}
	}

	var newAddrs []*net.UDPAddr

	for _, key := range loadKeys {
		ipAddr := (*key)[len(prefix):]

		log.Debugf("loading %s from S3", ipAddr)

		if output, err := getObject(client, bucket, key); err == nil {
			node := new(Node)
			err := json.NewDecoder(output.Body).Decode(node)
			output.Body.Close()
			if err != nil {
				log.Errorf("S3: %s: %s", ipAddr, err)
				continue
			}

			node.IPAddr = ipAddr
			node.TimeNs = output.LastModified.UnixNano()

			if newAddr := remotes.update(node, local, log); newAddr != nil {
				newAddrs = append(newAddrs, newAddr)
			}
		} else {
			log.Errorf("S3 GetObject: %s", err)
		}
	}

	if len(newAddrs) > 0 {
		reply <- newAddrs
	}

	remotes.expire(expireThreshold, local, log)

	return
}

func parseCredentials(data []byte) (creds aws.CredentialsProvider, err error) {
	var accessKey string
	var secretKey string

	if data != nil {
		fields := strings.Fields(strings.TrimSpace(string(data)))
		if len(fields) != 2 {
			err = errors.New("bad AWS credentials file format")
			return
		}

		accessKey = fields[0]
		secretKey = fields[1]
	}

	creds = aws.DetectCreds(accessKey, secretKey, "")
	return
}

func putObject(client *s3.S3, bucket string, key string, body []byte, contentType string) (err error) {
	contentLength := int64(len(body))

	request := &s3.PutObjectInput{
		Body:          bytesReadCloser{bytes.NewReader(body)},
		Bucket:        &bucket,
		ContentLength: &contentLength,
		ContentType:   &contentType,
		Key:           &key,
	}

	if client == nil {
		return
	}

	if _, err = client.PutObject(request); err != nil {
		err = fmt.Errorf("S3 PutObject: %s", err)
	}
	return
}

func getObject(client *s3.S3, bucket string, key *string) (output *s3.GetObjectOutput, err error) {
	request := &s3.GetObjectInput{
		Bucket: &bucket,
		Key:    key,
	}

	return client.GetObject(request)
}

func deleteObject(client *s3.S3, bucket string, key *string) (err error) {
	request := &s3.DeleteObjectInput{
		Bucket: &bucket,
		Key:    key,
	}

	_, err = client.DeleteObject(request)
	return
}

func listObjects(client *s3.S3, bucket, prefix string, log *Log) (channel chan *s3.Object, err error) {
	request := &s3.ListObjectsInput{
		Bucket: &bucket,
		Prefix: &prefix,
	}

	channel = make(chan *s3.Object)

	if client == nil {
		close(channel)
		return
	}

	output, err := client.ListObjects(request)
	if err != nil {
		err = fmt.Errorf("S3 ListObjects: %s", err)
		close(channel)
		return
	}

	go func() {
		defer close(channel)

		for {
			for i := range output.Contents {
				object := output.Contents[i]
				channel <- object
				request.Marker = object.Key
			}

			if !*output.IsTruncated {
				break
			}

			if output, err = client.ListObjects(request); err != nil {
				log.Errorf("S3 ListObjects: %s", err)
				break
			}
		}
	}()

	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Holt and The Caddy 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 caddy

import (
	"github.com/caddyserver/caddy/v2/notify"
	"golang.org/x/sys/windows/svc"
)

func init() {
	isService, err := svc.IsWindowsService()
	if err != nil || !isService {
		return
	}
	go func() {
		_ = svc.Run("", runner{})
	}()
}

type runner struct{}

func (runner) Execute(args []string, request <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
	notify.SetGlobalStatus(status)
	status <- svc.Status{State: svc.StartPending}

	for {
		req := <-request
		switch req.Cmd {
		case svc.Interrogate:
			status <- req.CurrentStatus
		case svc.Stop, svc.Shutdown:
			status <- svc.Status{State: svc.StopPending}
			exitProcessFromSignal("SIGINT")
			return false, 0
		}
	}
}
<commit_msg>core: Chdir to executable location on Windows (#5115)<commit_after>// Copyright 2015 Matthew Holt and The Caddy 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 caddy

import (
	"os"
	"path/filepath"

	"github.com/caddyserver/caddy/v2/notify"
	"golang.org/x/sys/windows/svc"
)

func init() {
	isService, err := svc.IsWindowsService()
	if err != nil || !isService {
		return
	}

	// Windows services always start in the system32 directory, try to
	// switch into the directory where the caddy executable is.
	execPath, err := os.Executable()
	if err == nil {
		_ = os.Chdir(filepath.Dir(execPath))
	}

	go func() {
		_ = svc.Run("", runner{})
	}()
}

type runner struct{}

func (runner) Execute(args []string, request <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
	notify.SetGlobalStatus(status)
	status <- svc.Status{State: svc.StartPending}

	for {
		req := <-request
		switch req.Cmd {
		case svc.Interrogate:
			status <- req.CurrentStatus
		case svc.Stop, svc.Shutdown:
			status <- svc.Status{State: svc.StopPending}
			exitProcessFromSignal("SIGINT")
			return false, 0
		}
	}
}
<|endoftext|>


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

// Worksheet API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/worksheet

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// WorksheetGraph defines a worksheet cid to be include in the worksheet
type WorksheetGraph struct {
	GraphCID string `json:"graph"` // string
}

// WorksheetSmartQuery defines a query to include multiple worksheets
type WorksheetSmartQuery struct {
	Name  string   `json:"name"`
	Order []string `json:"order"`
	Query string   `json:"query"`
}

// Worksheet defines a worksheet. See https://login.circonus.com/resources/api/calls/worksheet for more information.
type Worksheet struct {
	CID          string                `json:"_cid,omitempty"`          // string
	Description  *string               `json:"description"`             // string or null
	Favorite     bool                  `json:"favorite"`                // boolean
	Graphs       []WorksheetGraph      `json:"worksheets,omitempty"`    // [] len >= 0
	Notes        *string               `json:"notes"`                   // string or null
	SmartQueries []WorksheetSmartQuery `json:"smart_queries,omitempty"` // [] len >= 0
	Tags         []string              `json:"tags"`                    // [] len >= 0
	Title        string                `json:"title"`                   // string
}

// NewWorksheet returns a new Worksheet (with defaults, if applicable)
func NewWorksheet() *Worksheet {
	return &Worksheet{}
}

// FetchWorksheet retrieves worksheet with passed cid.
func (a *API) FetchWorksheet(cid CIDType) (*Worksheet, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid worksheet CID [none]")
	}

	worksheetCID := string(*cid)

	matched, err := regexp.MatchString(config.WorksheetCIDRegex, worksheetCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid worksheet CID [%s]", worksheetCID)
	}

	result, err := a.Get(string(*cid))
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch worksheet, received JSON: %s", string(result))
	}

	worksheet := new(Worksheet)
	if err := json.Unmarshal(result, worksheet); err != nil {
		return nil, err
	}

	return worksheet, nil
}

// FetchWorksheets retrieves all worksheets available to API Token.
func (a *API) FetchWorksheets() (*[]Worksheet, error) {
	result, err := a.Get(config.WorksheetPrefix)
	if err != nil {
		return nil, err
	}

	var worksheets []Worksheet
	if err := json.Unmarshal(result, &worksheets); err != nil {
		return nil, err
	}

	return &worksheets, nil
}

// UpdateWorksheet updates passed worksheet.
func (a *API) UpdateWorksheet(cfg *Worksheet) (*Worksheet, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid worksheet config [nil]")
	}

	worksheetCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.WorksheetCIDRegex, worksheetCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid worksheet CID [%s]", worksheetCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update worksheet, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(worksheetCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	worksheet := &Worksheet{}
	if err := json.Unmarshal(result, worksheet); err != nil {
		return nil, err
	}

	return worksheet, nil
}

// CreateWorksheet creates a new worksheet.
func (a *API) CreateWorksheet(cfg *Worksheet) (*Worksheet, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid worksheet config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create annotation, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.WorksheetPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	worksheet := &Worksheet{}
	if err := json.Unmarshal(result, worksheet); err != nil {
		return nil, err
	}

	return worksheet, nil
}

// DeleteWorksheet deletes passed worksheet.
func (a *API) DeleteWorksheet(cfg *Worksheet) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid worksheet config [nil]")
	}
	return a.DeleteWorksheetByCID(CIDType(&cfg.CID))
}

// DeleteWorksheetByCID deletes worksheet with passed cid.
func (a *API) DeleteWorksheetByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid worksheet CID [none]")
	}

	worksheetCID := string(*cid)

	matched, err := regexp.MatchString(config.WorksheetCIDRegex, worksheetCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid worksheet CID [%s]", worksheetCID)
	}

	_, err = a.Delete(worksheetCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchWorksheets returns worksheets matching the specified search
// query and/or filter. If nil is passed for both parameters all
// worksheets will be returned.
func (a *API) SearchWorksheets(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Worksheet, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchWorksheets()
	}

	reqURL := url.URL{
		Path:     config.WorksheetPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var worksheets []Worksheet
	if err := json.Unmarshal(result, &worksheets); err != nil {
		return nil, err
	}

	return &worksheets, nil
}
<commit_msg>fix: typo in json field name fix: remove `omitempty` from required attributes<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Worksheet API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/worksheet

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// WorksheetGraph defines a worksheet cid to be include in the worksheet
type WorksheetGraph struct {
	GraphCID string `json:"graph"` // string
}

// WorksheetSmartQuery defines a query to include multiple worksheets
type WorksheetSmartQuery struct {
	Name  string   `json:"name"`
	Order []string `json:"order"`
	Query string   `json:"query"`
}

// Worksheet defines a worksheet. See https://login.circonus.com/resources/api/calls/worksheet for more information.
type Worksheet struct {
	CID          string                `json:"_cid"`          // string
	Description  *string               `json:"description"`   // string or null
	Favorite     bool                  `json:"favorite"`      // boolean
	Graphs       []WorksheetGraph      `json:"graphs"`        // [] len >= 0
	Notes        *string               `json:"notes"`         // string or null
	SmartQueries []WorksheetSmartQuery `json:"smart_queries"` // [] len >= 0
	Tags         []string              `json:"tags"`          // [] len >= 0
	Title        string                `json:"title"`         // string
}

// NewWorksheet returns a new Worksheet (with defaults, if applicable)
func NewWorksheet() *Worksheet {
	return &Worksheet{}
}

// FetchWorksheet retrieves worksheet with passed cid.
func (a *API) FetchWorksheet(cid CIDType) (*Worksheet, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid worksheet CID [none]")
	}

	worksheetCID := string(*cid)

	matched, err := regexp.MatchString(config.WorksheetCIDRegex, worksheetCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid worksheet CID [%s]", worksheetCID)
	}

	result, err := a.Get(string(*cid))
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch worksheet, received JSON: %s", string(result))
	}

	worksheet := new(Worksheet)
	if err := json.Unmarshal(result, worksheet); err != nil {
		return nil, err
	}

	return worksheet, nil
}

// FetchWorksheets retrieves all worksheets available to API Token.
func (a *API) FetchWorksheets() (*[]Worksheet, error) {
	result, err := a.Get(config.WorksheetPrefix)
	if err != nil {
		return nil, err
	}

	var worksheets []Worksheet
	if err := json.Unmarshal(result, &worksheets); err != nil {
		return nil, err
	}

	return &worksheets, nil
}

// UpdateWorksheet updates passed worksheet.
func (a *API) UpdateWorksheet(cfg *Worksheet) (*Worksheet, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid worksheet config [nil]")
	}

	worksheetCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.WorksheetCIDRegex, worksheetCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid worksheet CID [%s]", worksheetCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update worksheet, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(worksheetCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	worksheet := &Worksheet{}
	if err := json.Unmarshal(result, worksheet); err != nil {
		return nil, err
	}

	return worksheet, nil
}

// CreateWorksheet creates a new worksheet.
func (a *API) CreateWorksheet(cfg *Worksheet) (*Worksheet, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid worksheet config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create annotation, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.WorksheetPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	worksheet := &Worksheet{}
	if err := json.Unmarshal(result, worksheet); err != nil {
		return nil, err
	}

	return worksheet, nil
}

// DeleteWorksheet deletes passed worksheet.
func (a *API) DeleteWorksheet(cfg *Worksheet) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid worksheet config [nil]")
	}
	return a.DeleteWorksheetByCID(CIDType(&cfg.CID))
}

// DeleteWorksheetByCID deletes worksheet with passed cid.
func (a *API) DeleteWorksheetByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid worksheet CID [none]")
	}

	worksheetCID := string(*cid)

	matched, err := regexp.MatchString(config.WorksheetCIDRegex, worksheetCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid worksheet CID [%s]", worksheetCID)
	}

	_, err = a.Delete(worksheetCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchWorksheets returns worksheets matching the specified search
// query and/or filter. If nil is passed for both parameters all
// worksheets will be returned.
func (a *API) SearchWorksheets(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Worksheet, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchWorksheets()
	}

	reqURL := url.URL{
		Path:     config.WorksheetPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var worksheets []Worksheet
	if err := json.Unmarshal(result, &worksheets); err != nil {
		return nil, err
	}

	return &worksheets, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2021 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package sftp

import (
	"fmt"

	"github.com/blacknon/lssh/common"
	"github.com/urfave/cli"
)

//
func (r *RunSftp) rename(args []string) {
	// create app
	app := cli.NewApp()
	// app.UseShortOptionHandling = true

	// set help message
	app.CustomAppHelpTemplate = helptext
	app.Name = "rename"
	app.Usage = "lsftp build-in command: rename [remote machine rename]"
	app.ArgsUsage = "[path path]"
	app.HideHelp = true
	app.HideVersion = true
	app.EnableBashCompletion = true

	// action
	app.Action = func(c *cli.Context) error {
		if len(c.Args()) != 2 {
			fmt.Println("Requires two arguments")
			fmt.Println("rename [old] [new]")
			return nil
		}

		exit := make(chan bool)
		for s, cl := range r.Client {
			server := s
			client := cl

			oldname := c.Args()[0]
			newname := c.Args()[1]

			go func() {
				// get writer
				client.Output.Create(server)
				w := client.Output.NewWriter()

				// get current directory
				err := client.Connect.Rename(oldname, newname)
				if err != nil {
					fmt.Fprintf(w, "%s\n", err)
					exit <- true
					return
				}

				fmt.Fprintf(w, "rename: %s => %s\n", oldname, newname)
				exit <- true
				return
			}()
		}

		for i := 0; i < len(r.Client); i++ {
			<-exit
		}

		return nil
	}

	// parse short options
	args = common.ParseArgs(app.Flags, args)
	app.Run(args)

	return
}
<commit_msg>update. lsftpのrenameをサーバ指定対応化<commit_after>// Copyright (c) 2021 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package sftp

import (
	"fmt"

	"github.com/blacknon/lssh/common"
	"github.com/urfave/cli"
)

//
func (r *RunSftp) rename(args []string) {
	// create app
	app := cli.NewApp()
	// app.UseShortOptionHandling = true

	// set help message
	app.CustomAppHelpTemplate = helptext
	app.Name = "rename"
	app.Usage = "lsftp build-in command: rename [remote machine rename]"
	app.ArgsUsage = "[path path]"
	app.HideHelp = true
	app.HideVersion = true
	app.EnableBashCompletion = true

	// action
	app.Action = func(c *cli.Context) error {
		if len(c.Args()) != 2 {
			fmt.Println("Requires two arguments")
			fmt.Println("rename [old] [new]")
			return nil
		}

		// parse old path, with server...
		oldname := c.Args()[0]
		targetmap := map[string]*TargetConnectMap{}
		targetmap = r.createTargetMap(targetmap, oldname)

		exit := make(chan bool)
		for s, cl := range targetmap {
			server := s
			client := cl

			newname := c.Args()[1]
			go func() {
				// get writer
				client.Output.Create(server)
				w := client.Output.NewWriter()

				// get current directory
				oldname := client.Path[0]
				err := client.Connect.Rename(oldname, newname)
				if err != nil {
					fmt.Fprintf(w, "%s\n", err)
					exit <- true
					return
				}

				fmt.Fprintf(w, "rename: %s => %s\n", oldname, newname)
				exit <- true
				return
			}()
		}

		for i := 0; i < len(targetmap); i++ {
			<-exit
		}

		return nil
	}

	// parse short options
	args = common.ParseArgs(app.Flags, args)
	app.Run(args)

	return
}
<|endoftext|>


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

package gocui

import "github.com/nsf/termbox-go"

const MaxInt = int(^uint(0) >> 1)

// handleEdit manages the edition mode. We do not handle errors here because if
// an error happens, it is enough to keep the view without modifications.
func (g *Gui) handleEdit(v *View, ev *termbox.Event) {
	switch {
	case ev.Ch != 0 && ev.Mod == 0:
		v.editWrite(ev.Ch)
	case ev.Key == termbox.KeySpace:
		v.editWrite(' ')
	case ev.Key == termbox.KeyBackspace || ev.Key == termbox.KeyBackspace2:
		v.editDelete(true)
	case ev.Key == termbox.KeyDelete:
		v.editDelete(false)
	case ev.Key == termbox.KeyInsert:
		v.overwrite = !v.overwrite
	case ev.Key == termbox.KeyEnter:
		v.editNewLine()
	case ev.Key == termbox.KeyArrowDown:
		v.moveCursor(0, 1, false)
	case ev.Key == termbox.KeyArrowUp:
		v.moveCursor(0, -1, false)
	case ev.Key == termbox.KeyArrowLeft:
		v.moveCursor(-1, 0, false)
	case ev.Key == termbox.KeyArrowRight:
		v.moveCursor(1, 0, false)
	}
}

// editWrite writes a rune at the cursor position.
func (v *View) editWrite(ch rune) {
	v.writeRune(v.cx, v.cy, ch)
	v.moveCursor(1, 0, true)
}

// editDelete deletes a rune at the cursor position. back determines
// the direction.
func (v *View) editDelete(back bool) {
	x, y := v.ox+v.cx, v.oy+v.cy
	if y < 0 {
		return
	} else if y >= len(v.viewLines) {
		v.moveCursor(-1, 0, true)
		return
	}

	maxX, _ := v.Size()
	if back {
		if x == 0 { // start of the line
			if y < 1 {
				return
			}

			var maxPrevWidth int
			if v.Wrap {
				maxPrevWidth = maxX
			} else {
				maxPrevWidth = MaxInt
			}

			if v.viewLines[y].linesX == 0 { // regular line
				v.mergeLines(v.cy - 1)
				if len(v.viewLines[y-1].line) < maxPrevWidth {
					v.moveCursor(-1, 0, true)
				}
			} else { // wrapped line
				v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
				v.moveCursor(-1, 0, true)
			}
		} else {
			v.deleteRune(v.cx-1, v.cy)
			v.moveCursor(-1, 0, true)
		}
	} else if x == len(v.viewLines[y].line) { // end of the line
		v.mergeLines(v.cy)
	} else { // middle of the line
		v.deleteRune(v.cx, v.cy)
	}
}

// editNewLine inserts a new line under the cursor.
func (v *View) editNewLine() {
	v.breakLine(v.cx, v.cy)

	y := v.oy + v.cy
	if y >= len(v.viewLines) || (y >= 0 && y < len(v.viewLines) &&
		!(v.Wrap && v.cx == 0 && v.viewLines[y].linesX > 0)) {
		// new line at the end of the buffer or
		// cursor is not at the beginning of a wrapped line
		v.ox = 0
		v.cx = 0
		v.moveCursor(0, 1, true)
	}
}

// moveCursor moves the cursor taking into account the line or view widths and
// moves the origin when necessary.
func (v *View) moveCursor(dx, dy int, writeMode bool) {
	maxX, maxY := v.Size()
	cx, cy := v.cx+dx, v.cy+dy
	x, y := v.ox+cx, v.oy+cy

	var curLineWidth, prevLineWidth int
	// get the width of the current line
	if writeMode {
		if v.Wrap {
			curLineWidth = maxX - 1
		} else {
			curLineWidth = MaxInt
		}
	} else {
		if y >= 0 && y < len(v.viewLines) {
			curLineWidth = len(v.viewLines[y].line)
			if v.Wrap && curLineWidth >= maxX {
				curLineWidth = maxX - 1
			}
		} else {
			curLineWidth = 0
		}
	}
	// get the width of the previous line
	if y-1 >= 0 && y-1 < len(v.viewLines) {
		prevLineWidth = len(v.viewLines[y-1].line)
	} else {
		prevLineWidth = 0
	}

	// adjust cursor's x position and view's x origin
	if x > curLineWidth { // move to next line
		if dx > 0 { // horizontal movement
			if !v.Wrap {
				v.ox = 0
			}
			v.cx = 0
			cy += 1
		} else { // vertical movement
			if curLineWidth > 0 { // move cursor to the EOF
				if v.Wrap {
					v.cx = curLineWidth
				} else {
					ncx := curLineWidth - v.ox
					if ncx < 0 {
						v.ox += ncx
						if v.ox < 0 {
							v.ox = 0
						}
						v.cx = 0
					} else {
						v.cx = ncx
					}
				}
			} else {
				if !v.Wrap {
					v.ox = 0
				}
				v.cx = 0
			}
		}
	} else if cx < 0 {
		if !v.Wrap && v.ox > 0 { // move origin to the left
			v.ox -= 1
		} else { // move to previous line
			if prevLineWidth > 0 {
				if !v.Wrap { // set origin so the EOL is visible
					nox := prevLineWidth - maxX + 1
					if nox < 0 {
						v.ox = 0
					} else {
						v.ox = nox
					}
				}
				v.cx = prevLineWidth
			} else {
				if !v.Wrap {
					v.ox = 0
				}
				v.cx = 0
			}
			cy -= 1
		}
	} else { // stay on the same line
		if v.Wrap {
			v.cx = cx
		} else {
			if cx >= maxX {
				v.ox += 1
			} else {
				v.cx = cx
			}
		}
	}

	// adjust cursor's y position and view's y origin
	if cy >= maxY {
		v.oy += 1
	} else if cy < 0 {
		if v.oy > 0 {
			v.oy -= 1
		}
	} else {
		v.cy = cy
	}
}
<commit_msg>Minor refactoring<commit_after>// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocui

import "github.com/nsf/termbox-go"

const MaxInt = int(^uint(0) >> 1)

// handleEdit manages the edition mode. We do not handle errors here because if
// an error happens, it is enough to keep the view without modifications.
func (g *Gui) handleEdit(v *View, ev *termbox.Event) {
	switch {
	case ev.Ch != 0 && ev.Mod == 0:
		v.editWrite(ev.Ch)
	case ev.Key == termbox.KeySpace:
		v.editWrite(' ')
	case ev.Key == termbox.KeyBackspace || ev.Key == termbox.KeyBackspace2:
		v.editDelete(true)
	case ev.Key == termbox.KeyDelete:
		v.editDelete(false)
	case ev.Key == termbox.KeyInsert:
		v.overwrite = !v.overwrite
	case ev.Key == termbox.KeyEnter:
		v.editNewLine()
	case ev.Key == termbox.KeyArrowDown:
		v.moveCursor(0, 1, false)
	case ev.Key == termbox.KeyArrowUp:
		v.moveCursor(0, -1, false)
	case ev.Key == termbox.KeyArrowLeft:
		v.moveCursor(-1, 0, false)
	case ev.Key == termbox.KeyArrowRight:
		v.moveCursor(1, 0, false)
	}
}

// editWrite writes a rune at the cursor position.
func (v *View) editWrite(ch rune) {
	v.writeRune(v.cx, v.cy, ch)
	v.moveCursor(1, 0, true)
}

// editDelete deletes a rune at the cursor position. back determines
// the direction.
func (v *View) editDelete(back bool) {
	x, y := v.ox+v.cx, v.oy+v.cy
	if y < 0 {
		return
	} else if y >= len(v.viewLines) {
		v.moveCursor(-1, 0, true)
		return
	}

	maxX, _ := v.Size()
	if back {
		if x == 0 { // start of the line
			if y < 1 {
				return
			}

			var maxPrevWidth int
			if v.Wrap {
				maxPrevWidth = maxX
			} else {
				maxPrevWidth = MaxInt
			}

			if v.viewLines[y].linesX == 0 { // regular line
				v.mergeLines(v.cy - 1)
				if len(v.viewLines[y-1].line) < maxPrevWidth {
					v.moveCursor(-1, 0, true)
				}
			} else { // wrapped line
				v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
				v.moveCursor(-1, 0, true)
			}
		} else {
			v.deleteRune(v.cx-1, v.cy)
			v.moveCursor(-1, 0, true)
		}
	} else {
		if x == len(v.viewLines[y].line) { // end of the line
			v.mergeLines(v.cy)
		} else { // middle of the line
			v.deleteRune(v.cx, v.cy)
		}
	}
}

// editNewLine inserts a new line under the cursor.
func (v *View) editNewLine() {
	v.breakLine(v.cx, v.cy)

	y := v.oy + v.cy
	if y >= len(v.viewLines) || (y >= 0 && y < len(v.viewLines) &&
		!(v.Wrap && v.cx == 0 && v.viewLines[y].linesX > 0)) {
		// new line at the end of the buffer or
		// cursor is not at the beginning of a wrapped line
		v.ox = 0
		v.cx = 0
		v.moveCursor(0, 1, true)
	}
}

// moveCursor moves the cursor taking into account the line or view widths and
// moves the origin when necessary.
func (v *View) moveCursor(dx, dy int, writeMode bool) {
	maxX, maxY := v.Size()
	cx, cy := v.cx+dx, v.cy+dy
	x, y := v.ox+cx, v.oy+cy

	var curLineWidth, prevLineWidth int
	// get the width of the current line
	if writeMode {
		if v.Wrap {
			curLineWidth = maxX - 1
		} else {
			curLineWidth = MaxInt
		}
	} else {
		if y >= 0 && y < len(v.viewLines) {
			curLineWidth = len(v.viewLines[y].line)
			if v.Wrap && curLineWidth >= maxX {
				curLineWidth = maxX - 1
			}
		} else {
			curLineWidth = 0
		}
	}
	// get the width of the previous line
	if y-1 >= 0 && y-1 < len(v.viewLines) {
		prevLineWidth = len(v.viewLines[y-1].line)
	} else {
		prevLineWidth = 0
	}

	// adjust cursor's x position and view's x origin
	if x > curLineWidth { // move to next line
		if dx > 0 { // horizontal movement
			if !v.Wrap {
				v.ox = 0
			}
			v.cx = 0
			cy += 1
		} else { // vertical movement
			if curLineWidth > 0 { // move cursor to the EOF
				if v.Wrap {
					v.cx = curLineWidth
				} else {
					ncx := curLineWidth - v.ox
					if ncx < 0 {
						v.ox += ncx
						if v.ox < 0 {
							v.ox = 0
						}
						v.cx = 0
					} else {
						v.cx = ncx
					}
				}
			} else {
				if !v.Wrap {
					v.ox = 0
				}
				v.cx = 0
			}
		}
	} else if cx < 0 {
		if !v.Wrap && v.ox > 0 { // move origin to the left
			v.ox -= 1
		} else { // move to previous line
			if prevLineWidth > 0 {
				if !v.Wrap { // set origin so the EOL is visible
					nox := prevLineWidth - maxX + 1
					if nox < 0 {
						v.ox = 0
					} else {
						v.ox = nox
					}
				}
				v.cx = prevLineWidth
			} else {
				if !v.Wrap {
					v.ox = 0
				}
				v.cx = 0
			}
			cy -= 1
		}
	} else { // stay on the same line
		if v.Wrap {
			v.cx = cx
		} else {
			if cx >= maxX {
				v.ox += 1
			} else {
				v.cx = cx
			}
		}
	}

	// adjust cursor's y position and view's y origin
	if cy >= maxY {
		v.oy += 1
	} else if cy < 0 {
		if v.oy > 0 {
			v.oy -= 1
		}
	} else {
		v.cy = cy
	}
}
<|endoftext|>


<commit_before>// +build linux

package btrfs

/*
#include <stdlib.h>
#include <dirent.h>
#include <btrfs/ioctl.h>
#include <btrfs/ctree.h>
*/
import "C"

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"syscall"
	"unsafe"

	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/mount"
	"github.com/opencontainers/runc/libcontainer/label"
)

func init() {
	graphdriver.Register("btrfs", Init)
}

// Init returns a new BTRFS driver.
// An error is returned if BTRFS is not supported.
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {

	fsMagic, err := graphdriver.GetFSMagic(home)
	if err != nil {
		return nil, err
	}

	if fsMagic != graphdriver.FsMagicBtrfs {
		return nil, graphdriver.ErrPrerequisites
	}

	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
	if err != nil {
		return nil, err
	}
	if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil {
		return nil, err
	}

	if err := mount.MakePrivate(home); err != nil {
		return nil, err
	}

	driver := &Driver{
		home:    home,
		uidMaps: uidMaps,
		gidMaps: gidMaps,
	}

	return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil
}

// Driver contains information about the filesystem mounted.
type Driver struct {
	//root of the file system
	home    string
	uidMaps []idtools.IDMap
	gidMaps []idtools.IDMap
}

// String prints the name of the driver (btrfs).
func (d *Driver) String() string {
	return "btrfs"
}

// Status returns current driver information in a two dimensional string array.
// Output contains "Build Version" and "Library Version" of the btrfs libraries used.
// Version information can be used to check compatibility with your kernel.
func (d *Driver) Status() [][2]string {
	status := [][2]string{}
	if bv := btrfsBuildVersion(); bv != "-" {
		status = append(status, [2]string{"Build Version", bv})
	}
	if lv := btrfsLibVersion(); lv != -1 {
		status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
	}
	return status
}

// GetMetadata returns empty metadata for this driver.
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
	return nil, nil
}

// Cleanup unmounts the home directory.
func (d *Driver) Cleanup() error {
	return mount.Unmount(d.home)
}

func free(p *C.char) {
	C.free(unsafe.Pointer(p))
}

func openDir(path string) (*C.DIR, error) {
	Cpath := C.CString(path)
	defer free(Cpath)

	dir := C.opendir(Cpath)
	if dir == nil {
		return nil, fmt.Errorf("Can't open dir")
	}
	return dir, nil
}

func closeDir(dir *C.DIR) {
	if dir != nil {
		C.closedir(dir)
	}
}

func getDirFd(dir *C.DIR) uintptr {
	return uintptr(C.dirfd(dir))
}

func subvolCreate(path, name string) error {
	dir, err := openDir(path)
	if err != nil {
		return err
	}
	defer closeDir(dir)

	var args C.struct_btrfs_ioctl_vol_args
	for i, c := range []byte(name) {
		args.name[i] = C.char(c)
	}

	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
		uintptr(unsafe.Pointer(&args)))
	if errno != 0 {
		return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
	}
	return nil
}

func subvolSnapshot(src, dest, name string) error {
	srcDir, err := openDir(src)
	if err != nil {
		return err
	}
	defer closeDir(srcDir)

	destDir, err := openDir(dest)
	if err != nil {
		return err
	}
	defer closeDir(destDir)

	var args C.struct_btrfs_ioctl_vol_args_v2
	args.fd = C.__s64(getDirFd(srcDir))
	for i, c := range []byte(name) {
		args.name[i] = C.char(c)
	}

	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
		uintptr(unsafe.Pointer(&args)))
	if errno != 0 {
		return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
	}
	return nil
}

func isSubvolume(p string) (bool, error) {
	var bufStat syscall.Stat_t
	if err := syscall.Lstat(p, &bufStat); err != nil {
		return false, err
	}

	// return true if it is a btrfs subvolume
	return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
}

func subvolDelete(dirpath, name string) error {
	dir, err := openDir(dirpath)
	if err != nil {
		return err
	}
	defer closeDir(dir)
	fullPath := path.Join(dirpath, name)

	var args C.struct_btrfs_ioctl_vol_args

	// walk the btrfs subvolumes
	walkSubvolumes := func(p string, f os.FileInfo, err error) error {
		if err != nil {
			if os.IsNotExist(err) && p != fullPath {
				// missing most likely because the path was a subvolume that got removed in the previous iteration
				// since it's gone anyway, we don't care
				return nil
			}
			return fmt.Errorf("error walking subvolumes: %v", err)
		}
		// we want to check children only so skip itself
		// it will be removed after the filepath walk anyways
		if f.IsDir() && p != fullPath {
			sv, err := isSubvolume(p)
			if err != nil {
				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
			}
			if sv {
				if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
				}
			}
		}
		return nil
	}
	if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
		return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
	}

	// all subvolumes have been removed
	// now remove the one originally passed in
	for i, c := range []byte(name) {
		args.name[i] = C.char(c)
	}
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
		uintptr(unsafe.Pointer(&args)))
	if errno != 0 {
		return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
	}
	return nil
}

func (d *Driver) subvolumesDir() string {
	return path.Join(d.home, "subvolumes")
}

func (d *Driver) subvolumesDirID(id string) string {
	return path.Join(d.subvolumesDir(), id)
}

// Create the filesystem with given id.
func (d *Driver) Create(id, parent, mountLabel string) error {
	subvolumes := path.Join(d.home, "subvolumes")
	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
	if err != nil {
		return err
	}
	if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil {
		return err
	}
	if parent == "" {
		if err := subvolCreate(subvolumes, id); err != nil {
			return err
		}
	} else {
		parentDir := d.subvolumesDirID(parent)
		st, err := os.Stat(parentDir)
		if err != nil {
			return err
		}
		if !st.IsDir() {
			return fmt.Errorf("%s: not a direcotory", parentDir)
		}
		if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
			return err
		}
	}

	// if we have a remapped root (user namespaces enabled), change the created snapshot
	// dir ownership to match
	if rootUID != 0 || rootGID != 0 {
		if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil {
			return err
		}
	}

	return label.Relabel(path.Join(subvolumes, id), mountLabel, false)
}

// Remove the filesystem with given id.
func (d *Driver) Remove(id string) error {
	dir := d.subvolumesDirID(id)
	if _, err := os.Stat(dir); err != nil {
		return err
	}
	if err := subvolDelete(d.subvolumesDir(), id); err != nil {
		return err
	}
	if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Get the requested filesystem id.
func (d *Driver) Get(id, mountLabel string) (string, error) {
	dir := d.subvolumesDirID(id)
	st, err := os.Stat(dir)
	if err != nil {
		return "", err
	}

	if !st.IsDir() {
		return "", fmt.Errorf("%s: not a directory", dir)
	}

	return dir, nil
}

// Put is not implemented for BTRFS as there is no cleanup required for the id.
func (d *Driver) Put(id string) error {
	// Get() creates no runtime resources (like e.g. mounts)
	// so this doesn't need to do anything.
	return nil
}

// Exists checks if the id exists in the filesystem.
func (d *Driver) Exists(id string) bool {
	dir := d.subvolumesDirID(id)
	_, err := os.Stat(dir)
	return err == nil
}
<commit_msg>Fix the typo<commit_after>// +build linux

package btrfs

/*
#include <stdlib.h>
#include <dirent.h>
#include <btrfs/ioctl.h>
#include <btrfs/ctree.h>
*/
import "C"

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"syscall"
	"unsafe"

	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/mount"
	"github.com/opencontainers/runc/libcontainer/label"
)

func init() {
	graphdriver.Register("btrfs", Init)
}

// Init returns a new BTRFS driver.
// An error is returned if BTRFS is not supported.
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {

	fsMagic, err := graphdriver.GetFSMagic(home)
	if err != nil {
		return nil, err
	}

	if fsMagic != graphdriver.FsMagicBtrfs {
		return nil, graphdriver.ErrPrerequisites
	}

	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
	if err != nil {
		return nil, err
	}
	if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil {
		return nil, err
	}

	if err := mount.MakePrivate(home); err != nil {
		return nil, err
	}

	driver := &Driver{
		home:    home,
		uidMaps: uidMaps,
		gidMaps: gidMaps,
	}

	return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil
}

// Driver contains information about the filesystem mounted.
type Driver struct {
	//root of the file system
	home    string
	uidMaps []idtools.IDMap
	gidMaps []idtools.IDMap
}

// String prints the name of the driver (btrfs).
func (d *Driver) String() string {
	return "btrfs"
}

// Status returns current driver information in a two dimensional string array.
// Output contains "Build Version" and "Library Version" of the btrfs libraries used.
// Version information can be used to check compatibility with your kernel.
func (d *Driver) Status() [][2]string {
	status := [][2]string{}
	if bv := btrfsBuildVersion(); bv != "-" {
		status = append(status, [2]string{"Build Version", bv})
	}
	if lv := btrfsLibVersion(); lv != -1 {
		status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
	}
	return status
}

// GetMetadata returns empty metadata for this driver.
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
	return nil, nil
}

// Cleanup unmounts the home directory.
func (d *Driver) Cleanup() error {
	return mount.Unmount(d.home)
}

func free(p *C.char) {
	C.free(unsafe.Pointer(p))
}

func openDir(path string) (*C.DIR, error) {
	Cpath := C.CString(path)
	defer free(Cpath)

	dir := C.opendir(Cpath)
	if dir == nil {
		return nil, fmt.Errorf("Can't open dir")
	}
	return dir, nil
}

func closeDir(dir *C.DIR) {
	if dir != nil {
		C.closedir(dir)
	}
}

func getDirFd(dir *C.DIR) uintptr {
	return uintptr(C.dirfd(dir))
}

func subvolCreate(path, name string) error {
	dir, err := openDir(path)
	if err != nil {
		return err
	}
	defer closeDir(dir)

	var args C.struct_btrfs_ioctl_vol_args
	for i, c := range []byte(name) {
		args.name[i] = C.char(c)
	}

	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
		uintptr(unsafe.Pointer(&args)))
	if errno != 0 {
		return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
	}
	return nil
}

func subvolSnapshot(src, dest, name string) error {
	srcDir, err := openDir(src)
	if err != nil {
		return err
	}
	defer closeDir(srcDir)

	destDir, err := openDir(dest)
	if err != nil {
		return err
	}
	defer closeDir(destDir)

	var args C.struct_btrfs_ioctl_vol_args_v2
	args.fd = C.__s64(getDirFd(srcDir))
	for i, c := range []byte(name) {
		args.name[i] = C.char(c)
	}

	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
		uintptr(unsafe.Pointer(&args)))
	if errno != 0 {
		return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
	}
	return nil
}

func isSubvolume(p string) (bool, error) {
	var bufStat syscall.Stat_t
	if err := syscall.Lstat(p, &bufStat); err != nil {
		return false, err
	}

	// return true if it is a btrfs subvolume
	return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
}

func subvolDelete(dirpath, name string) error {
	dir, err := openDir(dirpath)
	if err != nil {
		return err
	}
	defer closeDir(dir)
	fullPath := path.Join(dirpath, name)

	var args C.struct_btrfs_ioctl_vol_args

	// walk the btrfs subvolumes
	walkSubvolumes := func(p string, f os.FileInfo, err error) error {
		if err != nil {
			if os.IsNotExist(err) && p != fullPath {
				// missing most likely because the path was a subvolume that got removed in the previous iteration
				// since it's gone anyway, we don't care
				return nil
			}
			return fmt.Errorf("error walking subvolumes: %v", err)
		}
		// we want to check children only so skip itself
		// it will be removed after the filepath walk anyways
		if f.IsDir() && p != fullPath {
			sv, err := isSubvolume(p)
			if err != nil {
				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
			}
			if sv {
				if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
				}
			}
		}
		return nil
	}
	if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
		return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
	}

	// all subvolumes have been removed
	// now remove the one originally passed in
	for i, c := range []byte(name) {
		args.name[i] = C.char(c)
	}
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
		uintptr(unsafe.Pointer(&args)))
	if errno != 0 {
		return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
	}
	return nil
}

func (d *Driver) subvolumesDir() string {
	return path.Join(d.home, "subvolumes")
}

func (d *Driver) subvolumesDirID(id string) string {
	return path.Join(d.subvolumesDir(), id)
}

// Create the filesystem with given id.
func (d *Driver) Create(id, parent, mountLabel string) error {
	subvolumes := path.Join(d.home, "subvolumes")
	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
	if err != nil {
		return err
	}
	if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil {
		return err
	}
	if parent == "" {
		if err := subvolCreate(subvolumes, id); err != nil {
			return err
		}
	} else {
		parentDir := d.subvolumesDirID(parent)
		st, err := os.Stat(parentDir)
		if err != nil {
			return err
		}
		if !st.IsDir() {
			return fmt.Errorf("%s: not a directory", parentDir)
		}
		if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
			return err
		}
	}

	// if we have a remapped root (user namespaces enabled), change the created snapshot
	// dir ownership to match
	if rootUID != 0 || rootGID != 0 {
		if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil {
			return err
		}
	}

	return label.Relabel(path.Join(subvolumes, id), mountLabel, false)
}

// Remove the filesystem with given id.
func (d *Driver) Remove(id string) error {
	dir := d.subvolumesDirID(id)
	if _, err := os.Stat(dir); err != nil {
		return err
	}
	if err := subvolDelete(d.subvolumesDir(), id); err != nil {
		return err
	}
	if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Get the requested filesystem id.
func (d *Driver) Get(id, mountLabel string) (string, error) {
	dir := d.subvolumesDirID(id)
	st, err := os.Stat(dir)
	if err != nil {
		return "", err
	}

	if !st.IsDir() {
		return "", fmt.Errorf("%s: not a directory", dir)
	}

	return dir, nil
}

// Put is not implemented for BTRFS as there is no cleanup required for the id.
func (d *Driver) Put(id string) error {
	// Get() creates no runtime resources (like e.g. mounts)
	// so this doesn't need to do anything.
	return nil
}

// Exists checks if the id exists in the filesystem.
func (d *Driver) Exists(id string) bool {
	dir := d.subvolumesDirID(id)
	_, err := os.Stat(dir)
	return err == nil
}
<|endoftext|>


<commit_before>package redisq

import (
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/go-redis/redis"
	uuid "github.com/satori/go.uuid"

	"github.com/vmihailenco/taskq/v2"
	"github.com/vmihailenco/taskq/v2/internal"
	"github.com/vmihailenco/taskq/v2/internal/redislock"
)

const batchSize = 100

type redisStreamClient interface {
	Del(keys ...string) *redis.IntCmd

	XAdd(a *redis.XAddArgs) *redis.StringCmd
	XDel(stream string, ids ...string) *redis.IntCmd
	XLen(stream string) *redis.IntCmd
	XRangeN(stream, start, stop string, count int64) *redis.XMessageSliceCmd
	XGroupCreateMkStream(stream, group, start string) *redis.StatusCmd
	XReadGroup(a *redis.XReadGroupArgs) *redis.XStreamSliceCmd
	XAck(stream, group string, ids ...string) *redis.IntCmd
	XPendingExt(a *redis.XPendingExtArgs) *redis.XPendingExtCmd
	XTrim(key string, maxLen int64) *redis.IntCmd
	XGroupDelConsumer(stream, group, consumer string) *redis.IntCmd

	ZAdd(key string, members ...*redis.Z) *redis.IntCmd
	ZRangeByScore(key string, opt *redis.ZRangeBy) *redis.StringSliceCmd
	ZRem(key string, members ...interface{}) *redis.IntCmd
}

type Queue struct {
	opt *taskq.QueueOptions

	consumer *taskq.Consumer

	redis redisStreamClient
	wg    sync.WaitGroup

	zset                string
	stream              string
	streamGroup         string
	streamConsumer      string
	schedulerLockPrefix string

	_closed uint32
}

var _ taskq.Queue = (*Queue)(nil)

func NewQueue(opt *taskq.QueueOptions) *Queue {
	const redisPrefix = "taskq:"

	if opt.WaitTimeout == 0 {
		opt.WaitTimeout = time.Second
	}
	opt.Init()
	if opt.Redis == nil {
		panic(fmt.Errorf("redisq: Redis client is required"))
	}
	red, ok := opt.Redis.(redisStreamClient)
	if !ok {
		panic(fmt.Errorf("redisq: Redis client must support streams"))
	}

	q := &Queue{
		opt: opt,

		redis: red,

		zset:                redisPrefix + opt.Name + ":zset",
		stream:              redisPrefix + opt.Name + ":stream",
		streamGroup:         "taskq",
		streamConsumer:      consumer(),
		schedulerLockPrefix: redisPrefix + opt.Name + ":scheduler-lock:",
	}

	q.wg.Add(1)
	go func() {
		defer q.wg.Done()
		q.scheduler("delayed", q.scheduleDelayed)
	}()

	q.wg.Add(1)
	go func() {
		defer q.wg.Done()
		q.scheduler("pending", q.schedulePending)
	}()

	return q
}

func consumer() string {
	s, _ := os.Hostname()
	s += "-pid" + strconv.Itoa(os.Getpid())
	return s
}

func (q *Queue) Name() string {
	return q.opt.Name
}

func (q *Queue) String() string {
	return fmt.Sprintf("queue=%q", q.Name())
}

func (q *Queue) Options() *taskq.QueueOptions {
	return q.opt
}

func (q *Queue) Consumer() *taskq.Consumer {
	if q.consumer == nil {
		q.consumer = taskq.NewConsumer(q)
	}
	return q.consumer
}

func (q *Queue) Len() (int, error) {
	n, err := q.redis.XLen(q.stream).Result()
	return int(n), err
}

// Add adds message to the queue.
func (q *Queue) Add(msg *taskq.Message) error {
	if msg.TaskName == "" {
		return internal.ErrTaskNameRequired
	}
	if q.isDuplicate(msg) {
		return taskq.ErrDuplicate
	}

	if msg.ID == "" {
		msg.ID = uuid.NewV4().String()
	}

	body, err := msg.MarshalBinary()
	if err != nil {
		return err
	}

	if msg.Delay > 0 {
		tm := time.Now().Add(msg.Delay)
		return q.redis.ZAdd(q.zset, &redis.Z{
			Score:  float64(unixMs(tm)),
			Member: body,
		}).Err()
	}

	return q.redis.XAdd(&redis.XAddArgs{
		Stream: q.stream,
		Values: map[string]interface{}{
			"body": body,
		},
	}).Err()
}

func (q *Queue) ReserveN(n int, waitTimeout time.Duration) ([]taskq.Message, error) {
	streams, err := q.redis.XReadGroup(&redis.XReadGroupArgs{
		Streams:  []string{q.stream, ">"},
		Group:    q.streamGroup,
		Consumer: q.streamConsumer,
		Count:    int64(n),
		Block:    waitTimeout,
	}).Result()
	if err != nil {
		if err == redis.Nil { // timeout
			return nil, nil
		}
		if strings.HasPrefix(err.Error(), "NOGROUP") {
			q.createStreamGroup()
			return q.ReserveN(n, waitTimeout)
		}
		return nil, err
	}

	stream := &streams[0]
	msgs := make([]taskq.Message, len(stream.Messages))
	for i := range stream.Messages {
		xmsg := &stream.Messages[i]
		msg := &msgs[i]

		err = unmarshalMessage(msg, xmsg)
		if err != nil {
			msg.StickyErr = err
		}
	}

	return msgs, nil
}

func (q *Queue) createStreamGroup() {
	_ = q.redis.XGroupCreateMkStream(q.stream, q.streamGroup, "0").Err()
}

func (q *Queue) Release(msg *taskq.Message) error {
	err := q.redis.XDel(q.stream, msg.ID).Err()
	if err != nil {
		return err
	}

	msg.ReservedCount++
	return q.Add(msg)
}

// Delete deletes the message from the queue.
func (q *Queue) Delete(msg *taskq.Message) error {
	return q.redis.XAck(q.stream, q.streamGroup, msg.ID).Err()
}

// Purge deletes all messages from the queue.
func (q *Queue) Purge() error {
	_ = q.redis.Del(q.zset).Err()
	_ = q.redis.XTrim(q.stream, 0).Err()
	return nil
}

// Close is like CloseTimeout with 30 seconds timeout.
func (q *Queue) Close() error {
	return q.CloseTimeout(30 * time.Second)
}

// CloseTimeout closes the queue waiting for pending messages to be processed.
func (q *Queue) CloseTimeout(timeout time.Duration) error {
	atomic.StoreUint32(&q._closed, 1)

	if q.consumer != nil {
		_ = q.consumer.StopTimeout(timeout)
	}

	_ = q.redis.XGroupDelConsumer(q.stream, q.streamGroup, q.streamConsumer).Err()

	return nil
}

func (q *Queue) closed() bool {
	return atomic.LoadUint32(&q._closed) == 1
}

func (q *Queue) scheduler(name string, fn func() (int, error)) {
	for {
		if q.closed() {
			break
		}

		var n int
		err := q.withRedisLock(q.schedulerLockPrefix+name, func() error {
			var err error
			n, err = fn()
			return err
		})
		if err != nil && err != redislock.ErrNotObtained {
			internal.Logger.Printf("redisq: %s failed: %s", name, err)
		}
		if err != nil || n == 0 {
			time.Sleep(q.schedulerBackoff())
		}
	}
}

func (q *Queue) schedulerBackoff() time.Duration {
	n := 250 + rand.Intn(250)
	return time.Duration(n) * time.Millisecond
}

func (q *Queue) scheduleDelayed() (int, error) {
	tm := time.Now()
	max := strconv.FormatInt(unixMs(tm), 10)
	bodies, err := q.redis.ZRangeByScore(q.zset, &redis.ZRangeBy{
		Min:   "-inf",
		Max:   max,
		Count: batchSize,
	}).Result()
	if err != nil {
		return 0, err
	}

	// TODO: pipeline
	for _, body := range bodies {
		err = q.redis.XAdd(&redis.XAddArgs{
			Stream: q.stream,
			Values: map[string]interface{}{
				"body": body,
			},
		}).Err()
		if err != nil {
			return 0, err
		}

		err := q.redis.ZRem(q.zset, body).Err()
		if err != nil {
			return 0, err
		}
	}

	return len(bodies), nil
}

func (q *Queue) schedulePending() (int, error) {
	tm := time.Now().Add(q.opt.ReservationTimeout)
	start := strconv.FormatInt(unixMs(tm), 10)

	pending, err := q.redis.XPendingExt(&redis.XPendingExtArgs{
		Stream: q.stream,
		Group:  q.streamGroup,
		Start:  start,
		End:    "+",
		Count:  batchSize,
	}).Result()
	if err != nil {
		if strings.HasPrefix(err.Error(), "NOGROUP") {
			q.createStreamGroup()
			return 0, nil
		}
		return 0, err
	}

	for i := range pending {
		xmsgInfo := &pending[i]
		id := xmsgInfo.ID

		xmsgs, err := q.redis.XRangeN(q.stream, id, id, 1).Result()
		if err != nil {
			return 0, err
		}
		if len(xmsgs) != 1 {
			err := fmt.Errorf("redisq: can't find peding message id=%q in stream=%q",
				id, q.stream)
			return 0, err
		}

		xmsg := &xmsgs[0]
		msg := new(taskq.Message)
		err = unmarshalMessage(msg, xmsg)
		if err != nil {
			return 0, err
		}

		err = q.Release(msg)
		if err != nil {
			return 0, err
		}
	}

	return len(pending), nil
}

func (q *Queue) isDuplicate(msg *taskq.Message) bool {
	if msg.Name == "" {
		return false
	}
	return q.opt.Storage.Exists("taskq:" + q.opt.Name + ":" + msg.Name)
}

func (q *Queue) withRedisLock(name string, fn func() error) error {
	lock, err := redislock.Obtain(q.opt.Redis, name, time.Minute, nil)
	if err != nil {
		return err
	}

	err = fn()

	if err := lock.Release(); err != nil {
		internal.Logger.Printf("redislock.Release failed: %s", err)
	}

	return err
}

func unixMs(tm time.Time) int64 {
	return tm.UnixNano() / int64(time.Millisecond)
}

func unmarshalMessage(msg *taskq.Message, xmsg *redis.XMessage) error {
	body := xmsg.Values["body"].(string)
	err := msg.UnmarshalBinary(internal.StringToBytes(body))
	if err != nil {
		return err
	}

	msg.ID = xmsg.ID
	if msg.ReservedCount == 0 {
		msg.ReservedCount = 1
	}

	return nil
}
<commit_msg>Make redisq Release atomic.<commit_after>package redisq

import (
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/go-redis/redis"
	uuid "github.com/satori/go.uuid"

	"github.com/vmihailenco/taskq/v2"
	"github.com/vmihailenco/taskq/v2/internal"
	"github.com/vmihailenco/taskq/v2/internal/redislock"
)

const batchSize = 100

type redisStreamClient interface {
	Del(keys ...string) *redis.IntCmd
	TxPipeline() redis.Pipeliner

	XAdd(a *redis.XAddArgs) *redis.StringCmd
	XDel(stream string, ids ...string) *redis.IntCmd
	XLen(stream string) *redis.IntCmd
	XRangeN(stream, start, stop string, count int64) *redis.XMessageSliceCmd
	XGroupCreateMkStream(stream, group, start string) *redis.StatusCmd
	XReadGroup(a *redis.XReadGroupArgs) *redis.XStreamSliceCmd
	XAck(stream, group string, ids ...string) *redis.IntCmd
	XPendingExt(a *redis.XPendingExtArgs) *redis.XPendingExtCmd
	XTrim(key string, maxLen int64) *redis.IntCmd
	XGroupDelConsumer(stream, group, consumer string) *redis.IntCmd

	ZAdd(key string, members ...*redis.Z) *redis.IntCmd
	ZRangeByScore(key string, opt *redis.ZRangeBy) *redis.StringSliceCmd
	ZRem(key string, members ...interface{}) *redis.IntCmd
}

type Queue struct {
	opt *taskq.QueueOptions

	consumer *taskq.Consumer

	redis redisStreamClient
	wg    sync.WaitGroup

	zset                string
	stream              string
	streamGroup         string
	streamConsumer      string
	schedulerLockPrefix string

	_closed uint32
}

var _ taskq.Queue = (*Queue)(nil)

func NewQueue(opt *taskq.QueueOptions) *Queue {
	const redisPrefix = "taskq:"

	if opt.WaitTimeout == 0 {
		opt.WaitTimeout = time.Second
	}
	opt.Init()
	if opt.Redis == nil {
		panic(fmt.Errorf("redisq: Redis client is required"))
	}
	red, ok := opt.Redis.(redisStreamClient)
	if !ok {
		panic(fmt.Errorf("redisq: Redis client must support streams"))
	}

	q := &Queue{
		opt: opt,

		redis: red,

		zset:                redisPrefix + opt.Name + ":zset",
		stream:              redisPrefix + opt.Name + ":stream",
		streamGroup:         "taskq",
		streamConsumer:      consumer(),
		schedulerLockPrefix: redisPrefix + opt.Name + ":scheduler-lock:",
	}

	q.wg.Add(1)
	go func() {
		defer q.wg.Done()
		q.scheduler("delayed", q.scheduleDelayed)
	}()

	q.wg.Add(1)
	go func() {
		defer q.wg.Done()
		q.scheduler("pending", q.schedulePending)
	}()

	return q
}

func consumer() string {
	s, _ := os.Hostname()
	s += "-pid" + strconv.Itoa(os.Getpid())
	return s
}

func (q *Queue) Name() string {
	return q.opt.Name
}

func (q *Queue) String() string {
	return fmt.Sprintf("queue=%q", q.Name())
}

func (q *Queue) Options() *taskq.QueueOptions {
	return q.opt
}

func (q *Queue) Consumer() *taskq.Consumer {
	if q.consumer == nil {
		q.consumer = taskq.NewConsumer(q)
	}
	return q.consumer
}

func (q *Queue) Len() (int, error) {
	n, err := q.redis.XLen(q.stream).Result()
	return int(n), err
}

// Add adds message to the queue.
func (q *Queue) Add(msg *taskq.Message) error {
	return q.add(q.redis, msg)
}

func (q *Queue) add(pipe redisStreamClient, msg *taskq.Message) error {
	if msg.TaskName == "" {
		return internal.ErrTaskNameRequired
	}
	if q.isDuplicate(msg) {
		return taskq.ErrDuplicate
	}

	if msg.ID == "" {
		msg.ID = uuid.NewV4().String()
	}

	body, err := msg.MarshalBinary()
	if err != nil {
		return err
	}

	if msg.Delay > 0 {
		tm := time.Now().Add(msg.Delay)
		return pipe.ZAdd(q.zset, &redis.Z{
			Score:  float64(unixMs(tm)),
			Member: body,
		}).Err()
	}

	return pipe.XAdd(&redis.XAddArgs{
		Stream: q.stream,
		Values: map[string]interface{}{
			"body": body,
		},
	}).Err()
}

func (q *Queue) ReserveN(n int, waitTimeout time.Duration) ([]taskq.Message, error) {
	streams, err := q.redis.XReadGroup(&redis.XReadGroupArgs{
		Streams:  []string{q.stream, ">"},
		Group:    q.streamGroup,
		Consumer: q.streamConsumer,
		Count:    int64(n),
		Block:    waitTimeout,
	}).Result()
	if err != nil {
		if err == redis.Nil { // timeout
			return nil, nil
		}
		if strings.HasPrefix(err.Error(), "NOGROUP") {
			q.createStreamGroup()
			return q.ReserveN(n, waitTimeout)
		}
		return nil, err
	}

	stream := &streams[0]
	msgs := make([]taskq.Message, len(stream.Messages))
	for i := range stream.Messages {
		xmsg := &stream.Messages[i]
		msg := &msgs[i]

		err = unmarshalMessage(msg, xmsg)
		if err != nil {
			msg.StickyErr = err
		}
	}

	return msgs, nil
}

func (q *Queue) createStreamGroup() {
	_ = q.redis.XGroupCreateMkStream(q.stream, q.streamGroup, "0").Err()
}

func (q *Queue) Release(msg *taskq.Message) error {
	// Make the delete and re-queue operation atomic in case we crash midway and lose a message
	pipe := q.redis.TxPipeline()
	err := pipe.XDel(q.stream, msg.ID).Err()
	if err != nil {
		return err
	}

	msg.ReservedCount++
	err = q.add(pipe, msg)
	if err != nil {
		return err
	}
	_, err = pipe.Exec()
	return err
}

// Delete deletes the message from the queue.
func (q *Queue) Delete(msg *taskq.Message) error {
	return q.redis.XAck(q.stream, q.streamGroup, msg.ID).Err()
}

// Purge deletes all messages from the queue.
func (q *Queue) Purge() error {
	_ = q.redis.Del(q.zset).Err()
	_ = q.redis.XTrim(q.stream, 0).Err()
	return nil
}

// Close is like CloseTimeout with 30 seconds timeout.
func (q *Queue) Close() error {
	return q.CloseTimeout(30 * time.Second)
}

// CloseTimeout closes the queue waiting for pending messages to be processed.
func (q *Queue) CloseTimeout(timeout time.Duration) error {
	atomic.StoreUint32(&q._closed, 1)

	if q.consumer != nil {
		_ = q.consumer.StopTimeout(timeout)
	}

	_ = q.redis.XGroupDelConsumer(q.stream, q.streamGroup, q.streamConsumer).Err()

	return nil
}

func (q *Queue) closed() bool {
	return atomic.LoadUint32(&q._closed) == 1
}

func (q *Queue) scheduler(name string, fn func() (int, error)) {
	for {
		if q.closed() {
			break
		}

		var n int
		err := q.withRedisLock(q.schedulerLockPrefix+name, func() error {
			var err error
			n, err = fn()
			return err
		})
		if err != nil && err != redislock.ErrNotObtained {
			internal.Logger.Printf("redisq: %s failed: %s", name, err)
		}
		if err != nil || n == 0 {
			time.Sleep(q.schedulerBackoff())
		}
	}
}

func (q *Queue) schedulerBackoff() time.Duration {
	n := 250 + rand.Intn(250)
	return time.Duration(n) * time.Millisecond
}

func (q *Queue) scheduleDelayed() (int, error) {
	tm := time.Now()
	max := strconv.FormatInt(unixMs(tm), 10)
	bodies, err := q.redis.ZRangeByScore(q.zset, &redis.ZRangeBy{
		Min:   "-inf",
		Max:   max,
		Count: batchSize,
	}).Result()
	if err != nil {
		return 0, err
	}

	// TODO: pipeline
	for _, body := range bodies {
		err = q.redis.XAdd(&redis.XAddArgs{
			Stream: q.stream,
			Values: map[string]interface{}{
				"body": body,
			},
		}).Err()
		if err != nil {
			return 0, err
		}

		err := q.redis.ZRem(q.zset, body).Err()
		if err != nil {
			return 0, err
		}
	}

	return len(bodies), nil
}

func (q *Queue) schedulePending() (int, error) {
	tm := time.Now().Add(q.opt.ReservationTimeout)
	start := strconv.FormatInt(unixMs(tm), 10)

	pending, err := q.redis.XPendingExt(&redis.XPendingExtArgs{
		Stream: q.stream,
		Group:  q.streamGroup,
		Start:  start,
		End:    "+",
		Count:  batchSize,
	}).Result()
	if err != nil {
		if strings.HasPrefix(err.Error(), "NOGROUP") {
			q.createStreamGroup()
			return 0, nil
		}
		return 0, err
	}

	for i := range pending {
		xmsgInfo := &pending[i]
		id := xmsgInfo.ID

		xmsgs, err := q.redis.XRangeN(q.stream, id, id, 1).Result()
		if err != nil {
			return 0, err
		}
		if len(xmsgs) != 1 {
			err := fmt.Errorf("redisq: can't find peding message id=%q in stream=%q",
				id, q.stream)
			return 0, err
		}

		xmsg := &xmsgs[0]
		msg := new(taskq.Message)
		err = unmarshalMessage(msg, xmsg)
		if err != nil {
			return 0, err
		}

		err = q.Release(msg)
		if err != nil {
			return 0, err
		}
	}

	return len(pending), nil
}

func (q *Queue) isDuplicate(msg *taskq.Message) bool {
	if msg.Name == "" {
		return false
	}
	return q.opt.Storage.Exists("taskq:" + q.opt.Name + ":" + msg.Name)
}

func (q *Queue) withRedisLock(name string, fn func() error) error {
	lock, err := redislock.Obtain(q.opt.Redis, name, time.Minute, nil)
	if err != nil {
		return err
	}

	err = fn()

	if err := lock.Release(); err != nil {
		internal.Logger.Printf("redislock.Release failed: %s", err)
	}

	return err
}

func unixMs(tm time.Time) int64 {
	return tm.UnixNano() / int64(time.Millisecond)
}

func unmarshalMessage(msg *taskq.Message, xmsg *redis.XMessage) error {
	body := xmsg.Values["body"].(string)
	err := msg.UnmarshalBinary(internal.StringToBytes(body))
	if err != nil {
		return err
	}

	msg.ID = xmsg.ID
	if msg.ReservedCount == 0 {
		msg.ReservedCount = 1
	}

	return nil
}
<|endoftext|>


<commit_before>package events

import (
	"encoding/json"
	"errors"
	"log"
	"os"
	"strings"
	"time"

	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
	"github.com/xuther/go-message-router/common"
	"github.com/xuther/go-message-router/publisher"
)

var Pub publisher.Publisher
var Building string
var Room string
var Name string

func Init() {
	getBuildingAndRoomAndName()
	var err error
	Pub, err = publisher.NewPublisher("7003", 1000, 10)
	if err != nil {
		log.Fatalf("Could not start publisher. Error: %v\n", err.Error())
	}
	log.Printf("Started publisher on :7003")

	go Pub.Listen()
	go SubInit()
}

func Publish(event eventinfrastructure.EventInfo) error {
	var e eventinfrastructure.Event

	// create the event
	e.Hostname = os.Getenv("PI_HOSTNAME")
	e.Timestamp = time.Now().Format(time.RFC3339)
	e.LocalEnvironment = len(os.Getenv("LOCAL_ENVIRONMENT")) > 0
	e.Building = Building
	e.Room = Room
	e.Event.Type = eventinfrastructure.USERACTION
	e.Event.EventCause = eventinfrastructure.USERINPUT
	e.Event.Device = e.Hostname
	e.Event.EventInfoKey = event.EventInfoKey
	e.Event.EventInfoValue = event.EventInfoValue
	if len(e.Event.Device) == 0 || len(e.Event.EventInfoKey) == 0 || len(e.Event.EventInfoValue) == 0 {
		return errors.New("Please fill in all the necessary fields")
	}

	toSend, err := json.Marshal(&e)
	if err != nil {
		return err
	}

	header := [24]byte{}
	copy(header[:], []byte(eventinfrastructure.UI))

	log.Printf("[Publisher] Publishing event: %s", toSend)
	err = Pub.Write(common.Message{MessageHeader: header, MessageBody: toSend})
	if err != nil {
		log.Printf("error: %s", err.Error())
		return err
	}

	return nil
}

func getBuildingAndRoomAndName() {
	hostname := os.Getenv("PI_HOSTNAME")
	if len(hostname) < 1 {
		log.Fatalf("failed to get pi hostname. Is it set?")
		return
	}

	data := strings.Split(hostname, "-")

	Building = data[0]
	Room = data[1]
	Name = data[2]

	log.Printf("Building: %s, Room: %s, Device: %s", Building, Room, Name)
	return
}
<commit_msg>added metrics tag<commit_after>package events

import (
	"encoding/json"
	"errors"
	"log"
	"os"
	"strings"
	"time"

	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
	"github.com/xuther/go-message-router/common"
	"github.com/xuther/go-message-router/publisher"
)

var Pub publisher.Publisher
var Building string
var Room string
var Name string

func Init() {
	getBuildingAndRoomAndName()
	var err error
	Pub, err = publisher.NewPublisher("7003", 1000, 10)
	if err != nil {
		log.Fatalf("Could not start publisher. Error: %v\n", err.Error())
	}
	log.Printf("Started publisher on :7003")

	go Pub.Listen()
	go SubInit()
}

func Publish(event eventinfrastructure.EventInfo) error {
	var e eventinfrastructure.Event

	// create the event
	e.Hostname = os.Getenv("PI_HOSTNAME")
	e.Timestamp = time.Now().Format(time.RFC3339)
	e.LocalEnvironment = len(os.Getenv("LOCAL_ENVIRONMENT")) > 0
	e.Building = Building
	e.Room = Room
	e.Event.Type = eventinfrastructure.USERACTION
	e.Event.EventCause = eventinfrastructure.USERINPUT
	e.Event.Device = e.Hostname
	e.Event.EventInfoKey = event.EventInfoKey
	e.Event.EventInfoValue = event.EventInfoValue
	if len(e.Event.Device) == 0 || len(e.Event.EventInfoKey) == 0 || len(e.Event.EventInfoValue) == 0 {
		return errors.New("Please fill in all the necessary fields")
	}

	toSend, err := json.Marshal(&e)
	if err != nil {
		return err
	}

	header := [24]byte{}
	copy(header[:], []byte(eventinfrastructure.Metrics))

	log.Printf("[Publisher] Publishing event: %s", toSend)
	err = Pub.Write(common.Message{MessageHeader: header, MessageBody: toSend})
	if err != nil {
		log.Printf("error: %s", err.Error())
		return err
	}

	return nil
}

func getBuildingAndRoomAndName() {
	hostname := os.Getenv("PI_HOSTNAME")
	if len(hostname) < 1 {
		log.Fatalf("failed to get pi hostname. Is it set?")
		return
	}

	data := strings.Split(hostname, "-")

	Building = data[0]
	Room = data[1]
	Name = data[2]

	log.Printf("Building: %s, Room: %s, Device: %s", Building, Room, Name)
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 The Bazel Authors. 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 main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"syscall"
	"time"
)

var Version = "Development"

var overrideableBazelFlags []string = []string{
	"--test_output=",
}

var debounceDuration = flag.Duration("debounce", 100*time.Millisecond, "Debounce duration")
var logToFile = flag.String("log_to_file", "-", "Log iBazel stderr to a file instead of os.Stderr")

func usage() {
	fmt.Fprintf(os.Stderr, `iBazel - Version %s

A file watcher for Bazel. Whenever a source file used in a specified
target, run, build, or test the specified targets.

Usage:

ibazel [flags] build|test|run targets...

Example:

ibazel test //path/to/my/testing:target
ibazel test //path/to/my/testing/targets/...
ibazel run //path/to/my/runnable:target -- --arguments --for_your=binary
ibazel build //path/to/my/buildable:target

iBazel flags:
`, Version)
	flag.PrintDefaults()
}

func isOverrideableBazelFlag(arg string) bool {
	for _, overrideable := range overrideableBazelFlags {
		if strings.HasPrefix(arg, overrideable) {
			return true
		}
	}
	return false
}

func parseArgs(in []string) (targets, bazelArgs, args []string) {
	afterDoubleDash := false
	for _, arg := range in {
		if afterDoubleDash {
			// Put it in the extra args section if we are after a double dash.
			args = append(args, arg)
		} else {
			// Check to see if this token is a double dash.
			if arg == "--" {
				afterDoubleDash = true
				continue
			}

			// Check to see if this flag is on the bazel whitelist of flags.
			if isOverrideableBazelFlag(arg) {
				bazelArgs = append(bazelArgs, arg)
				continue
			}

			// If none of those things then it's probably a target.
			targets = append(targets, arg)
		}
	}
	return
}

// main entrypoint for IBazel.
func main() {
	flag.Usage = usage
	flag.Parse()

	if *logToFile != "-" {
		var err error
		logFile, err := os.OpenFile(*logToFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			panic(err)
		}
		os.Stderr = logFile
	}

	if len(flag.Args()) < 2 {
		usage()
		return
	}

	command := strings.ToLower(flag.Args()[0])
	args := flag.Args()[1:]

	i, err := New()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating iBazel", err)
		os.Exit(1)
	}
	i.SetDebounceDuration(*debounceDuration)
	defer i.Cleanup()

	// increase the number of files that this process can
	// have open.
	err = setUlimit()
	if err != nil {
		fmt.Fprint(os.Stderr, "error setting higher file descriptor limit for this process: ", err)
	}

	handle(i, command, args)
}

func handle(i *IBazel, command string, args []string) {
	targets, bazelArgs, args := parseArgs(args)
	i.SetBazelArgs(bazelArgs)

	switch command {
	case "build":
		i.Build(targets...)
	case "test":
		i.Test(targets...)
	case "run":
		// Run only takes one argument
		i.Run(targets[0], args)
	default:
		fmt.Fprintf(os.Stderr, "Asked me to perform %s. I don't know how to do that.", command)
		usage()
		return
	}
}

func setUlimit() error {
	var lim syscall.Rlimit

	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
	if err != nil {
		return err
	}

	// set the "soft" file descriptor to the maximum
	// allowed by a userspace program.
	// http://man7.org/linux/man-pages/man2/getrlimit.2.html
	lim.Cur = lim.Max

	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Optional config flag to be passed to bazel (for CI use with --config=ci) (#113)<commit_after>// Copyright 2017 The Bazel Authors. 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 main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"syscall"
	"time"
)

var Version = "Development"

var overrideableBazelFlags []string = []string{
	"--test_output=",
	"--config=",
}

var debounceDuration = flag.Duration("debounce", 100*time.Millisecond, "Debounce duration")
var logToFile = flag.String("log_to_file", "-", "Log iBazel stderr to a file instead of os.Stderr")

func usage() {
	fmt.Fprintf(os.Stderr, `iBazel - Version %s

A file watcher for Bazel. Whenever a source file used in a specified
target, run, build, or test the specified targets.

Usage:

ibazel [flags] build|test|run targets...

Example:

ibazel test //path/to/my/testing:target
ibazel test //path/to/my/testing/targets/...
ibazel run //path/to/my/runnable:target -- --arguments --for_your=binary
ibazel build //path/to/my/buildable:target

iBazel flags:
`, Version)
	flag.PrintDefaults()
}

func isOverrideableBazelFlag(arg string) bool {
	for _, overrideable := range overrideableBazelFlags {
		if strings.HasPrefix(arg, overrideable) {
			return true
		}
	}
	return false
}

func parseArgs(in []string) (targets, bazelArgs, args []string) {
	afterDoubleDash := false
	for _, arg := range in {
		if afterDoubleDash {
			// Put it in the extra args section if we are after a double dash.
			args = append(args, arg)
		} else {
			// Check to see if this token is a double dash.
			if arg == "--" {
				afterDoubleDash = true
				continue
			}

			// Check to see if this flag is on the bazel whitelist of flags.
			if isOverrideableBazelFlag(arg) {
				bazelArgs = append(bazelArgs, arg)
				continue
			}

			// If none of those things then it's probably a target.
			targets = append(targets, arg)
		}
	}
	return
}

// main entrypoint for IBazel.
func main() {
	flag.Usage = usage
	flag.Parse()

	if *logToFile != "-" {
		var err error
		logFile, err := os.OpenFile(*logToFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			panic(err)
		}
		os.Stderr = logFile
	}

	if len(flag.Args()) < 2 {
		usage()
		return
	}

	command := strings.ToLower(flag.Args()[0])
	args := flag.Args()[1:]

	i, err := New()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating iBazel", err)
		os.Exit(1)
	}
	i.SetDebounceDuration(*debounceDuration)
	defer i.Cleanup()

	// increase the number of files that this process can
	// have open.
	err = setUlimit()
	if err != nil {
		fmt.Fprint(os.Stderr, "error setting higher file descriptor limit for this process: ", err)
	}

	handle(i, command, args)
}

func handle(i *IBazel, command string, args []string) {
	targets, bazelArgs, args := parseArgs(args)
	i.SetBazelArgs(bazelArgs)

	switch command {
	case "build":
		i.Build(targets...)
	case "test":
		i.Test(targets...)
	case "run":
		// Run only takes one argument
		i.Run(targets[0], args)
	default:
		fmt.Fprintf(os.Stderr, "Asked me to perform %s. I don't know how to do that.", command)
		usage()
		return
	}
}

func setUlimit() error {
	var lim syscall.Rlimit

	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
	if err != nil {
		return err
	}

	// set the "soft" file descriptor to the maximum
	// allowed by a userspace program.
	// http://man7.org/linux/man-pages/man2/getrlimit.2.html
	lim.Cur = lim.Max

	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>