<commit_before>package relay

import (
	"context"
	"fmt"
	"math/rand"
	"strconv"
	"sync"
	"time"

	basic "github.com/libp2p/go-libp2p/p2p/host/basic"

	autonat "github.com/libp2p/go-libp2p-autonat"
	_ "github.com/libp2p/go-libp2p-circuit"
	discovery "github.com/libp2p/go-libp2p-discovery"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	routing "github.com/libp2p/go-libp2p-routing"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

const (
	RelayRendezvous = "/libp2p/relay"
)

var (
	DesiredRelays = 3

	BootDelay = 20 * time.Second
)

// AutoRelay is a Host that uses relays for connectivity when a NAT is detected.
type AutoRelay struct {
	host     *basic.BasicHost
	discover discovery.Discoverer
	router   routing.PeerRouting
	autonat  autonat.AutoNAT
	addrsF   basic.AddrsFactory

	disconnect chan struct{}

	mx     sync.Mutex
	relays map[peer.ID]pstore.PeerInfo
	addrs  []ma.Multiaddr
}

func NewAutoRelay(ctx context.Context, bhost *basic.BasicHost, discover discovery.Discoverer, router routing.PeerRouting) *AutoRelay {
	ar := &AutoRelay{
		host:       bhost,
		discover:   discover,
		router:     router,
		addrsF:     bhost.AddrsFactory,
		relays:     make(map[peer.ID]pstore.PeerInfo),
		disconnect: make(chan struct{}, 1),
	}
	ar.autonat = autonat.NewAutoNAT(ctx, bhost, ar.baseAddrs)
	bhost.AddrsFactory = ar.hostAddrs
	bhost.Network().Notify(ar)
	go ar.background(ctx)
	return ar
}

func (ar *AutoRelay) hostAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
	ar.mx.Lock()
	defer ar.mx.Unlock()
	if ar.addrs != nil && ar.autonat.Status() == autonat.NATStatusPrivate {
		return ar.addrs
	} else {
		return ar.addrsF(addrs)
	}
}

func (ar *AutoRelay) baseAddrs() []ma.Multiaddr {
	return ar.addrsF(ar.host.AllAddrs())
}

func (ar *AutoRelay) background(ctx context.Context) {
	select {
	case <-time.After(autonat.AutoNATBootDelay + BootDelay):
	case <-ctx.Done():
		return
	}

	// when true, we need to identify push
	push := false

	for {
		wait := autonat.AutoNATRefreshInterval
		switch ar.autonat.Status() {
		case autonat.NATStatusUnknown:
			wait = autonat.AutoNATRetryInterval

		case autonat.NATStatusPublic:
			// invalidate addrs
			ar.mx.Lock()
			if ar.addrs != nil {
				ar.addrs = nil
				push = true
			}
			ar.mx.Unlock()

			// if we had previously announced relay addrs, push our public addrs
			if push {
				push = false
				ar.host.PushIdentify()
			}

		case autonat.NATStatusPrivate:
			push = false // clear, findRelays pushes as needed
			ar.findRelays(ctx)
		}

		select {
		case <-ar.disconnect:
			// invalidate addrs
			ar.mx.Lock()
			if ar.addrs != nil {
				ar.addrs = nil
				push = true
			}
			ar.mx.Unlock()
		case <-time.After(wait):
		case <-ctx.Done():
			return
		}
	}
}

func (ar *AutoRelay) findRelays(ctx context.Context) {
	ar.mx.Lock()
	if len(ar.relays) >= DesiredRelays {
		ar.mx.Unlock()
		return
	}
	need := DesiredRelays - len(ar.relays)
	ar.mx.Unlock()

	limit := 1000

	dctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	pis, err := discovery.FindPeers(dctx, ar.discover, RelayRendezvous, limit)
	cancel()
	if err != nil {
		log.Debugf("error discovering relays: %s", err.Error())
		return
	}

	pis = ar.selectRelays(ctx, pis, 50)
	update := 0

	for _, pi := range pis {
		ar.mx.Lock()
		if _, ok := ar.relays[pi.ID]; ok {
			ar.mx.Unlock()
			continue
		}
		ar.mx.Unlock()

		cctx, cancel := context.WithTimeout(ctx, 15*time.Second)
		err = ar.host.Connect(cctx, pi)
		cancel()
		if err != nil {
			log.Debugf("error connecting to relay %s: %s", pi.ID, err.Error())
			continue
		}

		log.Debugf("connected to relay %s", pi.ID)
		ar.mx.Lock()
		ar.relays[pi.ID] = pi
		ar.mx.Unlock()

		// tag the connection as very important
		ar.host.ConnManager().TagPeer(pi.ID, "relay", 42)

		update++
		need--
		if need == 0 {
			break
		}
	}

	if update > 0 || ar.addrs == nil {
		ar.updateAddrs()
	}
}

func (ar *AutoRelay) selectRelays(ctx context.Context, pis []pstore.PeerInfo, count int) []pstore.PeerInfo {
	// TODO better relay selection strategy; this just selects random relays
	//      but we should probably use ping latency as the selection metric

	if len(pis) < count {
		count = len(pis)
	}

	// only select relays that can be found by routing
	type queryResult struct {
		pi  pstore.PeerInfo
		err error
	}
	result := make([]pstore.PeerInfo, 0, count)
	resultCh := make(chan queryResult, len(pis))

	qctx, cancel := context.WithTimeout(ctx, 15*time.Second)
	defer cancel()

	for _, pi := range pis {
		go func(p peer.ID) {
			pi, err := ar.router.FindPeer(qctx, p)
			if err != nil {
				log.Debugf("Error finding relay peer %s: %s", p, err.Error())
			}
			resultCh <- queryResult{pi: pi, err: err}
		}(pi.ID)
	}

	rcount := 0
	for len(result) < count && rcount < len(pis) {
		select {
		case qr := <-resultCh:
			rcount++
			if qr.err == nil {
				result = append(result, cleanupAddressSet(qr.pi))
			}

		case <-qctx.Done():
			break
		}
	}

	shuffleRelays(result)
	return result
}

func (ar *AutoRelay) updateAddrs() {
	ar.doUpdateAddrs()
	ar.host.PushIdentify()
}

// This function updates our NATed advertised addrs (ar.addrs)
// The public addrs are rewritten so that they only retain the public IP part; they
// become undialable but are useful as a hint to the dialer to determine whether or not
// to dial private addrs.
// The non-public addrs are included verbatim so that peers behind the same NAT/firewall
// can still dial us directly.
// On top of those, we add the relay-specific addrs for the relays to which we are
// connected. For each non-private relay addr, we encapsulate the p2p-circuit addr
// through which we can be dialed.
func (ar *AutoRelay) doUpdateAddrs() {
	ar.mx.Lock()
	defer ar.mx.Unlock()

	addrs := ar.baseAddrs()
	raddrs := make([]ma.Multiaddr, 0, len(addrs)+len(ar.relays))

	// remove our public addresses from the list
	for _, addr := range addrs {
		if manet.IsPublicAddr(addr) {
			continue
		}
		raddrs = append(raddrs, addr)
	}

	// add relay specific addrs to the list
	for _, pi := range ar.relays {
		circuit, err := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s/p2p-circuit", pi.ID.Pretty()))
		if err != nil {
			panic(err)
		}

		for _, addr := range pi.Addrs {
			if !manet.IsPrivateAddr(addr) {
				pub := addr.Encapsulate(circuit)
				raddrs = append(raddrs, pub)
			}
		}
	}

	ar.addrs = raddrs
}

// This function cleans up a relay's address set to remove private addresses and curtail
// addrsplosion. For the latter, we use the following heuristic:
// - if the address set includes a (tcp) address with the default port 4001,
//   we remove all tcp addrs with a different port
// - Otherwise we remove all addrs with ephemeral ports (>= 32768)
func cleanupAddressSet(pi pstore.PeerInfo) pstore.PeerInfo {
	// pass-1: find default port
	has4001 := false
	for _, addr := range pi.Addrs {
		port, err := tcpPort(addr)
		if err != nil {
			continue
		}
		if port == 4001 {
			has4001 = true
			break
		}
	}

	// pass-2: cleanup
	var newAddrs []ma.Multiaddr

	for _, addr := range pi.Addrs {
		if manet.IsPrivateAddr(addr) {
			continue
		}

		port, err := tcpPort(addr)
		if err == nil {
			if (has4001 && port != 4001) || port >= 32768 {
				continue
			}
		}

		newAddrs = append(newAddrs, addr)
	}

	return pstore.PeerInfo{ID: pi.ID, Addrs: newAddrs}
}

func tcpPort(addr ma.Multiaddr) (int, error) {
	val, err := addr.ValueForProtocol(ma.P_TCP)
	if err != nil {
		// not tcp
		return 0, err
	}
	return strconv.Atoi(val)
}

func shuffleRelays(pis []pstore.PeerInfo) {
	for i := range pis {
		j := rand.Intn(i + 1)
		pis[i], pis[j] = pis[j], pis[i]
	}
}

func (ar *AutoRelay) Listen(inet.Network, ma.Multiaddr)      {}
func (ar *AutoRelay) ListenClose(inet.Network, ma.Multiaddr) {}
func (ar *AutoRelay) Connected(inet.Network, inet.Conn)      {}

func (ar *AutoRelay) Disconnected(net inet.Network, c inet.Conn) {
	p := c.RemotePeer()

	ar.mx.Lock()
	defer ar.mx.Unlock()

	if ar.host.Network().Connectedness(p) == inet.Connected {
		// We have a second connection.
		return
	}

	if _, ok := ar.relays[p]; ok {
		delete(ar.relays, p)
		select {
		case ar.disconnect <- struct{}{}:
		default:
		}
	}
}

func (ar *AutoRelay) OpenedStream(inet.Network, inet.Stream) {}
func (ar *AutoRelay) ClosedStream(inet.Network, inet.Stream) {}
<commit_msg>shuffle relay set before queries<commit_after>package relay

import (
	"context"
	"fmt"
	"math/rand"
	"strconv"
	"sync"
	"time"

	basic "github.com/libp2p/go-libp2p/p2p/host/basic"

	autonat "github.com/libp2p/go-libp2p-autonat"
	_ "github.com/libp2p/go-libp2p-circuit"
	discovery "github.com/libp2p/go-libp2p-discovery"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	routing "github.com/libp2p/go-libp2p-routing"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

const (
	RelayRendezvous = "/libp2p/relay"
)

var (
	DesiredRelays = 3

	BootDelay = 20 * time.Second
)

// AutoRelay is a Host that uses relays for connectivity when a NAT is detected.
type AutoRelay struct {
	host     *basic.BasicHost
	discover discovery.Discoverer
	router   routing.PeerRouting
	autonat  autonat.AutoNAT
	addrsF   basic.AddrsFactory

	disconnect chan struct{}

	mx     sync.Mutex
	relays map[peer.ID]pstore.PeerInfo
	addrs  []ma.Multiaddr
}

func NewAutoRelay(ctx context.Context, bhost *basic.BasicHost, discover discovery.Discoverer, router routing.PeerRouting) *AutoRelay {
	ar := &AutoRelay{
		host:       bhost,
		discover:   discover,
		router:     router,
		addrsF:     bhost.AddrsFactory,
		relays:     make(map[peer.ID]pstore.PeerInfo),
		disconnect: make(chan struct{}, 1),
	}
	ar.autonat = autonat.NewAutoNAT(ctx, bhost, ar.baseAddrs)
	bhost.AddrsFactory = ar.hostAddrs
	bhost.Network().Notify(ar)
	go ar.background(ctx)
	return ar
}

func (ar *AutoRelay) hostAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
	ar.mx.Lock()
	defer ar.mx.Unlock()
	if ar.addrs != nil && ar.autonat.Status() == autonat.NATStatusPrivate {
		return ar.addrs
	} else {
		return ar.addrsF(addrs)
	}
}

func (ar *AutoRelay) baseAddrs() []ma.Multiaddr {
	return ar.addrsF(ar.host.AllAddrs())
}

func (ar *AutoRelay) background(ctx context.Context) {
	select {
	case <-time.After(autonat.AutoNATBootDelay + BootDelay):
	case <-ctx.Done():
		return
	}

	// when true, we need to identify push
	push := false

	for {
		wait := autonat.AutoNATRefreshInterval
		switch ar.autonat.Status() {
		case autonat.NATStatusUnknown:
			wait = autonat.AutoNATRetryInterval

		case autonat.NATStatusPublic:
			// invalidate addrs
			ar.mx.Lock()
			if ar.addrs != nil {
				ar.addrs = nil
				push = true
			}
			ar.mx.Unlock()

			// if we had previously announced relay addrs, push our public addrs
			if push {
				push = false
				ar.host.PushIdentify()
			}

		case autonat.NATStatusPrivate:
			push = false // clear, findRelays pushes as needed
			ar.findRelays(ctx)
		}

		select {
		case <-ar.disconnect:
			// invalidate addrs
			ar.mx.Lock()
			if ar.addrs != nil {
				ar.addrs = nil
				push = true
			}
			ar.mx.Unlock()
		case <-time.After(wait):
		case <-ctx.Done():
			return
		}
	}
}

func (ar *AutoRelay) findRelays(ctx context.Context) {
	ar.mx.Lock()
	if len(ar.relays) >= DesiredRelays {
		ar.mx.Unlock()
		return
	}
	need := DesiredRelays - len(ar.relays)
	ar.mx.Unlock()

	limit := 1000

	dctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	pis, err := discovery.FindPeers(dctx, ar.discover, RelayRendezvous, limit)
	cancel()
	if err != nil {
		log.Debugf("error discovering relays: %s", err.Error())
		return
	}

	pis = ar.selectRelays(ctx, pis, 50)
	update := 0

	for _, pi := range pis {
		ar.mx.Lock()
		if _, ok := ar.relays[pi.ID]; ok {
			ar.mx.Unlock()
			continue
		}
		ar.mx.Unlock()

		cctx, cancel := context.WithTimeout(ctx, 15*time.Second)
		err = ar.host.Connect(cctx, pi)
		cancel()
		if err != nil {
			log.Debugf("error connecting to relay %s: %s", pi.ID, err.Error())
			continue
		}

		log.Debugf("connected to relay %s", pi.ID)
		ar.mx.Lock()
		ar.relays[pi.ID] = pi
		ar.mx.Unlock()

		// tag the connection as very important
		ar.host.ConnManager().TagPeer(pi.ID, "relay", 42)

		update++
		need--
		if need == 0 {
			break
		}
	}

	if update > 0 || ar.addrs == nil {
		ar.updateAddrs()
	}
}

func (ar *AutoRelay) selectRelays(ctx context.Context, pis []pstore.PeerInfo, count int) []pstore.PeerInfo {
	// TODO better relay selection strategy; this just selects random relays
	//      but we should probably use ping latency as the selection metric

	if len(pis) < count {
		count = len(pis)
	}

	// only select relays that can be found by routing
	type queryResult struct {
		pi  pstore.PeerInfo
		err error
	}
	result := make([]pstore.PeerInfo, 0, count)
	resultCh := make(chan queryResult, len(pis))

	qctx, cancel := context.WithTimeout(ctx, 15*time.Second)
	defer cancel()

	// shuffle to randomize the order of queries
	shuffleRelays(pis)
	for _, pi := range pis {
		go func(p peer.ID) {
			pi, err := ar.router.FindPeer(qctx, p)
			if err != nil {
				log.Debugf("Error finding relay peer %s: %s", p, err.Error())
			}
			resultCh <- queryResult{pi: pi, err: err}
		}(pi.ID)
	}

	rcount := 0
	for len(result) < count && rcount < len(pis) {
		select {
		case qr := <-resultCh:
			rcount++
			if qr.err == nil {
				result = append(result, cleanupAddressSet(qr.pi))
			}

		case <-qctx.Done():
			break
		}
	}

	shuffleRelays(result)
	return result
}

func (ar *AutoRelay) updateAddrs() {
	ar.doUpdateAddrs()
	ar.host.PushIdentify()
}

// This function updates our NATed advertised addrs (ar.addrs)
// The public addrs are rewritten so that they only retain the public IP part; they
// become undialable but are useful as a hint to the dialer to determine whether or not
// to dial private addrs.
// The non-public addrs are included verbatim so that peers behind the same NAT/firewall
// can still dial us directly.
// On top of those, we add the relay-specific addrs for the relays to which we are
// connected. For each non-private relay addr, we encapsulate the p2p-circuit addr
// through which we can be dialed.
func (ar *AutoRelay) doUpdateAddrs() {
	ar.mx.Lock()
	defer ar.mx.Unlock()

	addrs := ar.baseAddrs()
	raddrs := make([]ma.Multiaddr, 0, len(addrs)+len(ar.relays))

	// remove our public addresses from the list
	for _, addr := range addrs {
		if manet.IsPublicAddr(addr) {
			continue
		}
		raddrs = append(raddrs, addr)
	}

	// add relay specific addrs to the list
	for _, pi := range ar.relays {
		circuit, err := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s/p2p-circuit", pi.ID.Pretty()))
		if err != nil {
			panic(err)
		}

		for _, addr := range pi.Addrs {
			if !manet.IsPrivateAddr(addr) {
				pub := addr.Encapsulate(circuit)
				raddrs = append(raddrs, pub)
			}
		}
	}

	ar.addrs = raddrs
}

// This function cleans up a relay's address set to remove private addresses and curtail
// addrsplosion. For the latter, we use the following heuristic:
// - if the address set includes a (tcp) address with the default port 4001,
//   we remove all tcp addrs with a different port
// - Otherwise we remove all addrs with ephemeral ports (>= 32768)
func cleanupAddressSet(pi pstore.PeerInfo) pstore.PeerInfo {
	// pass-1: find default port
	has4001 := false
	for _, addr := range pi.Addrs {
		port, err := tcpPort(addr)
		if err != nil {
			continue
		}
		if port == 4001 {
			has4001 = true
			break
		}
	}

	// pass-2: cleanup
	var newAddrs []ma.Multiaddr

	for _, addr := range pi.Addrs {
		if manet.IsPrivateAddr(addr) {
			continue
		}

		port, err := tcpPort(addr)
		if err == nil {
			if (has4001 && port != 4001) || port >= 32768 {
				continue
			}
		}

		newAddrs = append(newAddrs, addr)
	}

	return pstore.PeerInfo{ID: pi.ID, Addrs: newAddrs}
}

func tcpPort(addr ma.Multiaddr) (int, error) {
	val, err := addr.ValueForProtocol(ma.P_TCP)
	if err != nil {
		// not tcp
		return 0, err
	}
	return strconv.Atoi(val)
}

func shuffleRelays(pis []pstore.PeerInfo) {
	for i := range pis {
		j := rand.Intn(i + 1)
		pis[i], pis[j] = pis[j], pis[i]
	}
}

func (ar *AutoRelay) Listen(inet.Network, ma.Multiaddr)      {}
func (ar *AutoRelay) ListenClose(inet.Network, ma.Multiaddr) {}
func (ar *AutoRelay) Connected(inet.Network, inet.Conn)      {}

func (ar *AutoRelay) Disconnected(net inet.Network, c inet.Conn) {
	p := c.RemotePeer()

	ar.mx.Lock()
	defer ar.mx.Unlock()

	if ar.host.Network().Connectedness(p) == inet.Connected {
		// We have a second connection.
		return
	}

	if _, ok := ar.relays[p]; ok {
		delete(ar.relays, p)
		select {
		case ar.disconnect <- struct{}{}:
		default:
		}
	}
}

func (ar *AutoRelay) OpenedStream(inet.Network, inet.Stream) {}
func (ar *AutoRelay) ClosedStream(inet.Network, inet.Stream) {}
<|endoftext|>


<commit_before>package types

// target.go defines the target type and implements a few helper functions for
// manipulating the target type.

import (
	"errors"
	"math/big"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
)

type (
	// A Target is a hash that a block's ID must be "less than" in order for
	// the block to be considered valid. Miners vary the block's 'Nonce' field
	// in order to brute-force such an ID. The inverse of a Target is called
	// the "difficulty," because it is proportional to the amount of time
	// required to brute-force the Target.
	Target crypto.Hash
)

var (
	ErrNegativeTarget = errors.New("negative value used when converting to target")
)

// Add returns the resulting target with the difficulty of 'x' and 'y' are
// added together. Note that the difficulty is the inverse of the target. The
// sum is defined by:
//		sum(x, y) = 1/(1/x + 1/y)
func (x Target) AddDifficulties(y Target) (t Target) {
	sumDifficulty := new(big.Rat).Add(x.Inverse(), y.Inverse())
	return RatToTarget(new(big.Rat).Inv(sumDifficulty))
}

// Cmp compares the difficulties of two targets. Note that the difficulty is
// the inverse of the target. The results are as follows:
//		-1 if x <  y
//		 0 if x == y
//		+1 if x >  y
func (x Target) Cmp(y Target) int {
	return x.Int().Cmp(y.Int())
}

// Int converts a Target to a big.Int.
func (t Target) Int() *big.Int {
	return new(big.Int).SetBytes(t[:])
}

// IntToTarget converts a big.Int to a Target. Negative inputs trigger a panic.
func IntToTarget(i *big.Int) (t Target) {
	// Check for negatives.
	if i.Sign() < 0 {
		if build.DEBUG {
			panic(ErrNegativeTarget)
		}
	} else {
		// In the event of overflow, return the maximum.
		if i.BitLen() > 256 {
			return RootDepth
		}
		b := i.Bytes()
		offset := len(t[:]) - len(b)
		copy(t[offset:], b)
	}
	return
}

// Inverse returns the inverse of a Target as a big.Rat
func (t Target) Inverse() *big.Rat {
	return new(big.Rat).Inv(t.Rat())
}

// Mul multiplies the difficulty of a target by y. The product is defined by:
//		y / x
func (x Target) MulDifficulty(y *big.Rat) (t Target) {
	product := new(big.Rat).Mul(y, x.Inverse())
	product = product.Inv(product)
	return RatToTarget(product)
}

// Rat converts a Target to a big.Rat.
func (t Target) Rat() *big.Rat {
	return new(big.Rat).SetInt(t.Int())
}

// RatToTarget converts a big.Rat to a Target.
func RatToTarget(r *big.Rat) (t Target) {
	if r.Num().Sign() < 0 {
		if build.DEBUG {
			panic(ErrNegativeTarget)
		}
	} else {
		i := new(big.Int).Div(r.Num(), r.Denom())
		t = IntToTarget(i)
	}
	return
}
<commit_msg>add method 'Difficulty' to types.Target<commit_after>package types

// target.go defines the target type and implements a few helper functions for
// manipulating the target type.

import (
	"errors"
	"math/big"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
)

type (
	// A Target is a hash that a block's ID must be "less than" in order for
	// the block to be considered valid. Miners vary the block's 'Nonce' field
	// in order to brute-force such an ID. The inverse of a Target is called
	// the "difficulty," because it is proportional to the amount of time
	// required to brute-force the Target.
	Target crypto.Hash
)

var (
	ErrNegativeTarget = errors.New("negative value used when converting to target")
)

// Add returns the resulting target with the difficulty of 'x' and 'y' are
// added together. Note that the difficulty is the inverse of the target. The
// sum is defined by:
//		sum(x, y) = 1/(1/x + 1/y)
func (x Target) AddDifficulties(y Target) (t Target) {
	sumDifficulty := new(big.Rat).Add(x.Inverse(), y.Inverse())
	return RatToTarget(new(big.Rat).Inv(sumDifficulty))
}

// Cmp compares the difficulties of two targets. Note that the difficulty is
// the inverse of the target. The results are as follows:
//		-1 if x <  y
//		 0 if x == y
//		+1 if x >  y
func (x Target) Cmp(y Target) int {
	return x.Int().Cmp(y.Int())
}

// Difficulty returns the difficulty associated with a given target.
func (t Target) Difficulty() Currency {
	return NewCurrency(new(big.Int).Div(RootDepth.Int(), t.Int()))
}

// Int converts a Target to a big.Int.
func (t Target) Int() *big.Int {
	return new(big.Int).SetBytes(t[:])
}

// IntToTarget converts a big.Int to a Target. Negative inputs trigger a panic.
func IntToTarget(i *big.Int) (t Target) {
	// Check for negatives.
	if i.Sign() < 0 {
		if build.DEBUG {
			panic(ErrNegativeTarget)
		}
	} else {
		// In the event of overflow, return the maximum.
		if i.BitLen() > 256 {
			return RootDepth
		}
		b := i.Bytes()
		offset := len(t[:]) - len(b)
		copy(t[offset:], b)
	}
	return
}

// Inverse returns the inverse of a Target as a big.Rat
func (t Target) Inverse() *big.Rat {
	return new(big.Rat).Inv(t.Rat())
}

// Mul multiplies the difficulty of a target by y. The product is defined by:
//		y / x
func (x Target) MulDifficulty(y *big.Rat) (t Target) {
	product := new(big.Rat).Mul(y, x.Inverse())
	product = product.Inv(product)
	return RatToTarget(product)
}

// Rat converts a Target to a big.Rat.
func (t Target) Rat() *big.Rat {
	return new(big.Rat).SetInt(t.Int())
}

// RatToTarget converts a big.Rat to a Target.
func RatToTarget(r *big.Rat) (t Target) {
	if r.Num().Sign() < 0 {
		if build.DEBUG {
			panic(ErrNegativeTarget)
		}
	} else {
		i := new(big.Int).Div(r.Num(), r.Denom())
		t = IntToTarget(i)
	}
	return
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	golog "log"
	"os"
	"os/signal"
	"os/user"
	"runtime"
	"syscall"
	"time"

	"github.com/percona/pmm/proto"
	pc "github.com/percona/pmm/proto/config"
	"github.com/percona/qan-agent/agent"
	"github.com/percona/qan-agent/agent/release"
	"github.com/percona/qan-agent/client"
	"github.com/percona/qan-agent/data"
	"github.com/percona/qan-agent/instance"
	"github.com/percona/qan-agent/log"
	"github.com/percona/qan-agent/mrms"
	"github.com/percona/qan-agent/mysql"
	"github.com/percona/qan-agent/pct"
	pctCmd "github.com/percona/qan-agent/pct/cmd"
	"github.com/percona/qan-agent/qan"
	qanFactory "github.com/percona/qan-agent/qan/factory"
	"github.com/percona/qan-agent/qan/perfschema"
	"github.com/percona/qan-agent/qan/slowlog"
	"github.com/percona/qan-agent/query"
	"github.com/percona/qan-agent/ticker"
)

var (
	flagBasedir string
	flagPidFile string
	flagListen  string
	flagPing    bool
	flagVersion bool
)

func init() {
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(1)
	}

	golog.SetFlags(golog.Ldate | golog.Ltime | golog.Lmicroseconds | golog.Lshortfile)
	golog.SetOutput(os.Stdout)

	flag.StringVar(&flagBasedir, "basedir", pct.DEFAULT_BASEDIR, "Agent basedir")
	flag.StringVar(&flagPidFile, "pid-file", agent.DEFAULT_PIDFILE, "PID file")
	flag.StringVar(&flagListen, "listen", agent.DEFAULT_LISTEN, "Agent interface address")
	flag.BoolVar(&flagPing, "ping", false, "Ping API")
	flag.BoolVar(&flagVersion, "version", false, "Print version")

	flag.Parse()

	// We don't accept any possitional arguments
	if len(flag.Args()) != 0 {
		flag.Usage()
		os.Exit(1)
	}
}

func main() {
	// //////////////////////////////////////////////////////////////////////
	// Handle options like -version which don't start the agent.
	// //////////////////////////////////////////////////////////////////////

	agentVersion := fmt.Sprintf("percona-qan-agent %s", release.VERSION)
	if flagVersion {
		fmt.Println(agentVersion)
		return
	}

	if err := pct.Basedir.Init(flagBasedir); err != nil {
		fmt.Printf("Error initializing basedir %s: %s", flagBasedir, err)
		os.Exit(1)
	}

	// Read agent.conf to get API hostname and agent UUID.
	agentConfigFile := pct.Basedir.ConfigFile("agent")
	if !pct.FileExists(agentConfigFile) {
		fmt.Printf("Agent config file %s does not exist", agentConfigFile)
		os.Exit(1)
	}

	bytes, err := agent.LoadConfig()
	if err != nil {
		fmt.Printf("Error reading agent config file %s: %s", agentConfigFile, err)
		os.Exit(1)
	}
	agentConfig := &pc.Agent{}
	if err := json.Unmarshal(bytes, agentConfig); err != nil {
		fmt.Printf("Error decoding agent config file %s: %s", agentConfigFile, err)
		os.Exit(1)
	}

	apiURL := agentConfig.ApiHostname + agentConfig.ApiPath
	fmt.Printf("# Version: %s\n", agentVersion)
	fmt.Printf("# Basedir: %s\n", pct.Basedir.Path())
	fmt.Printf("# Listen:  %s\n", flagListen)
	fmt.Printf("# PID:     %d\n", os.Getpid())
	fmt.Printf("# API:     %s\n", apiURL)
	fmt.Printf("# UUID:    %s\n", agentConfig.UUID)

	// -ping and exit.
	if flagPing {
		t0 := time.Now()
		code, err := pct.Ping(apiURL, nil)
		d := time.Now().Sub(t0)
		if err != nil || code != 200 {
			fmt.Printf("Ping FAIL (%d %d %s)", d, code, err)
			os.Exit(1)
		}
		fmt.Printf("Ping OK (%s)", d)
		return
	}

	// //////////////////////////////////////////////////////////////////////
	// Run the agent
	// //////////////////////////////////////////////////////////////////////

	// Create PID file or die trying.
	pidFile := pct.NewPidFile()
	if err := pidFile.Set(flagPidFile); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	err = run(agentConfig) // run the agent

	pidFile.Remove() // always remove PID file

	if err != nil {
		golog.Println(err)
		os.Exit(1)
	}
}

func run(agentConfig *pc.Agent) error {
	golog.Println("Starting agent...")
	var stopErr error
	defer func() {
		if stopErr == nil {
			golog.Println("Agent has stopped")
		}
	}()

	// //////////////////////////////////////////////////////////////////////
	// Internal services, factories, and other dependencies
	// //////////////////////////////////////////////////////////////////////
	pctCmd.Factory = &pctCmd.RealCmdFactory{}
	connFactory := &mysql.RealConnectionFactory{}
	nowFunc := func() int64 { return time.Now().UTC().UnixNano() }
	clock := ticker.NewClock(&ticker.RealTickerFactory{}, nowFunc)

	// //////////////////////////////////////////////////////////////////////
	// Start API interface
	// //////////////////////////////////////////////////////////////////////

	// The API interface provides low-level functionality to websocket clients.
	// To be useful, it must connect once to get resource links. Do this async
	// so in case we're offline the agent still starts and collects data. We
	// can spool and send data later when API is online.
	api := pct.NewAPI(agentConfig.ServerUser, agentConfig.ServerPassword, agentConfig.ServerSSL, agentConfig.ServerInsecureSSL)
	go func() {
		haveWarned := false
		for {
			if err := api.Connect(agentConfig.ApiHostname, agentConfig.ApiPath, agentConfig.UUID); err != nil {
				if !haveWarned {
					golog.Printf("Cannot connect to API: %s. Verify that the"+
						" agent UUID and API hostname printed above are"+
						" correct and that no network issues prevent this"+
						" host from accessing the API. Connection"+
						" attempts to API will continue until successful, but"+
						" additional errors will not be logged, and agent"+
						" will not send data until connected to API.", err)
					haveWarned = true // don't flood log in case we're offline for a long time
				}
				time.Sleep(3 * time.Second)
				continue
			}
			golog.Println("API is ready")
			return
		}
	}()

	// //////////////////////////////////////////////////////////////////////
	// Agent services
	// //////////////////////////////////////////////////////////////////////

	// Log relay
	logChan := make(chan proto.LogEntry, log.BUFFER_SIZE*2)
	logClient, err := client.NewWebsocketClient(pct.NewLogger(logChan, "log-ws"), api, "log", nil)
	if err != nil {
		return err
	}
	logManager := log.NewManager(
		logClient,
		logChan,
	)
	if err := logManager.Start(); err != nil {
		return fmt.Errorf("Error starting log manager: %s", err)
	}

	// MRMS (MySQL Restart Monitoring Service)
	mrmsMonitor := mrms.NewRealMonitor(
		pct.NewLogger(logChan, "mrms-monitor"),
		&mysql.RealConnectionFactory{},
	)
	mrmsManager := mrms.NewManager(
		pct.NewLogger(logChan, "mrms-manager"),
		mrmsMonitor,
	)
	if err := mrmsManager.Start(); err != nil {
		return fmt.Errorf("Error starting mrms manager: %s", err)
	}

	// Instance manager
	itManager := instance.NewManager(
		pct.NewLogger(logChan, "instance-manager"),
		pct.Basedir.Dir("instance"),
		api,
		mrmsMonitor,
	)
	if err := itManager.Start(); err != nil {
		return fmt.Errorf("Error starting instance manager: %s", err)
	}

	// Data spooler and sender
	hostname, _ := os.Hostname()
	dataClient, err := client.NewWebsocketClient(pct.NewLogger(logChan, "data-ws"), api, "data", nil)
	if err != nil {
		return err
	}
	dataManager := data.NewManager(
		pct.NewLogger(logChan, "data"),
		pct.Basedir.Dir("data"),
		pct.Basedir.Dir("trash"),
		hostname,
		dataClient,
	)
	if err := dataManager.Start(); err != nil {
		return fmt.Errorf("Error starting data manager: %s", err)
	}

	// Query (real-time EXPLAIN, SHOW CREATE TABLE, etc.)
	queryManager := query.NewManager(
		pct.NewLogger(logChan, "query"),
		itManager.Repo(),
		&mysql.RealConnectionFactory{},
	)
	if err := queryManager.Start(); err != nil {
		return fmt.Errorf("Error starting query manager: %s", err)
	}

	// Query Analytics
	qanManager := qan.NewManager(
		pct.NewLogger(logChan, "qan"),
		clock,
		itManager.Repo(),
		mrmsMonitor,
		connFactory,
		qanFactory.NewRealAnalyzerFactory(
			logChan,
			qanFactory.NewRealIntervalIterFactory(logChan),
			slowlog.NewRealWorkerFactory(logChan),
			perfschema.NewRealWorkerFactory(logChan),
			dataManager.Spooler(),
			clock,
		),
	)
	if err := qanManager.Start(); err != nil {
		return fmt.Errorf("Error starting qan manager: %s", err)
	}

	// //////////////////////////////////////////////////////////////////////
	// Create and start the agent
	// //////////////////////////////////////////////////////////////////////

	cmdClient, err := client.NewWebsocketClient(pct.NewLogger(logChan, "agent-ws"), api, "cmd", nil)
	if err != nil {
		return err
	}
	agentLogger := pct.NewLogger(logChan, "agent")
	agentRouter := agent.NewAgent(
		agentConfig,
		agentLogger,
		cmdClient,
		flagListen,
		map[string]pct.ServiceManager{ // agent services
			"log":      logManager,
			"data":     dataManager,
			"qan":      qanManager,
			"instance": itManager,
			"mrms":     mrmsManager,
			"query":    queryManager,
		},
	)

	// Run the agent, wait for it to stop, signal, or crash.
	stopChan := make(chan error, 2)
	go func() {
		defer func() {
			if err := recover(); err != nil {
				errMsg := fmt.Sprintf("Agent crashed: %s", err)
				golog.Println(errMsg)
				agentLogger.Error(errMsg)
				stopChan <- fmt.Errorf("%s", errMsg)
			}
		}()
		stopChan <- agentRouter.Run() // ----- RUN THE AGENT -----
	}()

	// Run local agent API.
	lo := agent.NewLocalInterface(flagListen, agentRouter, itManager.Repo())
	go lo.Run()

	golog.Println("Agent is ready")

	// //////////////////////////////////////////////////////////////////////
	// Signal handlers
	// //////////////////////////////////////////////////////////////////////

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)

	reconnectSigChan := make(chan os.Signal, 1)
	signal.Notify(reconnectSigChan, syscall.SIGHUP) // kill -HUP PID

	intSigChan := make(chan os.Signal, 1)
	signal.Notify(intSigChan, syscall.SIGINT) // CTRL-C

	// //////////////////////////////////////////////////////////////////////
	// Wait for agent stop, signals, etc.
	// //////////////////////////////////////////////////////////////////////

SIGNAL_LOOP:
	for {
		select {
		case stopErr = <-stopChan:
			// stopErr is logged in defer
			break SIGNAL_LOOP // stop running
		case sig := <-sigChan:
			msg := fmt.Sprintf("Caught %s signal, shutting down", sig)
			agentLogger.Info(msg)
			golog.Println(msg)
			break SIGNAL_LOOP // stop running
		case <-intSigChan:
			msg := "Caught CTRL-C (SIGINT), shutting down"
			agentLogger.Warn(msg)
			golog.Println(msg)
			break SIGNAL_LOOP // stop running
		case <-reconnectSigChan:
			u, _ := user.Current()
			cmd := &proto.Cmd{
				Ts:        time.Now().UTC(),
				User:      u.Username + " (SIGHUP)",
				AgentUUID: agentConfig.UUID,
				Service:   "agent",
				Cmd:       "Reconnect",
			}
			agentRouter.Handle(cmd)
		}
	}

	// //////////////////////////////////////////////////////////////////////
	// Clean up, undo any changes we made to MySQL.
	/////////////////////////////////////////////////////////////////////////

	// Disable slow log or perf schema. It's not terrible if we don't, but
	// it's a lot better if we do.
	golog.Println("Stopping QAN...")
	if err := qanManager.Stop(); err != nil {
		msg := fmt.Sprintf("Cannot stop QAN: %s", err)
		agentLogger.Warn(msg)
		golog.Printf(msg)
	}

	golog.Println("Waiting 2 seconds to flush agent log to API...")
	time.Sleep(2 * time.Second) // wait for last replies and log entries

	return stopErr
}
<commit_msg>PID file is not required.<commit_after>/*
   Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	golog "log"
	"os"
	"os/signal"
	"os/user"
	"runtime"
	"syscall"
	"time"

	"github.com/percona/pmm/proto"
	pc "github.com/percona/pmm/proto/config"
	"github.com/percona/qan-agent/agent"
	"github.com/percona/qan-agent/agent/release"
	"github.com/percona/qan-agent/client"
	"github.com/percona/qan-agent/data"
	"github.com/percona/qan-agent/instance"
	"github.com/percona/qan-agent/log"
	"github.com/percona/qan-agent/mrms"
	"github.com/percona/qan-agent/mysql"
	"github.com/percona/qan-agent/pct"
	pctCmd "github.com/percona/qan-agent/pct/cmd"
	"github.com/percona/qan-agent/qan"
	qanFactory "github.com/percona/qan-agent/qan/factory"
	"github.com/percona/qan-agent/qan/perfschema"
	"github.com/percona/qan-agent/qan/slowlog"
	"github.com/percona/qan-agent/query"
	"github.com/percona/qan-agent/ticker"
)

var (
	flagBasedir string
	flagPidFile string
	flagListen  string
	flagPing    bool
	flagVersion bool
)

func init() {
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(1)
	}

	golog.SetFlags(golog.Ldate | golog.Ltime | golog.Lmicroseconds | golog.Lshortfile)
	golog.SetOutput(os.Stdout)

	flag.StringVar(&flagBasedir, "basedir", pct.DEFAULT_BASEDIR, "Agent basedir")
	flag.StringVar(&flagPidFile, "pid-file", "", "PID file")
	flag.StringVar(&flagListen, "listen", agent.DEFAULT_LISTEN, "Agent interface address")
	flag.BoolVar(&flagPing, "ping", false, "Ping API")
	flag.BoolVar(&flagVersion, "version", false, "Print version")

	flag.Parse()

	// We don't accept any possitional arguments
	if len(flag.Args()) != 0 {
		flag.Usage()
		os.Exit(1)
	}
}

func main() {
	// //////////////////////////////////////////////////////////////////////
	// Handle options like -version which don't start the agent.
	// //////////////////////////////////////////////////////////////////////

	agentVersion := fmt.Sprintf("percona-qan-agent %s", release.VERSION)
	if flagVersion {
		fmt.Println(agentVersion)
		return
	}

	if err := pct.Basedir.Init(flagBasedir); err != nil {
		fmt.Printf("Error initializing basedir %s: %s", flagBasedir, err)
		os.Exit(1)
	}

	// Read agent.conf to get API hostname and agent UUID.
	agentConfigFile := pct.Basedir.ConfigFile("agent")
	if !pct.FileExists(agentConfigFile) {
		fmt.Printf("Agent config file %s does not exist", agentConfigFile)
		os.Exit(1)
	}

	bytes, err := agent.LoadConfig()
	if err != nil {
		fmt.Printf("Error reading agent config file %s: %s", agentConfigFile, err)
		os.Exit(1)
	}
	agentConfig := &pc.Agent{}
	if err := json.Unmarshal(bytes, agentConfig); err != nil {
		fmt.Printf("Error decoding agent config file %s: %s", agentConfigFile, err)
		os.Exit(1)
	}

	apiURL := agentConfig.ApiHostname + agentConfig.ApiPath
	fmt.Printf("# Version: %s\n", agentVersion)
	fmt.Printf("# Basedir: %s\n", pct.Basedir.Path())
	fmt.Printf("# Listen:  %s\n", flagListen)
	fmt.Printf("# PID:     %d\n", os.Getpid())
	fmt.Printf("# API:     %s\n", apiURL)
	fmt.Printf("# UUID:    %s\n", agentConfig.UUID)

	// -ping and exit.
	if flagPing {
		t0 := time.Now()
		code, err := pct.Ping(apiURL, nil)
		d := time.Now().Sub(t0)
		if err != nil || code != 200 {
			fmt.Printf("Ping FAIL (%d %d %s)", d, code, err)
			os.Exit(1)
		}
		fmt.Printf("Ping OK (%s)", d)
		return
	}

	// //////////////////////////////////////////////////////////////////////
	// Run the agent
	// //////////////////////////////////////////////////////////////////////

	// Create PID file or die trying.
	var pidFile *pct.PidFile
	if flagPidFile != "" {
		pidFile = pct.NewPidFile()
		if err := pidFile.Set(flagPidFile); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}

	err = run(agentConfig) // run the agent

	if flagPidFile != "" {
		pidFile.Remove() // always remove PID file
	}

	if err != nil {
		golog.Println(err)
		os.Exit(1)
	}
}

func run(agentConfig *pc.Agent) error {
	golog.Println("Starting agent...")
	var stopErr error
	defer func() {
		if stopErr == nil {
			golog.Println("Agent has stopped")
		}
	}()

	// //////////////////////////////////////////////////////////////////////
	// Internal services, factories, and other dependencies
	// //////////////////////////////////////////////////////////////////////
	pctCmd.Factory = &pctCmd.RealCmdFactory{}
	connFactory := &mysql.RealConnectionFactory{}
	nowFunc := func() int64 { return time.Now().UTC().UnixNano() }
	clock := ticker.NewClock(&ticker.RealTickerFactory{}, nowFunc)

	// //////////////////////////////////////////////////////////////////////
	// Start API interface
	// //////////////////////////////////////////////////////////////////////

	// The API interface provides low-level functionality to websocket clients.
	// To be useful, it must connect once to get resource links. Do this async
	// so in case we're offline the agent still starts and collects data. We
	// can spool and send data later when API is online.
	api := pct.NewAPI(agentConfig.ServerUser, agentConfig.ServerPassword, agentConfig.ServerSSL, agentConfig.ServerInsecureSSL)
	go func() {
		haveWarned := false
		for {
			if err := api.Connect(agentConfig.ApiHostname, agentConfig.ApiPath, agentConfig.UUID); err != nil {
				if !haveWarned {
					golog.Printf("Cannot connect to API: %s. Verify that the"+
						" agent UUID and API hostname printed above are"+
						" correct and that no network issues prevent this"+
						" host from accessing the API. Connection"+
						" attempts to API will continue until successful, but"+
						" additional errors will not be logged, and agent"+
						" will not send data until connected to API.", err)
					haveWarned = true // don't flood log in case we're offline for a long time
				}
				time.Sleep(3 * time.Second)
				continue
			}
			golog.Println("API is ready")
			return
		}
	}()

	// //////////////////////////////////////////////////////////////////////
	// Agent services
	// //////////////////////////////////////////////////////////////////////

	// Log relay
	logChan := make(chan proto.LogEntry, log.BUFFER_SIZE*2)
	logClient, err := client.NewWebsocketClient(pct.NewLogger(logChan, "log-ws"), api, "log", nil)
	if err != nil {
		return err
	}
	logManager := log.NewManager(
		logClient,
		logChan,
	)
	if err := logManager.Start(); err != nil {
		return fmt.Errorf("Error starting log manager: %s", err)
	}

	// MRMS (MySQL Restart Monitoring Service)
	mrmsMonitor := mrms.NewRealMonitor(
		pct.NewLogger(logChan, "mrms-monitor"),
		&mysql.RealConnectionFactory{},
	)
	mrmsManager := mrms.NewManager(
		pct.NewLogger(logChan, "mrms-manager"),
		mrmsMonitor,
	)
	if err := mrmsManager.Start(); err != nil {
		return fmt.Errorf("Error starting mrms manager: %s", err)
	}

	// Instance manager
	itManager := instance.NewManager(
		pct.NewLogger(logChan, "instance-manager"),
		pct.Basedir.Dir("instance"),
		api,
		mrmsMonitor,
	)
	if err := itManager.Start(); err != nil {
		return fmt.Errorf("Error starting instance manager: %s", err)
	}

	// Data spooler and sender
	hostname, _ := os.Hostname()
	dataClient, err := client.NewWebsocketClient(pct.NewLogger(logChan, "data-ws"), api, "data", nil)
	if err != nil {
		return err
	}
	dataManager := data.NewManager(
		pct.NewLogger(logChan, "data"),
		pct.Basedir.Dir("data"),
		pct.Basedir.Dir("trash"),
		hostname,
		dataClient,
	)
	if err := dataManager.Start(); err != nil {
		return fmt.Errorf("Error starting data manager: %s", err)
	}

	// Query (real-time EXPLAIN, SHOW CREATE TABLE, etc.)
	queryManager := query.NewManager(
		pct.NewLogger(logChan, "query"),
		itManager.Repo(),
		&mysql.RealConnectionFactory{},
	)
	if err := queryManager.Start(); err != nil {
		return fmt.Errorf("Error starting query manager: %s", err)
	}

	// Query Analytics
	qanManager := qan.NewManager(
		pct.NewLogger(logChan, "qan"),
		clock,
		itManager.Repo(),
		mrmsMonitor,
		connFactory,
		qanFactory.NewRealAnalyzerFactory(
			logChan,
			qanFactory.NewRealIntervalIterFactory(logChan),
			slowlog.NewRealWorkerFactory(logChan),
			perfschema.NewRealWorkerFactory(logChan),
			dataManager.Spooler(),
			clock,
		),
	)
	if err := qanManager.Start(); err != nil {
		return fmt.Errorf("Error starting qan manager: %s", err)
	}

	// //////////////////////////////////////////////////////////////////////
	// Create and start the agent
	// //////////////////////////////////////////////////////////////////////

	cmdClient, err := client.NewWebsocketClient(pct.NewLogger(logChan, "agent-ws"), api, "cmd", nil)
	if err != nil {
		return err
	}
	agentLogger := pct.NewLogger(logChan, "agent")
	agentRouter := agent.NewAgent(
		agentConfig,
		agentLogger,
		cmdClient,
		flagListen,
		map[string]pct.ServiceManager{ // agent services
			"log":      logManager,
			"data":     dataManager,
			"qan":      qanManager,
			"instance": itManager,
			"mrms":     mrmsManager,
			"query":    queryManager,
		},
	)

	// Run the agent, wait for it to stop, signal, or crash.
	stopChan := make(chan error, 2)
	go func() {
		defer func() {
			if err := recover(); err != nil {
				errMsg := fmt.Sprintf("Agent crashed: %s", err)
				golog.Println(errMsg)
				agentLogger.Error(errMsg)
				stopChan <- fmt.Errorf("%s", errMsg)
			}
		}()
		stopChan <- agentRouter.Run() // ----- RUN THE AGENT -----
	}()

	// Run local agent API.
	lo := agent.NewLocalInterface(flagListen, agentRouter, itManager.Repo())
	go lo.Run()

	golog.Println("Agent is ready")

	// //////////////////////////////////////////////////////////////////////
	// Signal handlers
	// //////////////////////////////////////////////////////////////////////

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)

	reconnectSigChan := make(chan os.Signal, 1)
	signal.Notify(reconnectSigChan, syscall.SIGHUP) // kill -HUP PID

	intSigChan := make(chan os.Signal, 1)
	signal.Notify(intSigChan, syscall.SIGINT) // CTRL-C

	// //////////////////////////////////////////////////////////////////////
	// Wait for agent stop, signals, etc.
	// //////////////////////////////////////////////////////////////////////

SIGNAL_LOOP:
	for {
		select {
		case stopErr = <-stopChan:
			// stopErr is logged in defer
			break SIGNAL_LOOP // stop running
		case sig := <-sigChan:
			msg := fmt.Sprintf("Caught %s signal, shutting down", sig)
			agentLogger.Info(msg)
			golog.Println(msg)
			break SIGNAL_LOOP // stop running
		case <-intSigChan:
			msg := "Caught CTRL-C (SIGINT), shutting down"
			agentLogger.Warn(msg)
			golog.Println(msg)
			break SIGNAL_LOOP // stop running
		case <-reconnectSigChan:
			u, _ := user.Current()
			cmd := &proto.Cmd{
				Ts:        time.Now().UTC(),
				User:      u.Username + " (SIGHUP)",
				AgentUUID: agentConfig.UUID,
				Service:   "agent",
				Cmd:       "Reconnect",
			}
			agentRouter.Handle(cmd)
		}
	}

	// //////////////////////////////////////////////////////////////////////
	// Clean up, undo any changes we made to MySQL.
	/////////////////////////////////////////////////////////////////////////

	// Disable slow log or perf schema. It's not terrible if we don't, but
	// it's a lot better if we do.
	golog.Println("Stopping QAN...")
	if err := qanManager.Stop(); err != nil {
		msg := fmt.Sprintf("Cannot stop QAN: %s", err)
		agentLogger.Warn(msg)
		golog.Printf(msg)
	}

	golog.Println("Waiting 2 seconds to flush agent log to API...")
	time.Sleep(2 * time.Second) // wait for last replies and log entries

	return stopErr
}
<|endoftext|>


<commit_before>package proxy

import "net/http"

import (
	"testing"
	//"bytes"
	log "github.com/Sirupsen/logrus"
	"github.com/vulcand/oxy/testutils"
	"io/ioutil"
	"net/http/httptest"
)

//func TestTrace(t *testing.T) { TestingT(t) }

type TraceSuite struct{}

//var _ = Suite(&TraceSuite{})

func TestNextHandlerWorks(t *testing.T) {
	log.SetLevel(log.DebugLevel)
	//create some handler
	nextHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Content-Length", "5")
		w.Write([]byte("hello"))
	})

	teeHandler, _ := New(nextHandler)

	//trace := &bytes.Buffer{}
	//tee, err := New(teeHandler)

	srv := httptest.NewServer(teeHandler)
	defer srv.Close()

	res, _, err := testutils.MakeRequest(srv.URL+"/db1", testutils.Method("POST"), testutils.Body("123456"))

	if res.StatusCode != http.StatusOK || err != nil {
		t.Fatal("Error posting")
	}

	if res.Header.Get("Content-Length") != "5" {
		t.Error("Content-Length did not pass")
	}

	//buf := &bytes.Buffer{}
	//res.Body.Read(buf)
	//res.Body.Read()
	body, err := ioutil.ReadAll(res.Body)
	log.Debug(body)
}
<commit_msg>impl tee_test<commit_after>package proxy

import "net/http"

import (
	"testing"
	//"bytes"
	log "github.com/Sirupsen/logrus"
	"github.com/vulcand/oxy/testutils"
	"io/ioutil"
	"net/http/httptest"
	"os"

)

//func TestTrace(t *testing.T) { TestingT(t) }

type TeeSuite struct{
	generalHandler  http.HandlerFunc
	server		*httptest.Server
}

//var _ = Suite(&TeeSuite{})
func TestMain(m *testing.M) {
	log.SetLevel(log.DebugLevel)

	// call flag.Parse() here if TestMain uses flags
	os.Exit(m.Run())
}

func TestNextHandlerWorks(t *testing.T) {

	//create some handler
	nextHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Content-Length", "5")
		w.Write([]byte("hello"))
	})

	teeHandler, _ := New(nextHandler)

	//trace := &bytes.Buffer{}
	//tee, err := New(teeHandler)

	srv := httptest.NewServer(teeHandler)
	defer srv.Close()

	res, _, err := testutils.MakeRequest(srv.URL+"/db1", testutils.Method("POST"), testutils.Body("123456"))

	if res.StatusCode != http.StatusOK || err != nil {
		t.Fatal("Error posting")
	}

	if res.Header.Get("Content-Length") != "5" {
		t.Error("Content-Length did not pass")
	}

	//buf := &bytes.Buffer{}
	//res.Body.Read(buf)
	//res.Body.Read()
	body, err := ioutil.ReadAll(res.Body)
	log.Debug(body)
}

func (ts *TeeSuite) TestCopyRequest(t *testing.T)  {
	//res, _, err := testutils.MakeRequest(srv.URL+"/db1", testutils.Method("POST"), testutils.Body("123456"))
}
<|endoftext|>


<commit_before>package sns

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/sns"
)

// Message Attributes.
const (
	AttributeEnabled            = "Enabled"
	AttributeToken              = "Token"
	AttributeEndpointCreated    = "EventEndpointCreated"
	AttributeEndpointDeleted    = "EventEndpointDeleted"
	AttributeEndpointUpdated    = "EventEndpointUpdated"
	AttributeDeliveryFailure    = "EventDeliveryFailure"
	AttributePlatformCredential = "PlatformCredential"
	AttributePlatformPrincipal  = "PlatformPrincipal"
)

// Message Types.
const (
	TypeDeliveryFailure = "DeliveryFailure"
	TypeNotification    = "Notification"
)

// Platform supported by SNS for push.
const (
	PlatformAPNSSandbox Platform = iota + 1
	PlatformAPNS
	PlatformGCM
)

// Publish structures.
const (
	structureJSON = "json"
)

// Push formats.
const (
	fmtURN         = `%s://%s`
	msgAPNS        = `{"APNS": "{\"aps\": {\"alert\": \"%s\"}, \"urn\":\"%s\"}" }`
	msgAPNSSandbox = `{"APNS_SANDBOX": "{\"aps\": {\"alert\": \"%s\"}, \"urn\":\"%s\"}" }`
	msgGCM         = `{"GCM": "{\"notification\": {\"title\": \"%s\", \"data\": {\"urn\": \"%s\"}} }"}`
)

// PlatformIdentifiers helps to map Platfrom to human-readable strings.
var PlatformIdentifiers = map[Platform]string{
	PlatformAPNS:        "APNS",
	PlatformAPNSSandbox: "APNS_SANDBOX",
	PlatformGCM:         "GCM",
}

// API bundles common SNS interactions in a reasonably sized interface.
type API interface {
	CreatePlatformApplication(*sns.CreatePlatformApplicationInput) (*sns.CreatePlatformApplicationOutput, error)
	CreatePlatformEndpoint(*sns.CreatePlatformEndpointInput) (*sns.CreatePlatformEndpointOutput, error)
	GetEndpointAttributes(*sns.GetEndpointAttributesInput) (*sns.GetEndpointAttributesOutput, error)
	SetEndpointAttributes(*sns.SetEndpointAttributesInput) (*sns.SetEndpointAttributesOutput, error)
	Publish(*sns.PublishInput) (*sns.PublishOutput, error)
}

// Endpoint is the AWS SNS representation of a Device.
type Endpoint struct {
	ARN   string
	Token string
}

// Platform of a device.
type Platform uint8

// AppCreateAPNSFunc creates a new platform application for APNS production.
type AppCreateAPNSFunc func(name, cert, key string) (string, error)

// AppCreateAPNS creates a new platform application for APNS production.
func AppCreateAPNS(api API, changeTopic string) AppCreateAPNSFunc {
	return func(name, cert, key string) (string, error) {
		return createPlatformApp(
			api,
			PlatformAPNS,
			name,
			changeTopic,
			map[string]*string{
				AttributePlatformCredential: aws.String(key),
				AttributePlatformPrincipal:  aws.String(cert),
			},
		)
	}
}

// AppCreateAPNSSandboxFunc creates a new platform application for APNS sandbox.
type AppCreateAPNSSandboxFunc func(name, cert, key string) (string, error)

// AppCreateAPNSSandbox creates a new platform application for APNS sandbox.
func AppCreateAPNSSandbox(api API, changeTopic string) AppCreateAPNSSandboxFunc {
	return func(name, cert, key string) (string, error) {
		return createPlatformApp(
			api,
			PlatformAPNSSandbox,
			name,
			changeTopic,
			map[string]*string{
				AttributePlatformCredential: aws.String(key),
				AttributePlatformPrincipal:  aws.String(cert),
			},
		)
	}
}

// AppCreateGCMFunc creates a new platform application for GCM.
type AppCreateGCMFunc func(name, key string) (string, error)

// AppCreateGCM creates a new platform application for GCM.
func AppCreateGCM(api API, changeTopic string) AppCreateGCMFunc {
	return func(name, key string) (string, error) {
		return createPlatformApp(
			api,
			PlatformGCM,
			name,
			changeTopic,
			map[string]*string{
				AttributePlatformCredential: aws.String(key),
			},
		)
	}
}

// EndpointCreateFunc registers a new device endpoint for the given platform
// and token.
type EndpointCreateFunc func(platformARN, token string) (*Endpoint, error)

// EndpointCreate registers a new device endpoint for the given platform and
// token.
func EndpointCreate(api API) EndpointCreateFunc {
	return func(platformARN, token string) (*Endpoint, error) {
		r, err := api.CreatePlatformEndpoint(&sns.CreatePlatformEndpointInput{
			PlatformApplicationArn: aws.String(platformARN),
			Token: aws.String(token),
		})
		if err != nil {
			return nil, err
		}

		return &Endpoint{
			ARN:   *r.EndpointArn,
			Token: token,
		}, nil
	}
}

// EndpointRetrieveFunc returns the Endpoint for the given ARN.
type EndpointRetrieveFunc func(arn string) (*Endpoint, error)

// EndpointRetrieve returns the Endpoint for the given ARN.
func EndpointRetrieve(api API) EndpointRetrieveFunc {
	return func(arn string) (*Endpoint, error) {
		r, err := api.GetEndpointAttributes(
			&sns.GetEndpointAttributesInput{
				EndpointArn: aws.String(arn),
			},
		)
		if err != nil {
			if awsErr, ok := err.(awserr.RequestFailure); ok &&
				awsErr.StatusCode() == 404 {
				return nil, ErrEndpointNotFound
			}

			return nil, err
		}

		if *r.Attributes[AttributeEnabled] == "false" {
			return nil, ErrEndpointDisabled
		}

		return &Endpoint{
			ARN:   arn,
			Token: *r.Attributes[AttributeToken],
		}, nil
	}
}

// EndpointUpdateFunc takes a new token and stores it with the Endpoint.
type EndpointUpdateFunc func(arn, token string) (*Endpoint, error)

// EndpointUpdate takes a new token and stores it with the Endpoint.
func EndpointUpdate(api API) EndpointUpdateFunc {
	return func(arn, token string) (*Endpoint, error) {
		_, err := api.SetEndpointAttributes(&sns.SetEndpointAttributesInput{
			Attributes: map[string]*string{
				AttributeToken: aws.String(token),
			},
			EndpointArn: aws.String(arn),
		})
		if err != nil {
			return nil, err
		}

		return &Endpoint{
			ARN:   arn,
			Token: token,
		}, nil
	}
}

// PushFunc pushes a new notification to the device for the given endpoint ARN.
type PushFunc func(
	platform Platform,
	endpointARN, scheme, urn, message string,
) error

// Push pushes a new notification to the device for the given endpoint ARN.
func Push(api API) PushFunc {
	return func(p Platform, arn, scheme, urn, message string) error {
		fmtMsg := ""

		switch p {
		case PlatformAPNS:
			fmtMsg = msgAPNS
		case PlatformAPNSSandbox:
			fmtMsg = msgAPNSSandbox
		case PlatformGCM:
			fmtMsg = msgGCM
		}

		var (
			u = fmt.Sprintf(fmtURN, scheme, urn)
			m = fmt.Sprintf(fmtMsg, message, u)
		)

		_, err := api.Publish(&sns.PublishInput{
			Message:          aws.String(m),
			MessageStructure: aws.String(structureJSON),
			TargetArn:        aws.String(arn),
		})
		if err != nil {
			if awsErr, ok := err.(awserr.RequestFailure); ok {
				if awsErr.StatusCode() == 400 {
					return ErrDeliveryFailure
				}
			}

			return err
		}

		return nil
	}
}

func createPlatformApp(
	api API,
	platform Platform,
	name, topic string,
	attr map[string]*string,
) (string, error) {
	attr[AttributeEndpointCreated] = aws.String(topic)
	attr[AttributeEndpointDeleted] = aws.String(topic)
	attr[AttributeEndpointUpdated] = aws.String(topic)
	attr[AttributeDeliveryFailure] = aws.String(topic)

	res, err := api.CreatePlatformApplication(&sns.CreatePlatformApplicationInput{
		Attributes: attr,
		Name:       aws.String(name),
		Platform:   aws.String(PlatformIdentifiers[platform]),
	})
	if err != nil {
		return "", err
	}

	return *res.PlatformApplicationArn, nil
}
<commit_msg>Update GCM mesasge structure<commit_after>package sns

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/sns"
)

// Message Attributes.
const (
	AttributeEnabled            = "Enabled"
	AttributeToken              = "Token"
	AttributeEndpointCreated    = "EventEndpointCreated"
	AttributeEndpointDeleted    = "EventEndpointDeleted"
	AttributeEndpointUpdated    = "EventEndpointUpdated"
	AttributeDeliveryFailure    = "EventDeliveryFailure"
	AttributePlatformCredential = "PlatformCredential"
	AttributePlatformPrincipal  = "PlatformPrincipal"
)

// Message Types.
const (
	TypeDeliveryFailure = "DeliveryFailure"
	TypeNotification    = "Notification"
)

// Platform supported by SNS for push.
const (
	PlatformAPNSSandbox Platform = iota + 1
	PlatformAPNS
	PlatformGCM
)

// Publish structures.
const (
	structureJSON = "json"
)

// Push formats.
const (
	fmtURN         = `%s://%s`
	msgAPNS        = `{"APNS": "{\"aps\": {\"alert\": \"%s\"}, \"urn\":\"%s\"}" }`
	msgAPNSSandbox = `{"APNS_SANDBOX": "{\"aps\": {\"alert\": \"%s\"}, \"urn\":\"%s\"}" }`
	msgGCM         = `{"GCM": "{\"data\": {\"title\": \"Social\", \"body\": \"%s\", \"urn\": \"%s\"} }" }`
)

// PlatformIdentifiers helps to map Platfrom to human-readable strings.
var PlatformIdentifiers = map[Platform]string{
	PlatformAPNS:        "APNS",
	PlatformAPNSSandbox: "APNS_SANDBOX",
	PlatformGCM:         "GCM",
}

// API bundles common SNS interactions in a reasonably sized interface.
type API interface {
	CreatePlatformApplication(*sns.CreatePlatformApplicationInput) (*sns.CreatePlatformApplicationOutput, error)
	CreatePlatformEndpoint(*sns.CreatePlatformEndpointInput) (*sns.CreatePlatformEndpointOutput, error)
	GetEndpointAttributes(*sns.GetEndpointAttributesInput) (*sns.GetEndpointAttributesOutput, error)
	SetEndpointAttributes(*sns.SetEndpointAttributesInput) (*sns.SetEndpointAttributesOutput, error)
	Publish(*sns.PublishInput) (*sns.PublishOutput, error)
}

// Endpoint is the AWS SNS representation of a Device.
type Endpoint struct {
	ARN   string
	Token string
}

// Platform of a device.
type Platform uint8

// AppCreateAPNSFunc creates a new platform application for APNS production.
type AppCreateAPNSFunc func(name, cert, key string) (string, error)

// AppCreateAPNS creates a new platform application for APNS production.
func AppCreateAPNS(api API, changeTopic string) AppCreateAPNSFunc {
	return func(name, cert, key string) (string, error) {
		return createPlatformApp(
			api,
			PlatformAPNS,
			name,
			changeTopic,
			map[string]*string{
				AttributePlatformCredential: aws.String(key),
				AttributePlatformPrincipal:  aws.String(cert),
			},
		)
	}
}

// AppCreateAPNSSandboxFunc creates a new platform application for APNS sandbox.
type AppCreateAPNSSandboxFunc func(name, cert, key string) (string, error)

// AppCreateAPNSSandbox creates a new platform application for APNS sandbox.
func AppCreateAPNSSandbox(api API, changeTopic string) AppCreateAPNSSandboxFunc {
	return func(name, cert, key string) (string, error) {
		return createPlatformApp(
			api,
			PlatformAPNSSandbox,
			name,
			changeTopic,
			map[string]*string{
				AttributePlatformCredential: aws.String(key),
				AttributePlatformPrincipal:  aws.String(cert),
			},
		)
	}
}

// AppCreateGCMFunc creates a new platform application for GCM.
type AppCreateGCMFunc func(name, key string) (string, error)

// AppCreateGCM creates a new platform application for GCM.
func AppCreateGCM(api API, changeTopic string) AppCreateGCMFunc {
	return func(name, key string) (string, error) {
		return createPlatformApp(
			api,
			PlatformGCM,
			name,
			changeTopic,
			map[string]*string{
				AttributePlatformCredential: aws.String(key),
			},
		)
	}
}

// EndpointCreateFunc registers a new device endpoint for the given platform
// and token.
type EndpointCreateFunc func(platformARN, token string) (*Endpoint, error)

// EndpointCreate registers a new device endpoint for the given platform and
// token.
func EndpointCreate(api API) EndpointCreateFunc {
	return func(platformARN, token string) (*Endpoint, error) {
		r, err := api.CreatePlatformEndpoint(&sns.CreatePlatformEndpointInput{
			PlatformApplicationArn: aws.String(platformARN),
			Token: aws.String(token),
		})
		if err != nil {
			return nil, err
		}

		return &Endpoint{
			ARN:   *r.EndpointArn,
			Token: token,
		}, nil
	}
}

// EndpointRetrieveFunc returns the Endpoint for the given ARN.
type EndpointRetrieveFunc func(arn string) (*Endpoint, error)

// EndpointRetrieve returns the Endpoint for the given ARN.
func EndpointRetrieve(api API) EndpointRetrieveFunc {
	return func(arn string) (*Endpoint, error) {
		r, err := api.GetEndpointAttributes(
			&sns.GetEndpointAttributesInput{
				EndpointArn: aws.String(arn),
			},
		)
		if err != nil {
			if awsErr, ok := err.(awserr.RequestFailure); ok &&
				awsErr.StatusCode() == 404 {
				return nil, ErrEndpointNotFound
			}

			return nil, err
		}

		if *r.Attributes[AttributeEnabled] == "false" {
			return nil, ErrEndpointDisabled
		}

		return &Endpoint{
			ARN:   arn,
			Token: *r.Attributes[AttributeToken],
		}, nil
	}
}

// EndpointUpdateFunc takes a new token and stores it with the Endpoint.
type EndpointUpdateFunc func(arn, token string) (*Endpoint, error)

// EndpointUpdate takes a new token and stores it with the Endpoint.
func EndpointUpdate(api API) EndpointUpdateFunc {
	return func(arn, token string) (*Endpoint, error) {
		_, err := api.SetEndpointAttributes(&sns.SetEndpointAttributesInput{
			Attributes: map[string]*string{
				AttributeToken: aws.String(token),
			},
			EndpointArn: aws.String(arn),
		})
		if err != nil {
			return nil, err
		}

		return &Endpoint{
			ARN:   arn,
			Token: token,
		}, nil
	}
}

// PushFunc pushes a new notification to the device for the given endpoint ARN.
type PushFunc func(
	platform Platform,
	endpointARN, scheme, urn, message string,
) error

// Push pushes a new notification to the device for the given endpoint ARN.
func Push(api API) PushFunc {
	return func(p Platform, arn, scheme, urn, message string) error {
		fmtMsg := ""

		switch p {
		case PlatformAPNS:
			fmtMsg = msgAPNS
		case PlatformAPNSSandbox:
			fmtMsg = msgAPNSSandbox
		case PlatformGCM:
			fmtMsg = msgGCM
		}

		var (
			u = fmt.Sprintf(fmtURN, scheme, urn)
			m = fmt.Sprintf(fmtMsg, message, u)
		)

		_, err := api.Publish(&sns.PublishInput{
			Message:          aws.String(m),
			MessageStructure: aws.String(structureJSON),
			TargetArn:        aws.String(arn),
		})
		if err != nil {
			if awsErr, ok := err.(awserr.RequestFailure); ok {
				if awsErr.StatusCode() == 400 {
					return ErrDeliveryFailure
				}
			}

			return err
		}

		return nil
	}
}

func createPlatformApp(
	api API,
	platform Platform,
	name, topic string,
	attr map[string]*string,
) (string, error) {
	attr[AttributeEndpointCreated] = aws.String(topic)
	attr[AttributeEndpointDeleted] = aws.String(topic)
	attr[AttributeEndpointUpdated] = aws.String(topic)
	attr[AttributeDeliveryFailure] = aws.String(topic)

	res, err := api.CreatePlatformApplication(&sns.CreatePlatformApplicationInput{
		Attributes: attr,
		Name:       aws.String(name),
		Platform:   aws.String(PlatformIdentifiers[platform]),
	})
	if err != nil {
		return "", err
	}

	return *res.PlatformApplicationArn, nil
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	xp "github.com/BurntSushi/xgb/xproto"
)

const (
	// wmKeysym is the key to trigger taowm actions. For other possible
	// values, such as xkSuperL for the 'Windows' key that is typically
	// between the left Control and Alt keys, see keysym.go.
	wmKeysym = xkCapsLock

	// colorXxx are taowm's text and border colors. We assume 24-bit RGB.
	colorBaseUnfocused  = 0x1f3f1f
	colorBaseFocused    = 0x3f7f3f
	colorPulseUnfocused = 0x3f7f3f
	colorPulseFocused   = 0x7fff7f
	colorQuitUnfocused  = 0x7f1f1f
	colorQuitFocused    = 0xff3f3f

	// fontXxx are the font metrics for X11's default font (fixed).
	// fontHeight1 is the vertical offset for the first line of text.
	fontHeight  = 16
	fontHeight1 = 9
	fontWidth   = 6

	// pulseXxx are the animation durations.
	pulseFrameDuration = 50 * time.Millisecond
	pulseTotalDuration = 1000 * time.Millisecond

	// quitDuration is the grace period, when quitting, for programs to exit
	// cleanly.
	quitDuration = 60 * time.Second
)

// xSettings is the key/value pairs to announce via the XSETTINGS mechanism.
// In particular, these include font and theme configuration parameters picked
// up by GTK+ programs such as gnome-terminal.
//
// The dump_xsettings program from http://code.google.com/p/xsettingsd/ will
// show the XSETTINGS key/value pairs set by other desktop environments such
// as GNOME.
//
// If this array is empty, then taowm will not try to own the XSETTINGS list,
// allowing another program such as gnome-settings-daemon to do so.
var xSettings = [...]struct {
	name  string
	value interface{}
}{
	{"Net/IconThemeName", "Tango"},
	{"Net/ThemeName", "Clearlooks"},
	{"Xft/Antialias", 1},
	{"Xft/DPI", 96 * 1024}, // Hard-code 96 DPI, the same as what gnome-settings-daemon does.
	{"Xft/Hinting", 1},
	{"Xft/HintStyle", "hintslight"},
	{"Xft/RGBA", "none"},
}

const doAudioActions = true

// actions lists the action to be performed for each key press. The do function
// returns whether to pulsate the frames' borders to acknowledge the key press.
//
// The map keys are X11 keysyms as int32s. The unary +/^ means whether the
// shift modifier needs to be absent/present.
var actions = map[int32]struct {
	do  func(*workspace, interface{}) bool
	arg interface{}
}{
	+' ':      {doExec, []string{"google-chrome"}},
	^' ':      {doExec, []string{"google-chrome", "--incognito"}},
	^'|':      {doExec, []string{"gnome-screensaver-command", "-l"}},
	+xkReturn: {doExec, []string{"gnome-terminal"}},
	^xkReturn: {doExec, []string{"dmenu_run", "-nb", "#0f0f0f", "-nf", "#3f7f3f",
		"-sb", "#0f0f0f", "-sf", "#7fff7f", "-l", "10"}},

	+xkAudioLowerVolume: {doAudio, []string{"pactl", "set-sink-volume", "0", "--", "-5%"}},
	+xkAudioRaiseVolume: {doAudio, []string{"pactl", "set-sink-volume", "0", "--", "+5%"}},
	+xkAudioMute:        {doAudio, []string{"pactl", "set-sink-mute", "0", "toggle"}},

	+xkBackspace: {doWindowDelete, nil},
	^xkEscape:    {doQuit, nil},

	+'`':          {doScreen, next},
	^'~':          {doScreen, prev},
	+xkTab:        {doFrame, next},
	^xkISOLeftTab: {doFrame, prev},

	+'q': {doList, listWorkspaces},
	+'w': {doWorkspaceMigrate, nil},
	+'e': {doWorkspace, prev},
	^'E': {doWorkspaceNudge, prev},
	+'r': {doWorkspace, next},
	^'R': {doWorkspaceNudge, next},
	+'t': {doWorkspaceNew, nil},
	^'T': {doWorkspaceDelete, nil},

	+'a': {doList, listWindows},
	+'s': {doWindowSelect, false},
	^'S': {doWindowSelect, true},
	+'d': {doWindow, prev},
	^'D': {doWindowNudge, prev},
	+'f': {doWindow, next},
	^'F': {doWindowNudge, next},
	+'g': {doFullscreen, nil},
	^'G': {doHide, nil},

	+'-': {doSplit, horizontal},
	+'=': {doSplit, vertical},
	^'+': {doMerge, nil},

	+'1': {doWindowN, 0},
	+'2': {doWindowN, 1},
	+'3': {doWindowN, 2},
	+'4': {doWindowN, 3},
	+'5': {doWindowN, 4},
	+'6': {doWindowN, 5},
	+'7': {doWindowN, 6},
	+'8': {doWindowN, 7},
	+'9': {doWindowN, 8},
	+'0': {doWindowN, 9},

	+xkF1:  {doWorkspaceN, 0},
	+xkF2:  {doWorkspaceN, 1},
	+xkF3:  {doWorkspaceN, 2},
	+xkF4:  {doWorkspaceN, 3},
	+xkF5:  {doWorkspaceN, 4},
	+xkF6:  {doWorkspaceN, 5},
	+xkF7:  {doWorkspaceN, 6},
	+xkF8:  {doWorkspaceN, 7},
	+xkF9:  {doWorkspaceN, 8},
	+xkF10: {doWorkspaceN, 9},
	+xkF11: {doWorkspaceN, 10},
	+xkF12: {doWorkspaceN, 11},

	+'i': {doSynthetic, xp.Button(4)},
	^'I': {doSynthetic, xp.Button(4)},
	+'m': {doSynthetic, xp.Button(5)},
	^'M': {doSynthetic, xp.Button(5)},
	+'y': {doSynthetic, xp.Keysym(xkHome)},
	^'Y': {doSynthetic, xp.Keysym(xkHome)},
	+'u': {doSynthetic, xp.Keysym(xkPageUp)},
	^'U': {doSynthetic, xp.Keysym(xkPageUp)},
	+'h': {doSynthetic, xp.Keysym(xkLeft)},
	^'H': {doSynthetic, xp.Keysym(xkLeft)},
	+'j': {doSynthetic, xp.Keysym(xkDown)},
	^'J': {doSynthetic, xp.Keysym(xkDown)},
	+'k': {doSynthetic, xp.Keysym(xkUp)},
	^'K': {doSynthetic, xp.Keysym(xkUp)},
	+'l': {doSynthetic, xp.Keysym(xkRight)},
	^'L': {doSynthetic, xp.Keysym(xkRight)},
	+'b': {doSynthetic, xp.Keysym(xkEnd)},
	^'B': {doSynthetic, xp.Keysym(xkEnd)},
	+'n': {doSynthetic, xp.Keysym(xkPageDown)},
	^'N': {doSynthetic, xp.Keysym(xkPageDown)},
	+',': {doSynthetic, xp.Keysym(xkBackspace)},
	^'<': {doSynthetic, xp.Keysym(xkBackspace)},
	+'.': {doSynthetic, xp.Keysym(xkDelete)},
	^'>': {doSynthetic, xp.Keysym(xkDelete)},

	+'/': {doProgramAction, paTabNew},
	^'?': {doProgramAction, paTabClose},
	+'c': {doProgramAction, paTabPrev},
	+'v': {doProgramAction, paTabNext},
	+'o': {doProgramAction, paCopy},
	^'O': {doProgramAction, paCut},
	+'p': {doProgramAction, paPaste},
	^'P': {doProgramAction, paPasteSpecial},
	+'z': {doProgramAction, paZoomIn},
	^'Z': {doProgramAction, paZoomReset},
	+'x': {doProgramAction, paZoomOut},
}

// programAction is an action for a particular program to invoke, as opposed
// to a window management action or generic left/down/up/right synthetic key.
type programAction int

const (
	paTabNew programAction = iota
	paTabClose
	paTabPrev
	paTabNext
	paCut
	paCopy
	paPaste
	paPasteSpecial
	paZoomIn
	paZoomOut
	paZoomReset
	nProgramActions
)

// programActions defines the program-specific synthetic key combination to
// send to perform a generic program action. For example, the 'copy' action is
// Control-C for some programs and Control-Shift-C for others.
//
// The map keys are based on a window's WM_CLASS. To configure a program that
// isn't listed here, run "xprop | grep WM_CLASS", click on a window from that
// program, and use the first quoted value as the map key here.
var programActions = map[string][nProgramActions]struct {
	state  uint16
	keysym xp.Keysym
}{
	"gnome-terminal-server": {
		paTabNew:       {xp.ModMaskControl | xp.ModMaskShift, 'T'},
		paTabClose:     {xp.ModMaskControl | xp.ModMaskShift, 'W'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paCut:          {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paCopy:         {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paPaste:        {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},
	"google-chrome": {
		paTabNew:       {xp.ModMaskControl, 't'},
		paTabClose:     {xp.ModMaskControl, 'w'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paCut:          {xp.ModMaskControl, 'x'},
		paCopy:         {xp.ModMaskControl, 'c'},
		paPaste:        {xp.ModMaskControl, 'v'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},
}
<commit_msg>Handle the change in pactl set-sink-volume arg format.<commit_after>package main

import (
	"time"

	xp "github.com/BurntSushi/xgb/xproto"
)

const (
	// wmKeysym is the key to trigger taowm actions. For other possible
	// values, such as xkSuperL for the 'Windows' key that is typically
	// between the left Control and Alt keys, see keysym.go.
	wmKeysym = xkCapsLock

	// colorXxx are taowm's text and border colors. We assume 24-bit RGB.
	colorBaseUnfocused  = 0x1f3f1f
	colorBaseFocused    = 0x3f7f3f
	colorPulseUnfocused = 0x3f7f3f
	colorPulseFocused   = 0x7fff7f
	colorQuitUnfocused  = 0x7f1f1f
	colorQuitFocused    = 0xff3f3f

	// fontXxx are the font metrics for X11's default font (fixed).
	// fontHeight1 is the vertical offset for the first line of text.
	fontHeight  = 16
	fontHeight1 = 9
	fontWidth   = 6

	// pulseXxx are the animation durations.
	pulseFrameDuration = 50 * time.Millisecond
	pulseTotalDuration = 1000 * time.Millisecond

	// quitDuration is the grace period, when quitting, for programs to exit
	// cleanly.
	quitDuration = 60 * time.Second
)

// xSettings is the key/value pairs to announce via the XSETTINGS mechanism.
// In particular, these include font and theme configuration parameters picked
// up by GTK+ programs such as gnome-terminal.
//
// The dump_xsettings program from http://code.google.com/p/xsettingsd/ will
// show the XSETTINGS key/value pairs set by other desktop environments such
// as GNOME.
//
// If this array is empty, then taowm will not try to own the XSETTINGS list,
// allowing another program such as gnome-settings-daemon to do so.
var xSettings = [...]struct {
	name  string
	value interface{}
}{
	{"Net/IconThemeName", "Tango"},
	{"Net/ThemeName", "Clearlooks"},
	{"Xft/Antialias", 1},
	{"Xft/DPI", 96 * 1024}, // Hard-code 96 DPI, the same as what gnome-settings-daemon does.
	{"Xft/Hinting", 1},
	{"Xft/HintStyle", "hintslight"},
	{"Xft/RGBA", "none"},
}

const doAudioActions = true

// actions lists the action to be performed for each key press. The do function
// returns whether to pulsate the frames' borders to acknowledge the key press.
//
// The map keys are X11 keysyms as int32s. The unary +/^ means whether the
// shift modifier needs to be absent/present.
var actions = map[int32]struct {
	do  func(*workspace, interface{}) bool
	arg interface{}
}{
	+' ':      {doExec, []string{"google-chrome"}},
	^' ':      {doExec, []string{"google-chrome", "--incognito"}},
	^'|':      {doExec, []string{"gnome-screensaver-command", "-l"}},
	+xkReturn: {doExec, []string{"gnome-terminal"}},
	^xkReturn: {doExec, []string{"dmenu_run", "-nb", "#0f0f0f", "-nf", "#3f7f3f",
		"-sb", "#0f0f0f", "-sf", "#7fff7f", "-l", "10"}},

	+xkAudioLowerVolume: {doAudio, []string{"pactl", "set-sink-volume", "0", "-5%"}},
	+xkAudioRaiseVolume: {doAudio, []string{"pactl", "set-sink-volume", "0", "+5%"}},
	+xkAudioMute:        {doAudio, []string{"pactl", "set-sink-mute", "0", "toggle"}},

	+xkBackspace: {doWindowDelete, nil},
	^xkEscape:    {doQuit, nil},

	+'`':          {doScreen, next},
	^'~':          {doScreen, prev},
	+xkTab:        {doFrame, next},
	^xkISOLeftTab: {doFrame, prev},

	+'q': {doList, listWorkspaces},
	+'w': {doWorkspaceMigrate, nil},
	+'e': {doWorkspace, prev},
	^'E': {doWorkspaceNudge, prev},
	+'r': {doWorkspace, next},
	^'R': {doWorkspaceNudge, next},
	+'t': {doWorkspaceNew, nil},
	^'T': {doWorkspaceDelete, nil},

	+'a': {doList, listWindows},
	+'s': {doWindowSelect, false},
	^'S': {doWindowSelect, true},
	+'d': {doWindow, prev},
	^'D': {doWindowNudge, prev},
	+'f': {doWindow, next},
	^'F': {doWindowNudge, next},
	+'g': {doFullscreen, nil},
	^'G': {doHide, nil},

	+'-': {doSplit, horizontal},
	+'=': {doSplit, vertical},
	^'+': {doMerge, nil},

	+'1': {doWindowN, 0},
	+'2': {doWindowN, 1},
	+'3': {doWindowN, 2},
	+'4': {doWindowN, 3},
	+'5': {doWindowN, 4},
	+'6': {doWindowN, 5},
	+'7': {doWindowN, 6},
	+'8': {doWindowN, 7},
	+'9': {doWindowN, 8},
	+'0': {doWindowN, 9},

	+xkF1:  {doWorkspaceN, 0},
	+xkF2:  {doWorkspaceN, 1},
	+xkF3:  {doWorkspaceN, 2},
	+xkF4:  {doWorkspaceN, 3},
	+xkF5:  {doWorkspaceN, 4},
	+xkF6:  {doWorkspaceN, 5},
	+xkF7:  {doWorkspaceN, 6},
	+xkF8:  {doWorkspaceN, 7},
	+xkF9:  {doWorkspaceN, 8},
	+xkF10: {doWorkspaceN, 9},
	+xkF11: {doWorkspaceN, 10},
	+xkF12: {doWorkspaceN, 11},

	+'i': {doSynthetic, xp.Button(4)},
	^'I': {doSynthetic, xp.Button(4)},
	+'m': {doSynthetic, xp.Button(5)},
	^'M': {doSynthetic, xp.Button(5)},
	+'y': {doSynthetic, xp.Keysym(xkHome)},
	^'Y': {doSynthetic, xp.Keysym(xkHome)},
	+'u': {doSynthetic, xp.Keysym(xkPageUp)},
	^'U': {doSynthetic, xp.Keysym(xkPageUp)},
	+'h': {doSynthetic, xp.Keysym(xkLeft)},
	^'H': {doSynthetic, xp.Keysym(xkLeft)},
	+'j': {doSynthetic, xp.Keysym(xkDown)},
	^'J': {doSynthetic, xp.Keysym(xkDown)},
	+'k': {doSynthetic, xp.Keysym(xkUp)},
	^'K': {doSynthetic, xp.Keysym(xkUp)},
	+'l': {doSynthetic, xp.Keysym(xkRight)},
	^'L': {doSynthetic, xp.Keysym(xkRight)},
	+'b': {doSynthetic, xp.Keysym(xkEnd)},
	^'B': {doSynthetic, xp.Keysym(xkEnd)},
	+'n': {doSynthetic, xp.Keysym(xkPageDown)},
	^'N': {doSynthetic, xp.Keysym(xkPageDown)},
	+',': {doSynthetic, xp.Keysym(xkBackspace)},
	^'<': {doSynthetic, xp.Keysym(xkBackspace)},
	+'.': {doSynthetic, xp.Keysym(xkDelete)},
	^'>': {doSynthetic, xp.Keysym(xkDelete)},

	+'/': {doProgramAction, paTabNew},
	^'?': {doProgramAction, paTabClose},
	+'c': {doProgramAction, paTabPrev},
	+'v': {doProgramAction, paTabNext},
	+'o': {doProgramAction, paCopy},
	^'O': {doProgramAction, paCut},
	+'p': {doProgramAction, paPaste},
	^'P': {doProgramAction, paPasteSpecial},
	+'z': {doProgramAction, paZoomIn},
	^'Z': {doProgramAction, paZoomReset},
	+'x': {doProgramAction, paZoomOut},
}

// programAction is an action for a particular program to invoke, as opposed
// to a window management action or generic left/down/up/right synthetic key.
type programAction int

const (
	paTabNew programAction = iota
	paTabClose
	paTabPrev
	paTabNext
	paCut
	paCopy
	paPaste
	paPasteSpecial
	paZoomIn
	paZoomOut
	paZoomReset
	nProgramActions
)

// programActions defines the program-specific synthetic key combination to
// send to perform a generic program action. For example, the 'copy' action is
// Control-C for some programs and Control-Shift-C for others.
//
// The map keys are based on a window's WM_CLASS. To configure a program that
// isn't listed here, run "xprop | grep WM_CLASS", click on a window from that
// program, and use the first quoted value as the map key here.
var programActions = map[string][nProgramActions]struct {
	state  uint16
	keysym xp.Keysym
}{
	"gnome-terminal-server": {
		paTabNew:       {xp.ModMaskControl | xp.ModMaskShift, 'T'},
		paTabClose:     {xp.ModMaskControl | xp.ModMaskShift, 'W'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paCut:          {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paCopy:         {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paPaste:        {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},
	"google-chrome": {
		paTabNew:       {xp.ModMaskControl, 't'},
		paTabClose:     {xp.ModMaskControl, 'w'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paCut:          {xp.ModMaskControl, 'x'},
		paCopy:         {xp.ModMaskControl, 'c'},
		paPaste:        {xp.ModMaskControl, 'v'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},
}
<|endoftext|>