<commit_before>package gateway

import (
	"errors"
	"sync"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
)

type rpcID [8]byte

func (id rpcID) String() string {
	for i := range id {
		if id[i] == 0 {
			id[i] = ' '
		}
	}
	return string(id[:])
}

// handlerName truncates a string to 8 bytes. If len(name) < 8, the remaining
// bytes are 0. A handlerName is specified at the beginning of each network
// call, indicating which function should handle the connection.
func handlerName(name string) (id rpcID) {
	copy(id[:], name)
	return
}

// managedRPC calls an RPC on the given address. managedRPC cannot be called on
// an address that the Gateway is not connected to.
func (g *Gateway) managedRPC(addr modules.NetAddress, name string, fn modules.RPCFunc) error {
	g.mu.RLock()
	peer, ok := g.peers[addr]
	g.mu.RUnlock()
	if !ok {
		return errors.New("can't call RPC on unconnected peer " + string(addr))
	}

	conn, err := peer.open()
	if err != nil {
		return err
	}
	defer conn.Close()

	// write header
	if err := encoding.WriteObject(conn, handlerName(name)); err != nil {
		return err
	}
	// call fn
	return fn(conn)
}

// RPC calls an RPC on the given address. RPC cannot be called on an address
// that the Gateway is not connected to.
func (g *Gateway) RPC(addr modules.NetAddress, name string, fn modules.RPCFunc) error {
	if err := g.threads.Add(); err != nil {
		return err
	}
	defer g.threads.Done()
	return g.managedRPC(addr, name, fn)
}

// RegisterRPC registers an RPCFunc as a handler for a given identifier. To
// call an RPC, use gateway.RPC, supplying the same identifier given to
// RegisterRPC. Identifiers should always use PascalCase. The first 8
// characters of an identifier should be unique, as the identifier used
// internally is truncated to 8 bytes.
func (g *Gateway) RegisterRPC(name string, fn modules.RPCFunc) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.handlers[handlerName(name)]; ok {
		build.Critical("RPC already registered: " + name)
	}
	g.handlers[handlerName(name)] = fn
}

// UnregisterRPC unregisters an RPC and removes the corresponding RPCFunc from
// g.handlers. Future calls to the RPC by peers will fail.
func (g *Gateway) UnregisterRPC(name string) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.handlers[handlerName(name)]; !ok {
		build.Critical("RPC not registered: " + name)
	}
	delete(g.handlers, handlerName(name))
}

// RegisterConnectCall registers a name and RPCFunc to be called on a peer
// upon connecting.
func (g *Gateway) RegisterConnectCall(name string, fn modules.RPCFunc) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.initRPCs[name]; ok {
		build.Critical("ConnectCall already registered: " + name)
	}
	g.initRPCs[name] = fn
}

// UnregisterConnectCall unregisters an on-connect call and removes the
// corresponding RPCFunc from g.initRPCs. Future connections to peers will not
// trigger the RPC to be called on them.
func (g *Gateway) UnregisterConnectCall(name string) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.initRPCs[name]; !ok {
		build.Critical("ConnectCall not registered: " + name)
	}
	delete(g.initRPCs, name)
}

// threadedListenPeer listens for new streams on a peer connection and serves them via
// threadedHandleConn.
func (g *Gateway) threadedListenPeer(p *peer) {
	// Disconnect from the peer on gateway.Close or when the peer disconnects,
	// whichever comes first.
	peerCloseChan := make(chan struct{})
	defer close(peerCloseChan)
	// We register the goroutine with the ThreadGroup as it acquires a lock, which
	// may take some time.
	if g.threads.Add() != nil {
		return
	}
	go func() {
		defer g.threads.Done()
		select {
		case <-g.threads.StopChan():
		case <-peerCloseChan:
		}
		// Can't call Disconnect because it could return sync.ErrStopped.
		g.mu.Lock()
		delete(g.peers, p.NetAddress)
		g.mu.Unlock()
		if err := p.sess.Close(); err != nil {
			g.log.Debugf("WARN: error disconnecting from peer %q: %v", p.NetAddress, err)
		}
	}()

	if g.threads.Add() != nil {
		return
	}
	defer g.threads.Done()

	for {
		conn, err := p.accept()
		if err != nil {
			g.log.Println("WARN: lost connection to peer", p.NetAddress)
			return
		}

		// it is the handler's responsibility to close the connection
		go g.threadedHandleConn(conn)
	}
}

// threadedHandleConn reads header data from a connection, then routes it to the
// appropriate handler for further processing.
func (g *Gateway) threadedHandleConn(conn modules.PeerConn) {
	defer conn.Close()

	if g.threads.Add() != nil {
		return
	}
	defer g.threads.Done()

	var id rpcID
	if err := encoding.ReadObject(conn, &id, 8); err != nil {
		return
	}
	// call registered handler for this ID
	g.mu.RLock()
	fn, ok := g.handlers[id]
	g.mu.RUnlock()
	if !ok {
		g.log.Debugf("WARN: incoming conn %v requested unknown RPC \"%v\"", conn.RPCAddr(), id)
		return
	}
	g.log.Debugf("INFO: incoming conn %v requested RPC \"%v\"", conn.RPCAddr(), id)

	// call fn
	err := fn(conn)
	// don't log benign errors
	if err == modules.ErrDuplicateTransactionSet || err == modules.ErrBlockKnown {
		err = nil
	}
	if err != nil {
		g.log.Debugf("WARN: incoming RPC \"%v\" from conn %v failed: %v", id, conn.RPCAddr(), err)
	}
}

// Broadcast calls an RPC on all of the specified peers. The calls are run in
// parallel. Broadcasts are restricted to "one-way" RPCs, which simply write an
// object and disconnect. This is why Broadcast takes an interface{} instead of
// an RPCFunc.
func (g *Gateway) Broadcast(name string, obj interface{}, peers []modules.Peer) {
	if g.threads.Add() != nil {
		return
	}
	defer g.threads.Done()

	g.log.Printf("INFO: broadcasting RPC %q to %v peers", name, len(peers))

	// only encode obj once, instead of using WriteObject
	enc := encoding.Marshal(obj)
	fn := func(conn modules.PeerConn) error {
		return encoding.WritePrefix(conn, enc)
	}

	var wg sync.WaitGroup
	for _, p := range peers {
		wg.Add(1)
		go func(addr modules.NetAddress) {
			defer wg.Done()
			err := g.managedRPC(addr, name, fn)
			if err != nil {
				g.log.Debugf("WARN: broadcasting RPC %q to peer %q failed (attempting again in 10 seconds): %v", name, addr, err)
				// try one more time before giving up
				select {
				case <-time.After(10 * time.Second):
				case <-g.threads.StopChan():
					return
				}
				err := g.RPC(addr, name, fn)
				if err != nil {
					g.log.Debugf("WARN: broadcasting RPC %q to peer %q failed twice: %v", name, addr, err)
				}
			}
		}(p.NetAddress)
	}
	wg.Wait()
}
<commit_msg>add docstrings to gateway/rpc.go<commit_after>package gateway

import (
	"errors"
	"sync"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
)

// rpcID is an 8-byte signature that is added to all RPCs to tell the gatway
// what to do with the RPC.
type rpcID [8]byte

// String returns a string representation of an rpcID.
func (id rpcID) String() string {
	for i := range id {
		if id[i] == 0 {
			id[i] = ' '
		}
	}
	return string(id[:])
}

// handlerName truncates a string to 8 bytes. If len(name) < 8, the remaining
// bytes are 0. A handlerName is specified at the beginning of each network
// call, indicating which function should handle the connection.
func handlerName(name string) (id rpcID) {
	copy(id[:], name)
	return
}

// managedRPC calls an RPC on the given address. managedRPC cannot be called on
// an address that the Gateway is not connected to.
func (g *Gateway) managedRPC(addr modules.NetAddress, name string, fn modules.RPCFunc) error {
	g.mu.RLock()
	peer, ok := g.peers[addr]
	g.mu.RUnlock()
	if !ok {
		return errors.New("can't call RPC on unconnected peer " + string(addr))
	}

	conn, err := peer.open()
	if err != nil {
		return err
	}
	defer conn.Close()

	// write header
	if err := encoding.WriteObject(conn, handlerName(name)); err != nil {
		return err
	}
	// call fn
	return fn(conn)
}

// RPC calls an RPC on the given address. RPC cannot be called on an address
// that the Gateway is not connected to.
func (g *Gateway) RPC(addr modules.NetAddress, name string, fn modules.RPCFunc) error {
	if err := g.threads.Add(); err != nil {
		return err
	}
	defer g.threads.Done()
	return g.managedRPC(addr, name, fn)
}

// RegisterRPC registers an RPCFunc as a handler for a given identifier. To
// call an RPC, use gateway.RPC, supplying the same identifier given to
// RegisterRPC. Identifiers should always use PascalCase. The first 8
// characters of an identifier should be unique, as the identifier used
// internally is truncated to 8 bytes.
func (g *Gateway) RegisterRPC(name string, fn modules.RPCFunc) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.handlers[handlerName(name)]; ok {
		build.Critical("RPC already registered: " + name)
	}
	g.handlers[handlerName(name)] = fn
}

// UnregisterRPC unregisters an RPC and removes the corresponding RPCFunc from
// g.handlers. Future calls to the RPC by peers will fail.
func (g *Gateway) UnregisterRPC(name string) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.handlers[handlerName(name)]; !ok {
		build.Critical("RPC not registered: " + name)
	}
	delete(g.handlers, handlerName(name))
}

// RegisterConnectCall registers a name and RPCFunc to be called on a peer
// upon connecting.
func (g *Gateway) RegisterConnectCall(name string, fn modules.RPCFunc) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.initRPCs[name]; ok {
		build.Critical("ConnectCall already registered: " + name)
	}
	g.initRPCs[name] = fn
}

// UnregisterConnectCall unregisters an on-connect call and removes the
// corresponding RPCFunc from g.initRPCs. Future connections to peers will not
// trigger the RPC to be called on them.
func (g *Gateway) UnregisterConnectCall(name string) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if _, ok := g.initRPCs[name]; !ok {
		build.Critical("ConnectCall not registered: " + name)
	}
	delete(g.initRPCs, name)
}

// threadedListenPeer listens for new streams on a peer connection and serves them via
// threadedHandleConn.
func (g *Gateway) threadedListenPeer(p *peer) {
	// Disconnect from the peer on gateway.Close or when the peer disconnects,
	// whichever comes first.
	peerCloseChan := make(chan struct{})
	defer close(peerCloseChan)
	// We register the goroutine with the ThreadGroup as it acquires a lock, which
	// may take some time.
	if g.threads.Add() != nil {
		return
	}
	go func() {
		defer g.threads.Done()
		select {
		case <-g.threads.StopChan():
		case <-peerCloseChan:
		}
		// Can't call Disconnect because it could return sync.ErrStopped.
		g.mu.Lock()
		delete(g.peers, p.NetAddress)
		g.mu.Unlock()
		if err := p.sess.Close(); err != nil {
			g.log.Debugf("WARN: error disconnecting from peer %q: %v", p.NetAddress, err)
		}
	}()

	if g.threads.Add() != nil {
		return
	}
	defer g.threads.Done()

	for {
		conn, err := p.accept()
		if err != nil {
			g.log.Println("WARN: lost connection to peer", p.NetAddress)
			return
		}

		// it is the handler's responsibility to close the connection
		go g.threadedHandleConn(conn)
	}
}

// threadedHandleConn reads header data from a connection, then routes it to the
// appropriate handler for further processing.
func (g *Gateway) threadedHandleConn(conn modules.PeerConn) {
	defer conn.Close()

	if g.threads.Add() != nil {
		return
	}
	defer g.threads.Done()

	var id rpcID
	if err := encoding.ReadObject(conn, &id, 8); err != nil {
		return
	}
	// call registered handler for this ID
	g.mu.RLock()
	fn, ok := g.handlers[id]
	g.mu.RUnlock()
	if !ok {
		g.log.Debugf("WARN: incoming conn %v requested unknown RPC \"%v\"", conn.RPCAddr(), id)
		return
	}
	g.log.Debugf("INFO: incoming conn %v requested RPC \"%v\"", conn.RPCAddr(), id)

	// call fn
	err := fn(conn)
	// don't log benign errors
	if err == modules.ErrDuplicateTransactionSet || err == modules.ErrBlockKnown {
		err = nil
	}
	if err != nil {
		g.log.Debugf("WARN: incoming RPC \"%v\" from conn %v failed: %v", id, conn.RPCAddr(), err)
	}
}

// Broadcast calls an RPC on all of the specified peers. The calls are run in
// parallel. Broadcasts are restricted to "one-way" RPCs, which simply write an
// object and disconnect. This is why Broadcast takes an interface{} instead of
// an RPCFunc.
func (g *Gateway) Broadcast(name string, obj interface{}, peers []modules.Peer) {
	if g.threads.Add() != nil {
		return
	}
	defer g.threads.Done()

	g.log.Printf("INFO: broadcasting RPC %q to %v peers", name, len(peers))

	// only encode obj once, instead of using WriteObject
	enc := encoding.Marshal(obj)
	fn := func(conn modules.PeerConn) error {
		return encoding.WritePrefix(conn, enc)
	}

	var wg sync.WaitGroup
	for _, p := range peers {
		wg.Add(1)
		go func(addr modules.NetAddress) {
			defer wg.Done()
			err := g.managedRPC(addr, name, fn)
			if err != nil {
				g.log.Debugf("WARN: broadcasting RPC %q to peer %q failed (attempting again in 10 seconds): %v", name, addr, err)
				// try one more time before giving up
				select {
				case <-time.After(10 * time.Second):
				case <-g.threads.StopChan():
					return
				}
				err := g.RPC(addr, name, fn)
				if err != nil {
					g.log.Debugf("WARN: broadcasting RPC %q to peer %q failed twice: %v", name, addr, err)
				}
			}
		}(p.NetAddress)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

type Message map[string]interface{}

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{}
	glcd.QuitChan = sigChan
	glcd.init(configFile)

	// receiving quit shuts down
	<-sigChan
}

type GLCClient struct {
	Name		string
	Topic		string
	Writer		*nsq.Writer
	Heartbeat	time.Time
	Clientid	string
}

// struct type for Bot3
type GLCD struct {
	Online			 bool
	ConfigFile		 *iniconf.ConfigFile

	// NSQ input/output to bot3Server
	GLCInput		 *nsq.Reader
	Clients			 map[string] *GLCClient

	QuitChan                 chan os.Signal
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	serverTopic, _ := conf.GetString("nsq", "server-topic")
	serverChannel, _ := conf.GetString("nsq", "server-channel")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	writer := nsq.NewWriter(nsqdAddress)
	writer.Publish(serverTopic, []byte("{\"client\":\"server\"}"))

	// set up listener for heartbeat from bot3server
	reader, err := nsq.NewReader(serverTopic, serverChannel)
	if err != nil {
		panic(err)
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCInput = reader

	glcd.GLCInput.AddHandler(glcd)
	glcd.GLCInput.ConnectToLookupd(lookupdAddress)

	// Spawn goroutine to clear out clients who don't send hearbeats
	// anymore.
	go glcd.CleanupClients()

	// hardcoded kill command just in case

	return nil
}

func (cl *GLCClient) Publish(msg *Message) {
	if cl.Writer == nil {
		args := strings.SplitN(cl.Clientid, ":", 3)
		if len(args) != 3 { return }
		host, port, topic := args[0], args[1], args[2]
		log.Printf("cl.publish: Attempting to connect to '" + host + ":" + port + "'")
		cl.Writer = nsq.NewWriter(host + ":" + port)
		cl.Topic = topic
	}
	encodedRequest, _ := json.Marshal(*msg)
	cl.Writer.Publish(cl.Topic, encodedRequest)
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				delete(glcd.Clients, k)
			}
		}
	}
}

// Coming up next. COFFEE?
func (glcd *GLCD) HandleMessage(message *nsq.Message) error {
	msg := Message{}

	err := json.Unmarshal(message.Body, &msg)

	if err != nil { return fmt.Errorf("Not a JSON interface") }

	// If "client" is not in the JSON received, dump it.
	cdata, ok := msg["client"]
	if !ok { return fmt.Errorf("No client provided") }

	// If "client" from JSON is
	clientid, ok := cdata.(string)
	if !ok { return fmt.Errorf("Invalid format: client is not a string.") }

	// Ignore our silly "create a topic" message.
	if clientid == "server" {
		return nil
	}

	// Make sure client exists in glcd.Clients
	cl, exists := glcd.Clients[clientid];
	if !exists {
		cl = &GLCClient{}
		cl.Clientid = clientid
		glcd.Clients[clientid] = cl
		cl.Heartbeat = time.Now()
	}

	// Now perform the client's action.
	cmddata, ok := msg["command"]
	if !ok {
		// Lacking a command is okay - It's a heartbeat.
		return nil
	}

	// If "client" from JSON is
	command, ok := cmddata.(string)
	if !ok { return fmt.Errorf("Invalid format: command is not a string.") }
	log.Printf("We got a command: ", command)

	switch command {
	case "ping":
		cl.Publish(&Message{"pong": fmt.Sprint(time.Now())})
		break
	case "wall":
		for _, v := range(glcd.Clients) {
			v.Publish(&msg)
		}
	}
	return nil
}
<commit_msg>Add SendZones and SendZone.<commit_after>package main

import (
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	// "github.com/gamelost/bot3server/server"
	nsq "github.com/gamelost/go-nsq"
	// irc "github.com/gamelost/goirc/client"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

const (
	GLCD_CONFIG = "glcd.config"
)

type Message map[string]interface{}

func main() {
	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// read in necessary configuration
	configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	glcd := &GLCD{}
	glcd.QuitChan = sigChan
	glcd.init(configFile)

	// receiving quit shuts down
	<-sigChan
}

type GLCClient struct {
	Name		string
	Topic		string
	Writer		*nsq.Writer
	Heartbeat	time.Time
	Clientid	string
	State		interface{}		// json representation of player state.
}

// struct type for Bot3
type GLCD struct {
	Online			 bool
	ConfigFile		 *iniconf.ConfigFile

	// NSQ input/output to bot3Server
	GLCInput		 *nsq.Reader
	Clients			 map[string] *GLCClient

	QuitChan                 chan os.Signal

	MongoSession		*mgo.Session
	MongoDB			*mgo.Database
}

func (glcd *GLCD) init(conf *iniconf.ConfigFile) error {

	glcd.ConfigFile = conf
	glcd.Online = false

	glcd.Clients = map[string]*GLCClient{}

	// Connect to Mongo.
	servers, err := glcd.ConfigFile.GetString("mongo", "servers")

	if err != nil {
		return fmt.Errorf("Mongo: No server configured.")
	}

	glcd.MongoSession, err = mgo.Dial(servers)

	if err != nil {
	}

	db, err := glcd.ConfigFile.GetString("mongo", "db")

	if err != nil {
		return fmt.Errorf("Mongo: No database configured.")
	}

	glcd.MongoDB = glcd.MongoSession.DB(db)

	lookupdAddress, _ := conf.GetString("nsq", "lookupd-address")
	nsqdAddress, _ := conf.GetString("nsq", "nsqd-address")
	serverTopic, _ := conf.GetString("nsq", "server-topic")
	serverChannel, _ := conf.GetString("nsq", "server-channel")

	// Create the channel, by connecting to lookupd. (TODO; if it doesn't
	// exist. Also do it the right way with a Register command?)
	writer := nsq.NewWriter(nsqdAddress)
	writer.Publish(serverTopic, []byte("{\"client\":\"server\"}"))

	// set up listener for heartbeat from bot3server
	reader, err := nsq.NewReader(serverTopic, serverChannel)
	if err != nil {
		panic(err)
		glcd.QuitChan <- syscall.SIGINT
	}
	glcd.GLCInput = reader

	glcd.GLCInput.AddHandler(glcd)
	glcd.GLCInput.ConnectToLookupd(lookupdAddress)

	// Spawn goroutine to clear out clients who don't send hearbeats
	// anymore.
	go glcd.CleanupClients()

	return nil
}

func (cl *GLCClient) Publish(msg *Message) {
	if cl.Writer == nil {
		args := strings.SplitN(cl.Clientid, ":", 3)
		if len(args) != 3 { return }
		host, port, topic := args[0], args[1], args[2]
		// log.Printf("cl.publish: Attempting to connect to '" + host + ":" + port + "'")
		cl.Writer = nsq.NewWriter(host + ":" + port)
		cl.Topic = topic
	}
	encodedRequest, _ := json.Marshal(*msg)
	cl.Writer.Publish(cl.Topic, encodedRequest)
}

func (cl *GLCClient) SendCommand(command string, data *Message) {
	msg := Message{}
	msg["command"] = command
	msg["data"] = data
	cl.Publish(&msg)
}

func (cl *GLCClient) SendMessage(text string) {
	cl.SendCommand("message", &Message{"data": text})
}

func (glcd *GLCD) SendCommandAll(command string, data *Message) {
	msg := Message{}
	msg["command"] = command
	msg["data"] = data
	for _, v := range(glcd.Clients) {
		v.Publish(&msg)
	}
}

func (glcd *GLCD) CleanupClients() error {
	for {
		exp := time.Now().Unix()
		<-time.After(time.Second * 10)
		// Expire any clients who haven't sent a heartbeat in the last 10 seconds.
		for k, v := range glcd.Clients {
			if v.Heartbeat.Unix() < exp {
				delete(glcd.Clients, k)
			}
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) SendZones(cl *GLCClient) {
	c := glcd.MongoDB.C("zones")
	q := c.Find(nil)

	if q == nil {
		cl.SendMessage(fmt.Sprintf("No zones found"))
	} else {
		var results []interface{}
		err := q.All(&results)
		if err == nil {
			for _, res := range results {
				cl.SendCommand("updateZone", &Message{"data": res})
			}
		} else {
			cl.SendMessage(fmt.Sprintf("Unable to fetch zones: %v", err))
		}
	}
}

func (glcd *GLCD) SendZone(cl *GLCClient, zone string) {
	c := glcd.MongoDB.C("zones")
	query := bson.M{"zone":zone}
	results := c.Find(query)

	if results == nil {
		cl.SendMessage(fmt.Sprintf("No such zone '%s'", zone))
	} else {
		var res interface{}
		err := results.One(&res)
		if err == nil {
			cl.SendCommand("updateZone", &Message{"data": res})
		} else {
			cl.SendMessage(fmt.Sprintf("Unable to fetch zone: %v", err))
		}
	}
}

// Send a zone file update.
func (glcd *GLCD) UpdateZone(cl *GLCClient, zone string, zdata interface{}) {
	query := bson.M{"zone":zone}

	c := glcd.MongoDB.C("zones")
	val := bson.M{"type": "zone", "zdata": zdata, "timestamp": time.Now()}
	change := bson.M{"$set": val}

	err := c.Update(query, change)

	if err == mgo.ErrNotFound {
		val["id"], _ = c.Count()
		change = bson.M{"$set": val}
		err = c.Update(query, change)
	}

	if err != nil {
		cl.SendMessage(fmt.Sprintf("Unable to update zone: %v", err))
	} else {
		cl.SendMessage(fmt.Sprintf("Updated zone '%s'", zone))
	}
}

func (glcd *GLCD) HandleMessage(message *nsq.Message) error {
	msg := Message{}

	err := json.Unmarshal(message.Body, &msg)

	if err != nil { return fmt.Errorf("Not a JSON interface") }

	// If "client" is not in the JSON received, dump it.
	cdata, ok := msg["client"]
	if !ok { return fmt.Errorf("No client provided") }

	// If "client" from JSON is
	clientid, ok := cdata.(string)
	if !ok { return fmt.Errorf("Invalid format: client is not a string.") }

	// Ignore our silly "create a topic" message.
	if clientid == "server" {
		return nil
	}

	// Make sure client exists in glcd.Clients
	cl, exists := glcd.Clients[clientid];
	if !exists {
		cl = &GLCClient{}
		cl.Clientid = clientid
		glcd.Clients[clientid] = cl
		cl.Heartbeat = time.Now()
	}

	// Now perform the client's action.
	cmddata, ok := msg["command"]
	if !ok {
		// Lacking a command is okay - It's a heartbeat.
		return nil
	}

	// If "client" from JSON is
	command, ok := cmddata.(string)
	if !ok { return fmt.Errorf("Invalid format: command is not a string.") }
	// log.Printf("We got a command: ", command)

	switch command {
	case "ping":
		cl.Publish(&Message{"pong": fmt.Sprint(time.Now())})
		break
// 	case "playerState":
// 		newstate, ok := msg["data"]
// 		if ok {
// 			cl.State = newstate
// 			cl.Publish(&Message{"pong": fmt.Sprint(time.Now())})
// 		}
// 		break
	case "updateZone":
		args, ok := msg["data"]
		if !ok { break }
		margs, ok := args.(Message)
		if !ok { break }
		zonei, ok := margs["zone"]
		if !ok { break }
		zone, ok := zonei.(string)
		if !ok { break }

		zdata, ok := margs["data"]
		if !ok { break }

		glcd.UpdateZone(cl, zone, zdata)
	case "sendZone":
		args, ok := msg["data"]
		if !ok { log.Printf("No data in sendZone"); break }
		log.Println("data:")
		log.Println(args)
		margs, ok := args.(map[string]interface{})
		if !ok { log.Printf("args is not a message in sendZone"); break }
		zonei, ok := margs["zone"]
		if !ok { log.Printf("No zone in sendZone"); break }
		zone, ok := zonei.(string)
		if !ok { log.Printf("Zone is not a string in sendZone"); break }

		glcd.SendZone(cl, zone)
		break
	case "connected":
		glcd.SendZones(cl)
		break
	case "wall":
		for _, v := range(glcd.Clients) {
			v.Publish(&msg)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

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

const (
	port    = ":8001"
	dump    = "dump.gob"
	logf    = "gone.log"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Seen)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	w, err := os.OpenFile(logf, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			log.Println("removing", k)
			log.SetOutput(w)
			log.Println("%s %s %s %s",
				k.Class, k.Name, v.Spent, v.Seen)
			log.SetOutput(os.Stderr)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname + ".tmp")
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
	os.Rename(fname+".tmp", fname)
}

func (t Tracker) reset() {
	for k := range t {
		delete(t, k)
	}
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   time.Duration
	Zzz     bool
}

type Records []Record

type Record struct {
	Class string
	Name  string
	Spent time.Duration
	Seen  time.Time
	Odd   bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent})
	}
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: v})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent,
			Seen:  v.Seen})
	}

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func resetHandler(w http.ResponseWriter, r *http.Request) {
	tracks.reset()
	http.Redirect(w, r, "/", http.StatusFound)
}

func main() {
	tracks.load(dump)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dump)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/dump.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	http.ListenAndServe(port, nil)
}
<commit_msg>change log order for sake of easy parsing<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

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

const (
	port    = ":8001"
	dump    = "dump.gob"
	logf    = "gone.log"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Seen)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	w, err := os.OpenFile(logf, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			log.Println("removing", k)
			log.SetOutput(w)
			log.Println("%s %s %s %s",
				v.Seen, v.Spent, k.Class, k.Name)
			log.SetOutput(os.Stderr)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname + ".tmp")
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
	os.Rename(fname+".tmp", fname)
}

func (t Tracker) reset() {
	for k := range t {
		delete(t, k)
	}
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   time.Duration
	Zzz     bool
}

type Records []Record

type Record struct {
	Class string
	Name  string
	Spent time.Duration
	Seen  time.Time
	Odd   bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent})
	}
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: v})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent,
			Seen:  v.Seen})
	}

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func resetHandler(w http.ResponseWriter, r *http.Request) {
	tracks.reset()
	http.Redirect(w, r, "/", http.StatusFound)
}

func main() {
	tracks.load(dump)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dump)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/dump.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/mewkiz/pkg/goutil"
)

var (
	goneDir       string
	dumpFileName  string
	logFileName   string
	indexFileName string
	tracks        Tracks
	zzz           bool
	logger        *log.Logger
	current       Window
	display       string
	listen        string
	timeout       time.Duration
	expire        time.Duration
	refresh       time.Duration
)

func init() {
	var err error
	goneDir, err = goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	dumpFileName = filepath.Join(goneDir, "gone.gob")
	logFileName = filepath.Join(goneDir, "gone.log")
	indexFileName = filepath.Join(goneDir, "root.tmpl")
	initTemplate(indexFileName)

	flag.StringVar(&display, "display", ":0", "X11 display")
	flag.StringVar(&listen, "listen", "127.0.0.1:8001", "web reporter")
	flag.DurationVar(&timeout, "timeout", time.Minute*5, "idle timeout")
	flag.DurationVar(&expire, "expire", time.Hour*8, "expire timeout")
	flag.DurationVar(&refresh, "refresh", time.Minute, "refresh interval")
	flag.Parse()
}

type Tracks map[Window]Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
	Idle  time.Duration
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s",
		t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (t Tracks) Snooze(idle time.Duration) {
	if !zzz {
		logger.Println("away from keyboard, idle for", idle)
		if c, ok := t[current]; ok {
			if c.Idle < idle {
				c.Idle = idle
			}
			t[current] = c
		}
		zzz = true
	}
}

func (t Tracks) Wakeup() {
	if zzz {
		logger.Println("back to keyboard")
		if c, ok := t[current]; ok {
			c.Seen = time.Now()
			t[current] = c
		}
		zzz = false
	}
}

func (t Tracks) Update(w Window) {
	if !zzz {
		if c, ok := t[current]; ok {
			c.Spent += time.Since(c.Seen)
			t[current] = c
		}
	}

	if _, ok := t[w]; !ok {
		t[w] = Track{}
	}

	s := t[w]
	s.Seen = time.Now()
	t[w] = s

	current = w
}

func (t Tracks) Remove(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Seen) > d || v.Idle > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
}

func Load(fname string) Tracks {
	t := make(Tracks)
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return t
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
	return t
}

func (t Tracks) Store(fname string) {
	tmp := fname + ".tmp"
	dump, err := os.Create(tmp)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
		os.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

func (t Tracks) Cleanup() {
	for {
		tracks.Remove(expire)
		tracks.Store(dumpFileName)
		time.Sleep(time.Minute)
	}
}

func main() {
	X := Connect()
	defer X.Close()

	logfile, err := os.OpenFile(logFileName,
		os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	tracks = Load(dumpFileName)

	go X.Collect(tracks, timeout)
	go tracks.Cleanup()

	webReporter(listen)
}
<commit_msg>Reorder types, vars, funcs<commit_after>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/mewkiz/pkg/goutil"
)

type Tracks map[Window]Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
	Idle  time.Duration
}

var (
	goneDir       string
	dumpFileName  string
	logFileName   string
	indexFileName string
	tracks        Tracks
	zzz           bool
	logger        *log.Logger
	current       Window
	display       string
	listen        string
	timeout       time.Duration
	expire        time.Duration
	refresh       time.Duration
)

func init() {
	var err error
	goneDir, err = goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}

	dumpFileName = filepath.Join(goneDir, "gone.gob")
	logFileName = filepath.Join(goneDir, "gone.log")
	indexFileName = filepath.Join(goneDir, "root.tmpl")
	initTemplate(indexFileName)

	flag.StringVar(&display, "display", ":0", "X11 display")
	flag.StringVar(&listen, "listen", "127.0.0.1:8001", "web reporter")
	flag.DurationVar(&timeout, "timeout", time.Minute*5, "idle timeout")
	flag.DurationVar(&expire, "expire", time.Hour*8, "expire timeout")
	flag.DurationVar(&refresh, "refresh", time.Minute, "refresh interval")
	flag.Parse()
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s",
		t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (t Tracks) Snooze(idle time.Duration) {
	if !zzz {
		logger.Println("away from keyboard, idle for", idle)
		if c, ok := t[current]; ok {
			if c.Idle < idle {
				c.Idle = idle
			}
			t[current] = c
		}
		zzz = true
	}
}

func (t Tracks) Wakeup() {
	if zzz {
		logger.Println("back to keyboard")
		if c, ok := t[current]; ok {
			c.Seen = time.Now()
			t[current] = c
		}
		zzz = false
	}
}

func (t Tracks) Update(w Window) {
	if !zzz {
		if c, ok := t[current]; ok {
			c.Spent += time.Since(c.Seen)
			t[current] = c
		}
	}

	if _, ok := t[w]; !ok {
		t[w] = Track{}
	}

	s := t[w]
	s.Seen = time.Now()
	t[w] = s

	current = w
}

func (t Tracks) Remove(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Seen) > d || v.Idle > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
}

func Load(fname string) Tracks {
	t := make(Tracks)
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return t
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
	return t
}

func (t Tracks) Store(fname string) {
	tmp := fname + ".tmp"
	dump, err := os.Create(tmp)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
		os.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

func (t Tracks) Cleanup() {
	for {
		tracks.Remove(expire)
		tracks.Store(dumpFileName)
		time.Sleep(time.Minute)
	}
}

func main() {
	X := Connect()
	defer X.Close()

	logfile, err := os.OpenFile(logFileName,
		os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	tracks = Load(dumpFileName)

	go X.Collect(tracks, timeout)
	go tracks.Cleanup()

	webReporter(listen)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"errors"
	"fmt"
	"net/http"
	"reflect"
)

var (
	// LogErrors issues appengine.Context.Errorf on any error.
	LogErrors bool = true
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context       appengine.Context
	cache         map[string]interface{}
	inTransaction bool
	toSet         map[string]interface{}
	toDelete      []string
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return FromContext(appengine.NewContext(r))
}

func FromContext(c appengine.Context) *Goon {
	return &Goon{
		context: c,
		cache:   make(map[string]interface{}),
	}
}

func (g *Goon) error(err error) {
	if LogErrors {
		g.context.Errorf(err.Error())
	}
}

func (g *Goon) extractKeys(src interface{}) ([]*datastore.Key, error) {
	v := reflect.Indirect(reflect.ValueOf(src))
	if v.Kind() != reflect.Slice {
		return nil, errors.New("goon: value must be a slice or pointer-to-slice")
	}
	l := v.Len()

	keys := make([]*datastore.Key, l)
	for i := 0; i < l; i++ {
		vi := v.Index(i)
		key, err := g.getStructKey(vi.Interface())
		if err != nil {
			return nil, err
		}
		keys[i] = key
	}
	return keys, nil
}

func (g *Goon) Key(src interface{}) (*datastore.Key, error) {
	return g.getStructKey(src)
}

// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.context, func(tc appengine.Context) error {
		ng = &Goon{
			context:       tc,
			inTransaction: true,
			toSet:         make(map[string]interface{}),
		}
		return f(ng)
	}, opts)

	if err == nil {
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for _, k := range ng.toDelete {
			delete(g.cache, k)
		}
	} else {
		g.error(err)
	}

	return err
}

// Put stores src.
// If src has an incomplete key, it is updated.
func (g *Goon) Put(src interface{}) error {
	return g.PutMulti([]interface{}{src})
}

// PutMany is a wrapper around PutMulti.
func (g *Goon) PutMany(srcs ...interface{}) error {
	return g.PutMulti(srcs)
}

const putMultiLimit = 500

// PutMulti stores a sequence of entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(src interface{}) error {
	keys, err := g.extractKeys(src)
	if err != nil {
		return err
	}

	var memkeys []string
	for _, key := range keys {
		if !key.Incomplete() {
			memkeys = append(memkeys, memkey(key))
		}
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition
	defer memcache.DeleteMulti(g.context, memkeys)

	v := reflect.Indirect(reflect.ValueOf(src))
	for i := 0; i <= len(keys)/putMultiLimit; i++ {
		lo := i * putMultiLimit
		hi := (i + 1) * putMultiLimit
		if hi > len(keys) {
			hi = len(keys)
		}
		rkeys, err := datastore.PutMulti(g.context, keys[lo:hi], v.Slice(lo, hi).Interface())
		if err != nil {
			g.error(err)
			return err
		}

		for i, key := range keys[lo:hi] {
			vi := v.Index(lo+i).Interface()
			if key.Incomplete() {
				setStructKey(vi, rkeys[i])
			}
			if g.inTransaction {
				g.toSet[memkey(rkeys[i])] = vi
			}
		}
	}

	if !g.inTransaction {
		g.putMemoryMulti(src)
	}

	return nil
}

func (g *Goon) putMemoryMulti(src interface{}) {
	v := reflect.Indirect(reflect.ValueOf(src))
	for i := 0; i < v.Len(); i++ {
		g.putMemory(v.Index(i).Interface())
	}
}

func (g *Goon) putMemory(src interface{}) {
	key, _ := g.getStructKey(src)
	g.cache[memkey(key)] = src
}

func (g *Goon) putMemcache(srcs []interface{}) error {
	items := make([]*memcache.Item, len(srcs))

	for i, src := range srcs {
		gob, err := toGob(src)
		if err != nil {
			g.error(err)
			return err
		}
		key, err := g.getStructKey(src)

		items[i] = &memcache.Item{
			Key:   memkey(key),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		g.error(err)
		return err
	}

	g.putMemoryMulti(srcs)
	return nil
}

// Get fetches an entity of kind src by key.
// If there is no such key, datastore.ErrNoSuchEntity is returned.
func (g *Goon) Get(dst interface{}) error {
	dsts := []interface{}{dst}
	if err := g.GetMulti(dsts); err != nil {
		// Look for an embedded error if it's multi
		if me, ok := err.(appengine.MultiError); ok {
			for i, merr := range me {
				if i == 0 {
					return merr
				}
			}
		}
		// Not multi, normal error
		return err
	}
	return nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(dst interface{}) error {
	keys, err := g.extractKeys(dst)
	if err != nil {
		return err
	}

	if g.inTransaction {
		return datastore.GetMulti(g.context, keys, dst)
	}

	var dskeys []*datastore.Key
	var dsdst []interface{}
	var dixs []int

	var memkeys []string
	var mixs []int

	v := reflect.Indirect(reflect.ValueOf(dst))
	for i, key := range keys {
		m := memkey(key)
		if s, present := g.cache[m]; present {
			vi := v.Index(i)
			vi.Set(reflect.ValueOf(s))
		} else {
			memkeys = append(memkeys, m)
			mixs = append(mixs, i)
		}
	}

	memvalues, err := memcache.GetMulti(g.context, memkeys)
	if err != nil {
		g.error(errors.New(fmt.Sprintf("goon: ignored memcache error: %v", err.Error())))
		// ignore memcache errors
		//return err
	}

	for i, m := range memkeys {
		d := v.Index(mixs[i]).Interface()
		if s, present := memvalues[m]; present {
			err := fromGob(d, s.Value)
			if err != nil {
				g.error(err)
				return err
			}

			g.putMemory(d)
		} else {
			key, err := g.getStructKey(d)
			if err != nil {
				g.error(err)
				return err
			}
			dskeys = append(dskeys, key)
			dsdst = append(dsdst, d)
			dixs = append(dixs, mixs[i])
		}
	}

	gmerr := datastore.GetMulti(g.context, dskeys, dsdst)
	var ret error
	var multiErr appengine.MultiError
	var toCache []interface{}
	if gmerr != nil {
		merr, ok := gmerr.(appengine.MultiError)
		if !ok {
			g.error(gmerr)
			return gmerr
		}
		multiErr = make(appengine.MultiError, len(keys))
		for i, idx := range dixs {
			multiErr[idx] = merr[i]
			if merr[i] == nil {
				toCache = append(toCache, dsdst[i])
			}
		}
		ret = multiErr
	} else {
		toCache = dsdst
	}

	if len(dskeys) > 0 {
		if err := g.putMemcache(toCache); err != nil {
			g.error(err)
			return err
		}
	}

	return ret
}

// Delete deletes the entity for the given key.
func (g *Goon) Delete(key *datastore.Key) error {
	keys := []*datastore.Key{key}
	return g.DeleteMulti(keys)
}

// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	memkeys := make([]string, len(keys))
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			g.toDelete = append(g.toDelete, mk)
		} else {
			delete(g.cache, mk)
		}
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition
	defer memcache.DeleteMulti(g.context, memkeys)

	return datastore.DeleteMulti(g.context, keys)
}

// NotFound returns true if err is an appengine.MultiError and err[idx] is an datastore.ErrNoSuchEntity.
func NotFound(err error, idx int) bool {
	if merr, ok := err.(appengine.MultiError); ok {
		return idx < len(merr) && merr[idx] == datastore.ErrNoSuchEntity
	}
	return false
}
<commit_msg>go fmt<commit_after>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"errors"
	"fmt"
	"net/http"
	"reflect"
)

var (
	// LogErrors issues appengine.Context.Errorf on any error.
	LogErrors bool = true
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context       appengine.Context
	cache         map[string]interface{}
	inTransaction bool
	toSet         map[string]interface{}
	toDelete      []string
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return FromContext(appengine.NewContext(r))
}

func FromContext(c appengine.Context) *Goon {
	return &Goon{
		context: c,
		cache:   make(map[string]interface{}),
	}
}

func (g *Goon) error(err error) {
	if LogErrors {
		g.context.Errorf(err.Error())
	}
}

func (g *Goon) extractKeys(src interface{}) ([]*datastore.Key, error) {
	v := reflect.Indirect(reflect.ValueOf(src))
	if v.Kind() != reflect.Slice {
		return nil, errors.New("goon: value must be a slice or pointer-to-slice")
	}
	l := v.Len()

	keys := make([]*datastore.Key, l)
	for i := 0; i < l; i++ {
		vi := v.Index(i)
		key, err := g.getStructKey(vi.Interface())
		if err != nil {
			return nil, err
		}
		keys[i] = key
	}
	return keys, nil
}

func (g *Goon) Key(src interface{}) (*datastore.Key, error) {
	return g.getStructKey(src)
}

// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.context, func(tc appengine.Context) error {
		ng = &Goon{
			context:       tc,
			inTransaction: true,
			toSet:         make(map[string]interface{}),
		}
		return f(ng)
	}, opts)

	if err == nil {
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for _, k := range ng.toDelete {
			delete(g.cache, k)
		}
	} else {
		g.error(err)
	}

	return err
}

// Put stores src.
// If src has an incomplete key, it is updated.
func (g *Goon) Put(src interface{}) error {
	return g.PutMulti([]interface{}{src})
}

// PutMany is a wrapper around PutMulti.
func (g *Goon) PutMany(srcs ...interface{}) error {
	return g.PutMulti(srcs)
}

const putMultiLimit = 500

// PutMulti stores a sequence of entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(src interface{}) error {
	keys, err := g.extractKeys(src)
	if err != nil {
		return err
	}

	var memkeys []string
	for _, key := range keys {
		if !key.Incomplete() {
			memkeys = append(memkeys, memkey(key))
		}
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition
	defer memcache.DeleteMulti(g.context, memkeys)

	v := reflect.Indirect(reflect.ValueOf(src))
	for i := 0; i <= len(keys)/putMultiLimit; i++ {
		lo := i * putMultiLimit
		hi := (i + 1) * putMultiLimit
		if hi > len(keys) {
			hi = len(keys)
		}
		rkeys, err := datastore.PutMulti(g.context, keys[lo:hi], v.Slice(lo, hi).Interface())
		if err != nil {
			g.error(err)
			return err
		}

		for i, key := range keys[lo:hi] {
			vi := v.Index(lo + i).Interface()
			if key.Incomplete() {
				setStructKey(vi, rkeys[i])
			}
			if g.inTransaction {
				g.toSet[memkey(rkeys[i])] = vi
			}
		}
	}

	if !g.inTransaction {
		g.putMemoryMulti(src)
	}

	return nil
}

func (g *Goon) putMemoryMulti(src interface{}) {
	v := reflect.Indirect(reflect.ValueOf(src))
	for i := 0; i < v.Len(); i++ {
		g.putMemory(v.Index(i).Interface())
	}
}

func (g *Goon) putMemory(src interface{}) {
	key, _ := g.getStructKey(src)
	g.cache[memkey(key)] = src
}

func (g *Goon) putMemcache(srcs []interface{}) error {
	items := make([]*memcache.Item, len(srcs))

	for i, src := range srcs {
		gob, err := toGob(src)
		if err != nil {
			g.error(err)
			return err
		}
		key, err := g.getStructKey(src)

		items[i] = &memcache.Item{
			Key:   memkey(key),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		g.error(err)
		return err
	}

	g.putMemoryMulti(srcs)
	return nil
}

// Get fetches an entity of kind src by key.
// If there is no such key, datastore.ErrNoSuchEntity is returned.
func (g *Goon) Get(dst interface{}) error {
	dsts := []interface{}{dst}
	if err := g.GetMulti(dsts); err != nil {
		// Look for an embedded error if it's multi
		if me, ok := err.(appengine.MultiError); ok {
			for i, merr := range me {
				if i == 0 {
					return merr
				}
			}
		}
		// Not multi, normal error
		return err
	}
	return nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(dst interface{}) error {
	keys, err := g.extractKeys(dst)
	if err != nil {
		return err
	}

	if g.inTransaction {
		return datastore.GetMulti(g.context, keys, dst)
	}

	var dskeys []*datastore.Key
	var dsdst []interface{}
	var dixs []int

	var memkeys []string
	var mixs []int

	v := reflect.Indirect(reflect.ValueOf(dst))
	for i, key := range keys {
		m := memkey(key)
		if s, present := g.cache[m]; present {
			vi := v.Index(i)
			vi.Set(reflect.ValueOf(s))
		} else {
			memkeys = append(memkeys, m)
			mixs = append(mixs, i)
		}
	}

	memvalues, err := memcache.GetMulti(g.context, memkeys)
	if err != nil {
		g.error(errors.New(fmt.Sprintf("goon: ignored memcache error: %v", err.Error())))
		// ignore memcache errors
		//return err
	}

	for i, m := range memkeys {
		d := v.Index(mixs[i]).Interface()
		if s, present := memvalues[m]; present {
			err := fromGob(d, s.Value)
			if err != nil {
				g.error(err)
				return err
			}

			g.putMemory(d)
		} else {
			key, err := g.getStructKey(d)
			if err != nil {
				g.error(err)
				return err
			}
			dskeys = append(dskeys, key)
			dsdst = append(dsdst, d)
			dixs = append(dixs, mixs[i])
		}
	}

	gmerr := datastore.GetMulti(g.context, dskeys, dsdst)
	var ret error
	var multiErr appengine.MultiError
	var toCache []interface{}
	if gmerr != nil {
		merr, ok := gmerr.(appengine.MultiError)
		if !ok {
			g.error(gmerr)
			return gmerr
		}
		multiErr = make(appengine.MultiError, len(keys))
		for i, idx := range dixs {
			multiErr[idx] = merr[i]
			if merr[i] == nil {
				toCache = append(toCache, dsdst[i])
			}
		}
		ret = multiErr
	} else {
		toCache = dsdst
	}

	if len(dskeys) > 0 {
		if err := g.putMemcache(toCache); err != nil {
			g.error(err)
			return err
		}
	}

	return ret
}

// Delete deletes the entity for the given key.
func (g *Goon) Delete(key *datastore.Key) error {
	keys := []*datastore.Key{key}
	return g.DeleteMulti(keys)
}

// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	memkeys := make([]string, len(keys))
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			g.toDelete = append(g.toDelete, mk)
		} else {
			delete(g.cache, mk)
		}
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition
	defer memcache.DeleteMulti(g.context, memkeys)

	return datastore.DeleteMulti(g.context, keys)
}

// NotFound returns true if err is an appengine.MultiError and err[idx] is an datastore.ErrNoSuchEntity.
func NotFound(err error, idx int) bool {
	if merr, ok := err.(appengine.MultiError); ok {
		return idx < len(merr) && merr[idx] == datastore.ErrNoSuchEntity
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/paddie/gokmp"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"syscall"
	"unicode/utf8"
)

var END_OF_WORK = ""
var MAX_OPEN_FILES uint64 = 17711
var WORK_QUEUE_SIZE int = 28657
var inv_ext map[string]bool
var lookingfor string
var kmp *gokmp.KMP
var max_workers int = 228
var wg sync.WaitGroup
var work_queue chan string
var done chan bool
var index int

func main() {
	flag.Parse()
	// TODO: handle incorrect input

	getReady(flag.Arg(0))
	crawlFolder()

	// tell workers it's over
	work_queue <- END_OF_WORK
	wg.Wait()

	fmt.Println("Done.")
}

func getReady(lookingFor string) {
	// put cpu cores to work
	numCPUs := runtime.NumCPU() - 1
	if numCPUs < 1 {
		numCPUs = 1
	}
	runtime.GOMAXPROCS(numCPUs)

	// Set max open files
	var rLimit syscall.Rlimit
	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	if err != nil {
		fmt.Println("Error Getting Rlimit ", err)
	}
	rLimit.Cur = MAX_OPEN_FILES
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	if err != nil {
		fmt.Println("Error Setting Rlimit ", err)
	} else {
		max_workers = 6765
	}

	// Setup workers
	work_queue = make(chan string, WORK_QUEUE_SIZE)
	done = make(chan bool)

	for i := 0; i < max_workers/3; i++ {
		go searchWorker(work_queue)
	}
	go func() {
		for i := 0; i < (max_workers * (2 / 3)); i++ {
			go searchWorker(work_queue)
		}
	}()

	// Get KMP ready
	lookingfor = flag.Arg(0)
	fmt.Println("Searching for ->", lookingfor)
	kmp, _ = gokmp.NewKMP(lookingfor)
}

func crawlFolder() {
	current := "."
	filepath.Walk(current, visit)
}

func visit(path string, fileInfo os.FileInfo, err error) error {

	if fileInfo.IsDir() || err != nil {
		return nil
	}
	work_queue <- path
	return nil
}

func searchWorker(work_queue chan string) {
	wg.Add(1)
	defer wg.Done()
	localkmp := kmp
	var x []byte
	var err error
	for path := range work_queue {
		if path == END_OF_WORK {
			// tell everyone else we're done
			work_queue <- END_OF_WORK
			break
		}

		x, err = ioutil.ReadFile(path)
		if err != nil || x == nil {
			x = nil
			continue //fail gracefully
		}

		if index > len(x) {
			x = nil
			index = len(x)
		}
		if !utf8.ValidString(string(x[:index])) {
			continue
		}

		if localkmp == nil {
			localkmp = kmp
		}
		if localkmp.ContainedIn(string(x)) {
			fmt.Println(path)
		}
	}
}
<commit_msg>Fix bug for shorter files.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/paddie/gokmp"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"syscall"
	"unicode/utf8"
)

var END_OF_WORK = ""
var MAX_OPEN_FILES uint64 = 17711
var WORK_QUEUE_SIZE int = 28657
var inv_ext map[string]bool
var lookingfor string
var kmp *gokmp.KMP
var max_workers int = 228
var wg sync.WaitGroup
var work_queue chan string
var done chan bool
var index int

func main() {
	flag.Parse()
	// TODO: handle incorrect input

	getReady(flag.Arg(0))
	crawlFolder()

	// tell workers it's over
	work_queue <- END_OF_WORK
	wg.Wait()

	fmt.Println("Done.")
}

func getReady(lookingFor string) {
	// put cpu cores to work
	numCPUs := runtime.NumCPU() - 1
	if numCPUs < 1 {
		numCPUs = 1
	}
	runtime.GOMAXPROCS(numCPUs)

	// Set max open files
	var rLimit syscall.Rlimit
	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	if err != nil {
		fmt.Println("Error Getting Rlimit ", err)
	}
	rLimit.Cur = MAX_OPEN_FILES
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	if err != nil {
		fmt.Println("Error Setting Rlimit ", err)
	} else {
		max_workers = 6765
	}

	// Setup workers
	work_queue = make(chan string, WORK_QUEUE_SIZE)
	done = make(chan bool)

	for i := 0; i < max_workers/3; i++ {
		go searchWorker(work_queue)
	}
	go func() {
		for i := 0; i < (max_workers * (2 / 3)); i++ {
			go searchWorker(work_queue)
		}
	}()

	// Get KMP ready
	lookingfor = flag.Arg(0)
	fmt.Println("Searching for ->", lookingfor)
	kmp, _ = gokmp.NewKMP(lookingfor)
}

func crawlFolder() {
	current := "."
	filepath.Walk(current, visit)
}

func visit(path string, fileInfo os.FileInfo, err error) error {

	if fileInfo.IsDir() || err != nil {
		return nil
	}
	work_queue <- path
	return nil
}

func searchWorker(work_queue chan string) {
	wg.Add(1)
	defer wg.Done()
	localkmp := kmp
	var x []byte
	var err error
	for path := range work_queue {
		if path == END_OF_WORK {
			// tell everyone else we're done
			work_queue <- END_OF_WORK
			break
		}

		x, err = ioutil.ReadFile(path)
		if err != nil || x == nil {
			x = nil
			continue //fail gracefully
		}

		if index > len(x) {
			index = len(x)
		}
		if !utf8.ValidString(string(x[:index])) {
			continue
		}

		if localkmp == nil {
			localkmp = kmp
		}
		if localkmp.ContainedIn(string(x)) {
			fmt.Println(path)
		}
	}
}
<|endoftext|>


<commit_before>package gobuggery

import (
	"bytes"
	"encoding/json"
	"net/http"
)

// Debugger is a gateway to the remote debugger methods
type Debugger struct {
	endpoint string
	buf      bytes.Buffer
}

// Request will be marshalled to JSON to make a request to the remote debugger
type Request struct {
	Method string        `json:"method"`
	Args   []interface{} `json:"args"`
}

// NewDebugger returns a Debugger configured to interact with the given
// endpoint URI
func NewDebugger(ep string) Debugger {
	return Debugger{endpoint: ep}
}

// RunMethod runs a remote method via a POST/JSON API
func (d *Debugger) RunMethod(meth string, args []interface{}) (interface{}, error) {

	d.buf.Reset()
	err := json.NewEncoder(&d.buf).Encode(
		Request{
			Method: meth,
			Args:   args,
		},
	)

	if err != nil {
		return []interface{}{}, err
	}

	resp, err := http.Post(d.endpoint, "application/json", &d.buf)
	if err != nil {
		return []interface{}{}, err
	}

	defer resp.Body.Close()

	var result interface{}
	e := json.NewDecoder(resp.Body).Decode(&result)
	return result, e

}

// Execute runs an arbitrary command string (as if typed into windbg) and
// returns the result
func (d *Debugger) Execute(command string) (string, error) {

	res, err := d.RunMethod("execute", []interface{}{command})
	if err != nil {
		return "", err
	}

	return res.(string), err
}

// AttachLocalKernel tells the remote to attach as a local kernel debugger (
// like lkd )
func (d *Debugger) AttachLocalKernel() (bool, error) {

	res, err := d.RunMethod("attach_local_kernel", []interface{}{})
	if err != nil {
		return false, err
	}
	return res.(bool), nil
}

// WaitForEvent asks the remote to wait for the next debugger event. Blocking.
func (d *Debugger) WaitForEvent(timeout int) error {

	_, err := d.RunMethod("wait_for_event", []interface{}{timeout})
	return err
}
<commit_msg>rename<commit_after><|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

const (
	GitIgnore = ".gitignore"
)

var (
	GOPATH = os.Getenv("GOPATH")

	dir = ""
)

func main() {
	log.SetFlags(0)

	if len(os.Args) < 2 {
		failAndUsage("Please specify a command")
	}
	cmd := strings.ToLower(os.Args[1])
	switch cmd {
	case "init":
		doinit()
	case "get":
		get()
	default:
		failAndUsage("Unknown command: %s", cmd)
	}
}

func doinit() {
	var err error
	dir, err = os.Getwd()
	if err != nil {
		log.Fatalf("Unable to determine current directory: %s", err)
	}

	if exists(".git") {
		log.Fatalf("%s already contains a .git folder, can't initialize gost", dir)
	}

	// Initialize a git repo
	run("git", "init")

	writeAndCommit(GitIgnore, DefaultGitIgnore, "Initialized repo with a .gitignore file")

	// Done
	log.Printf("Initialized git repo with a default README.md, please set your GOPATH to \"%s\", e.g.", dir)
	log.Printf("  export GOPATH=\"%s\"", dir)
	os.Setenv("GOPATH", dir)
}

func get() {
	requireGOPATH()

	flags := flag.NewFlagSet("get", flag.ExitOnError)
	update := flags.Bool("u", false, "update existing from remote")
	flags.Parse(os.Args[2:])
	args := flags.Args()
	if len(args) < 1 {
		log.Fatal("Please specify a package")
	}

	pkg := args[0]
	if !isGithub(pkg) {
		log.Fatal("gost only supports packages on github.com")
	}

	branch := "master"
	if len(args) > 1 {
		branch = args[1]
	}

	fetchSubtree(pkg, branch, *update, map[string]bool{})

	run("git", "add", "src")
	run("git", "commit", "-m", fmt.Sprintf("[gost] Added %s and its dependencies", pkg))
}

func fetchSubtree(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	// Take only the path up to the github repo
	pkgParts := strings.Split(pkg, "/")
	pkgRoot := path.Join(pkgParts[:3]...)
	if alreadyFetched[pkgRoot] {
		return
	}

	prefix := path.Join("src", pkgRoot)
	if exists(prefix) {
		if update {
			run("git", "subtree", "pull", "--squash",
				"--prefix", prefix,
				fmt.Sprintf("https://%s.git", pkgRoot),
				branch)
		} else {
			log.Printf("%s already exists, declining to add as subtree", prefix)
		}
	} else {
		run("git", "subtree", "add", "--squash",
			"--prefix", prefix,
			fmt.Sprintf("https://%s.git", pkgRoot),
			branch)
	}
	alreadyFetched[pkgRoot] = true
	fetchDeps(pkg, "master", update, alreadyFetched)
}

func fetchDeps(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	depsString := run("go", "list", "-f", "{{range .Deps}}{{.}} {{end}} {{range .TestImports}}{{.}} {{end}}", pkg)
	deps := parseDeps(depsString)

	nonGithubDeps := []string{}
	for _, dep := range deps {
		dep = strings.TrimSpace(dep)
		if dep == "" || dep == "." {
			continue
		}
		if isGithub(dep) {
			fetchSubtree(dep, branch, update, alreadyFetched)
		} else {
			nonGithubDeps = append(nonGithubDeps, dep)
		}
	}

	for _, dep := range nonGithubDeps {
		goGet(dep, update, alreadyFetched)
	}
}

func goGet(pkg string, update bool, alreadyFetched map[string]bool) {
	if alreadyFetched[pkg] {
		return
	}
	args := []string{"get"}
	if update {
		args = append(args, "-u")
	}
	args = append(args, pkg)
	run("go", args...)
	alreadyFetched[pkg] = true
}

func writeAndCommit(file string, content string, comment string) {
	if exists(file) {
		log.Fatalf("%s already contains %s, can't initialize gost", dir, file)
	}

	err := ioutil.WriteFile(file, []byte(content), 0644)
	if err != nil {
		log.Fatalf("Unable to write %s: %s", file, err)
	}

	// Write and commit
	run("git", "add", file)
	run("git", "commit", file, "-m", "[gost] "+comment)
}

func requireGOPATH() {
	if GOPATH == "" {
		log.Fatal("Please set your GOPATH")
	}
}

func exists(file string) bool {
	_, err := os.Stat(file)
	return err == nil
}

func isGithub(pkg string) bool {
	return strings.Index(pkg, "github.com/") == 0
}

func parseDeps(depsString string) []string {
	depsString = strings.Replace(depsString, "[", "", -1)
	depsString = strings.Replace(depsString, "]", "", -1)
	return strings.Split(depsString, " ")
}

func run(prg string, args ...string) string {
	out, err := doRun(prg, args...)
	if err != nil {
		log.Fatal(err)
	}
	return out
}

func doRun(prg string, args ...string) (string, error) {
	cmd := exec.Command(prg, args...)
	log.Printf("Running %s %s", prg, strings.Join(args, " "))
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("%s says %s", prg, string(out))
	}
	return string(out), nil
}

func failAndUsage(msg string, args ...interface{}) {
	log.Printf(msg, args...)
	log.Fatal(`
Commands:
	init - initialize a git repo in the current directory and set GOPATH to here
	get  - like go get, except that all github dependencies are imported as subtrees
`)
}

const DefaultGitIgnore = `pkg
bin
`
<commit_msg>Updated default .gitignore<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

const (
	GitIgnore = ".gitignore"
)

var (
	GOPATH = os.Getenv("GOPATH")

	dir = ""
)

func main() {
	log.SetFlags(0)

	if len(os.Args) < 2 {
		failAndUsage("Please specify a command")
	}
	cmd := strings.ToLower(os.Args[1])
	switch cmd {
	case "init":
		doinit()
	case "get":
		get()
	default:
		failAndUsage("Unknown command: %s", cmd)
	}
}

func doinit() {
	var err error
	dir, err = os.Getwd()
	if err != nil {
		log.Fatalf("Unable to determine current directory: %s", err)
	}

	if exists(".git") {
		log.Fatalf("%s already contains a .git folder, can't initialize gost", dir)
	}

	// Initialize a git repo
	run("git", "init")

	writeAndCommit(GitIgnore, DefaultGitIgnore, "Initialized repo with a .gitignore file")

	// Done
	log.Printf("Initialized git repo with a default README.md, please set your GOPATH to \"%s\", e.g.", dir)
	log.Printf("  export GOPATH=\"%s\"", dir)
	os.Setenv("GOPATH", dir)
}

func get() {
	requireGOPATH()

	flags := flag.NewFlagSet("get", flag.ExitOnError)
	update := flags.Bool("u", false, "update existing from remote")
	flags.Parse(os.Args[2:])
	args := flags.Args()
	if len(args) < 1 {
		log.Fatal("Please specify a package")
	}

	pkg := args[0]
	if !isGithub(pkg) {
		log.Fatal("gost only supports packages on github.com")
	}

	branch := "master"
	if len(args) > 1 {
		branch = args[1]
	}

	fetchSubtree(pkg, branch, *update, map[string]bool{})

	run("git", "add", "src")
	run("git", "commit", "-m", fmt.Sprintf("[gost] Added %s and its dependencies", pkg))
}

func fetchSubtree(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	// Take only the path up to the github repo
	pkgParts := strings.Split(pkg, "/")
	pkgRoot := path.Join(pkgParts[:3]...)
	if alreadyFetched[pkgRoot] {
		return
	}

	prefix := path.Join("src", pkgRoot)
	if exists(prefix) {
		if update {
			run("git", "subtree", "pull", "--squash",
				"--prefix", prefix,
				fmt.Sprintf("https://%s.git", pkgRoot),
				branch)
		} else {
			log.Printf("%s already exists, declining to add as subtree", prefix)
		}
	} else {
		run("git", "subtree", "add", "--squash",
			"--prefix", prefix,
			fmt.Sprintf("https://%s.git", pkgRoot),
			branch)
	}
	alreadyFetched[pkgRoot] = true
	fetchDeps(pkg, "master", update, alreadyFetched)
}

func fetchDeps(pkg string, branch string, update bool, alreadyFetched map[string]bool) {
	depsString := run("go", "list", "-f", "{{range .Deps}}{{.}} {{end}} {{range .TestImports}}{{.}} {{end}}", pkg)
	deps := parseDeps(depsString)

	nonGithubDeps := []string{}
	for _, dep := range deps {
		dep = strings.TrimSpace(dep)
		if dep == "" || dep == "." {
			continue
		}
		if isGithub(dep) {
			fetchSubtree(dep, branch, update, alreadyFetched)
		} else {
			nonGithubDeps = append(nonGithubDeps, dep)
		}
	}

	for _, dep := range nonGithubDeps {
		goGet(dep, update, alreadyFetched)
	}
}

func goGet(pkg string, update bool, alreadyFetched map[string]bool) {
	if alreadyFetched[pkg] {
		return
	}
	args := []string{"get"}
	if update {
		args = append(args, "-u")
	}
	args = append(args, pkg)
	run("go", args...)
	alreadyFetched[pkg] = true
}

func writeAndCommit(file string, content string, comment string) {
	if exists(file) {
		log.Fatalf("%s already contains %s, can't initialize gost", dir, file)
	}

	err := ioutil.WriteFile(file, []byte(content), 0644)
	if err != nil {
		log.Fatalf("Unable to write %s: %s", file, err)
	}

	// Write and commit
	run("git", "add", file)
	run("git", "commit", file, "-m", "[gost] "+comment)
}

func requireGOPATH() {
	if GOPATH == "" {
		log.Fatal("Please set your GOPATH")
	}
}

func exists(file string) bool {
	_, err := os.Stat(file)
	return err == nil
}

func isGithub(pkg string) bool {
	return strings.Index(pkg, "github.com/") == 0
}

func parseDeps(depsString string) []string {
	depsString = strings.Replace(depsString, "[", "", -1)
	depsString = strings.Replace(depsString, "]", "", -1)
	return strings.Split(depsString, " ")
}

func run(prg string, args ...string) string {
	out, err := doRun(prg, args...)
	if err != nil {
		log.Fatal(err)
	}
	return out
}

func doRun(prg string, args ...string) (string, error) {
	cmd := exec.Command(prg, args...)
	log.Printf("Running %s %s", prg, strings.Join(args, " "))
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("%s says %s", prg, string(out))
	}
	return string(out), nil
}

func failAndUsage(msg string, args ...interface{}) {
	log.Printf(msg, args...)
	log.Fatal(`
Commands:
	init - initialize a git repo in the current directory and set GOPATH to here
	get  - like go get, except that all github dependencies are imported as subtrees
`)
}

const DefaultGitIgnore = `pkg
bin
.DS_Store
`
<|endoftext|>


<commit_before>package main

import (
	"time"
)

func DecisionStart() error {
	waitForClusterFull()
	go func() {
		for {
			monitor, primary, secondary := assignMembers()
		}

		case
		Status.State
	}()
	return nil
}

func waitForClusterFull() {
	for {
		if len(Cluster()) > 2 {
			fmt.Println("members are all online!")
			return
		}
		time.Sleep(time.Second)
	}
}

func assignMembers() monitor, primary, secondary Status {

}<commit_msg>update decisions<commit_after>package main

import (
	"time"
)

var lastKnownCluster *[]Status

func DecisionStart() error {
	waitForClusterFull()
	go func() {
		self := WhoAmI()
		if self.CRole == "monitor" {
			fmt.Println("im a monitor.. i dont make decisions")
			return nil
		}
		// figure out what to start as.
		startDb()
		lastKnownCluster = Cluster()
		for {
			if clusterChanges() {
				performAction()
			}
			// check for node dropping
			// if node drops react accordingly
		}

	}()
	return nil
}

// this will ping the cluster until it has the
// appropriate number of members
func waitForClusterFull() {
	for {
		if len(Cluster()) > 2 {
			fmt.Println("members are all online!")
			return
		}
		time.Sleep(time.Second)
	}
}

// figure out what to start as.
func startDb() {
	self := WhoAmI()
	switch self.CRole {
  case "primary":
  	action <- startType("master")
  case "secondary":
  	action <- startType("slave")
  default:
  	fmt.Println("Monitors dont do anything.")
  }
}

func startType(def string) string {
	self := WhoAmI()
	switch self.DBRole {
	case "initialized":
		return def
	case "single":
		return "master"
	case "master":
		// check the other node and see if it is single
		// if not i stay master
		// if so i go secondary
		other, err := WhoIs(otherRole(self))
		if other.DBRole == "single" {
			return "slave"
		}
		retun "master"
	case "slave":
		return "slave"
	}
	panic("i should have caught all scenarios")
	return def
}

func clusterChanges() bool {
	for _, member, := lastKnownCluster {
		if member != WhoIs(member.CRole) {
			lastKnownCluster = Cluster()
			return true
		}
	}
	return false
}

func performAction() {
	self, err := WhoAmI()
	other, err := WhoIs(otherRole(self))

	switch self.DBRole {
	case "single":

		return "master"
	case "master":
		// check the other node and see if it is single
		// if not i stay master
		// if so i go secondary
		if self.CRole == "primary" {
			sec := WhoIs("secondary")
			if sec.DBRole == "single" {
				return "slave"
			}
		}
		retun "master"
	case "slave":
		return "slave"
	}
}

func otherRole(st Status) string {
	if st.CRole == "primary" {
		return "secondary"
	} else {
		return "primary"
	}
	return ""
}<|endoftext|>


<commit_before>package guid

import (
	"errors"
	"fmt"
	"math/rand"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

// GUID is a unique identifier designed to virtually guarantee non-conflict between values generated
// across a distributed system.
type GUID struct {
	timeHighAndVersion      uint16
	timeMid                 uint16
	timeLow                 uint32
	clockSeqHighAndReserved uint8
	clockSeqLow             uint8
	node                    [6]byte
}

// Format enumerates the values that are supported by Parse and Format
type Format string

// These constants define the possible string formats available via this implementation of Guid.
const (
	FormatB       Format = "B"
	FormatD       Format = "D"
	FormatN       Format = "N"
	FormatP       Format = "P"
	FormatX       Format = "X"
	FormatDefault Format = FormatD
)

// CreationStrategy enumerates the values that are supported for populating the bits of a new Guid.
type CreationStrategy string

// These constants define the possible creation strategies available via this implementation of Guid.
const (
	CreationStrategyRFC4122Version1 CreationStrategy = "version1"
	CreationStrategyRFC4122Version2 CreationStrategy = "version2"
	CreationStrategyRFC4122Version3 CreationStrategy = "version3"
	CreationStrategyRFC4122Version4 CreationStrategy = "version4"
	CreationStrategyRFC4122Version5 CreationStrategy = "version5"
)

const localHost = "127.0.0.1"

var (
	emptyGUID GUID
)

// NewGUID generates and returns a new globally unique identifier
func NewGUID() GUID {
	result, err := version4()
	if err != nil {
		panic(err) //Version 4 (psuedo-random GUID) doesn't use anything that could fail.
	}
	return result
}

var knownStrategies = map[CreationStrategy]func() (GUID, error){
	CreationStrategyRFC4122Version1: version1,
	CreationStrategyRFC4122Version4: version4,
}

// NewGUIDs generates and returns a new globally unique identifier that conforms to the given strategy.
func NewGUIDs(strategy CreationStrategy) (GUID, error) {
	if creator, present := knownStrategies[strategy]; present {
		result, err := creator()
		return result, err
	}
	return emptyGUID, errors.New("Unsupported CreationStrategy")
}

// Empty returns a copy of the default and empty GUID.
func Empty() GUID {
	return emptyGUID
}

var knownFormats = map[Format]string{
	FormatN: "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
	FormatD: "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
	FormatB: "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
	FormatP: "(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
	FormatX: "{0x%08x,0x%04x,0x%04x,{0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}}",
}

// Parse instantiates a GUID from a text represention of the same GUID.
// This is the inverse of function family String()
func Parse(value string) (GUID, error) {
	var guid GUID
	for _, fullFormat := range knownFormats {
		parity, err := fmt.Sscanf(
			value,
			fullFormat,
			&guid.timeLow,
			&guid.timeMid,
			&guid.timeHighAndVersion,
			&guid.clockSeqHighAndReserved,
			&guid.clockSeqLow,
			&guid.node[0],
			&guid.node[1],
			&guid.node[2],
			&guid.node[3],
			&guid.node[4],
			&guid.node[5])
		if parity == 11 && err == nil {
			return guid, err
		}
	}
	return emptyGUID, fmt.Errorf("\"%s\" is not in a recognized format", value)
}

func (guid *GUID) String() string {
	result, _ := guid.Stringf(FormatDefault)
	return result
}

// Stringf returns a text representation of a GUID that conforms to the specified format.
func (guid *GUID) Stringf(format Format) (string, error) {
	if format == "" {
		format = FormatDefault
	}
	fullFormat, present := knownFormats[format]
	if !present {
		return "", fmt.Errorf("%s is not a recognized format string. Please choose from", format)
	}
	return fmt.Sprintf(
		fullFormat,
		guid.timeLow,
		guid.timeMid,
		guid.timeHighAndVersion,
		guid.clockSeqHighAndReserved,
		guid.clockSeqLow,
		guid.node[0],
		guid.node[1],
		guid.node[2],
		guid.node[3],
		guid.node[4],
		guid.node[5]), nil
}

// Version reads a GUID to parse which mechanism of generating GUIDS was employed.
// Values returned here are documented in rfc4122.txt.
func (guid *GUID) Version() uint {
	return uint(guid.timeHighAndVersion >> 12)
}

const (
	creationFileMode      os.FileMode = os.ModeExclusive
	creationStateFilePath string      = "foo.lock"
)

var unixToGregorianOffset = time.Date(1970, 01, 01, 0, 0, 00, 0, time.UTC).Sub(time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC))

// getRFC4122Time returns a 60-bit count of 100-nanosecond intervals since 00:00:00.00 October 15th, 1582
func getRFC4122Time() int64 {
	currentTime := time.Now().UTC().Add(unixToGregorianOffset).UnixNano()
	currentTime /= 100
	return currentTime & 0x0FFFFFFFFFFFFFFF
}

var clockSeqVal uint16
var clockSeqKey sync.Mutex

func getClockSequence() uint16 {
	clockSeqKey.Lock()
	defer clockSeqKey.Unlock()

	if 0 == clockSeqVal {
		clockSeqVal = uint16(rand.Uint32())
	}
	clockSeqVal++
	return clockSeqVal
}

func getMACAddress() ([6]byte, error) {
	var retval [6]byte
	var err error
	var hostNICs []net.Interface

	hostNICs, err = net.Interfaces()
	if nil == err {
		found := false
		for _, nic := range hostNICs {
			var parity int

			parity, err = fmt.Sscanf(
				strings.ToLower(nic.HardwareAddr.String()),
				"%02x:%02x:%02x:%02x:%02x:%02x",
				&retval[0],
				&retval[1],
				&retval[2],
				&retval[3],
				&retval[4],
				&retval[5])
			if parity == len(retval) {
				found = true
				break
			}
		}
		if !found {
			err = fmt.Errorf("No suitable address found")
		}
	}
	return retval, err
}

func version1() (GUID, error) {
	var retval GUID
	var localMAC [6]byte
	var err error

	currentTime := getRFC4122Time()

	retval.timeLow = uint32(currentTime)
	retval.timeMid = uint16(currentTime >> 32)
	retval.timeHighAndVersion = uint16(currentTime >> 48)
	if err = retval.setVersion(1); err != nil {
		return emptyGUID, err
	}

	if localMAC, err = getMACAddress(); nil != err {
		return emptyGUID, err
	}
	copy(retval.node[:], localMAC[:])

	clockSeq := getClockSequence()

	retval.clockSeqLow = uint8(clockSeq)
	retval.clockSeqHighAndReserved = uint8(clockSeq >> 8)

	retval.setReservedBits()

	return retval, nil
}

func version4() (GUID, error) {
	var retval GUID
	var bits uint32

	// Randomly set all time components and version
	bits = rand.Uint32()
	retval.timeHighAndVersion |= uint16(bits >> 16)
	retval.timeMid |= uint16(bits)
	bits = rand.Uint32()
	retval.timeLow = bits
	bits = rand.Uint32()
	retval.clockSeqHighAndReserved = uint8(bits)
	retval.clockSeqLow = uint8(bits >> 8)

	//Randomly set clock-sequence, reserved, and node
	if written, err := rand.Read(retval.node[:]); !(nil == err && written == len(retval.node)) {
		retval = emptyGUID
		return retval, err
	}

	if err := retval.setVersion(4); nil != err {
		return emptyGUID, err
	}
	retval.setReservedBits()

	return retval, nil
}

func (guid *GUID) setVersion(version uint16) error {
	if version > 5 {
		return fmt.Errorf("While setting GUID version, unsupported version: %d", version)
	}
	guid.timeHighAndVersion = (guid.timeHighAndVersion & 0x0fff) | version<<12
	return nil
}

func (guid *GUID) setReservedBits() {
	guid.clockSeqHighAndReserved = (guid.clockSeqHighAndReserved & 0x3f) | 0x80
}
<commit_msg>Moving to crypto/rand in order to satisfy gas.<commit_after>package guid

import (
	"crypto/rand"
	"errors"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

// GUID is a unique identifier designed to virtually guarantee non-conflict between values generated
// across a distributed system.
type GUID struct {
	timeHighAndVersion      uint16
	timeMid                 uint16
	timeLow                 uint32
	clockSeqHighAndReserved uint8
	clockSeqLow             uint8
	node                    [6]byte
}

// Format enumerates the values that are supported by Parse and Format
type Format string

// These constants define the possible string formats available via this implementation of Guid.
const (
	FormatB       Format = "B"
	FormatD       Format = "D"
	FormatN       Format = "N"
	FormatP       Format = "P"
	FormatX       Format = "X"
	FormatDefault Format = FormatD
)

// CreationStrategy enumerates the values that are supported for populating the bits of a new Guid.
type CreationStrategy string

// These constants define the possible creation strategies available via this implementation of Guid.
const (
	CreationStrategyRFC4122Version1 CreationStrategy = "version1"
	CreationStrategyRFC4122Version2 CreationStrategy = "version2"
	CreationStrategyRFC4122Version3 CreationStrategy = "version3"
	CreationStrategyRFC4122Version4 CreationStrategy = "version4"
	CreationStrategyRFC4122Version5 CreationStrategy = "version5"
)

const localHost = "127.0.0.1"

var emptyGUID GUID

// NewGUID generates and returns a new globally unique identifier
func NewGUID() GUID {
	result, err := version4()
	if err != nil {
		panic(err) //Version 4 (psuedo-random GUID) doesn't use anything that could fail.
	}
	return result
}

var knownStrategies = map[CreationStrategy]func() (GUID, error){
	CreationStrategyRFC4122Version1: version1,
	CreationStrategyRFC4122Version4: version4,
}

// NewGUIDs generates and returns a new globally unique identifier that conforms to the given strategy.
func NewGUIDs(strategy CreationStrategy) (GUID, error) {
	if creator, present := knownStrategies[strategy]; present {
		result, err := creator()
		return result, err
	}
	return emptyGUID, errors.New("Unsupported CreationStrategy")
}

// Empty returns a copy of the default and empty GUID.
func Empty() GUID {
	return emptyGUID
}

var knownFormats = map[Format]string{
	FormatN: "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
	FormatD: "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
	FormatB: "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
	FormatP: "(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
	FormatX: "{0x%08x,0x%04x,0x%04x,{0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}}",
}

// Parse instantiates a GUID from a text represention of the same GUID.
// This is the inverse of function family String()
func Parse(value string) (GUID, error) {
	var guid GUID
	for _, fullFormat := range knownFormats {
		parity, err := fmt.Sscanf(
			value,
			fullFormat,
			&guid.timeLow,
			&guid.timeMid,
			&guid.timeHighAndVersion,
			&guid.clockSeqHighAndReserved,
			&guid.clockSeqLow,
			&guid.node[0],
			&guid.node[1],
			&guid.node[2],
			&guid.node[3],
			&guid.node[4],
			&guid.node[5])
		if parity == 11 && err == nil {
			return guid, err
		}
	}
	return emptyGUID, fmt.Errorf("\"%s\" is not in a recognized format", value)
}

func (guid *GUID) String() string {
	if result, err := guid.Stringf(FormatDefault); err == nil {
		return result
	}
	return ""
}

// Stringf returns a text representation of a GUID that conforms to the specified format.
func (guid *GUID) Stringf(format Format) (string, error) {
	if format == "" {
		format = FormatDefault
	}
	fullFormat, present := knownFormats[format]
	if !present {
		return "", fmt.Errorf("%s is not a recognized format string. Please choose from", format)
	}
	return fmt.Sprintf(
		fullFormat,
		guid.timeLow,
		guid.timeMid,
		guid.timeHighAndVersion,
		guid.clockSeqHighAndReserved,
		guid.clockSeqLow,
		guid.node[0],
		guid.node[1],
		guid.node[2],
		guid.node[3],
		guid.node[4],
		guid.node[5]), nil
}

// Version reads a GUID to parse which mechanism of generating GUIDS was employed.
// Values returned here are documented in rfc4122.txt.
func (guid *GUID) Version() uint {
	return uint(guid.timeHighAndVersion >> 12)
}

const (
	creationFileMode      os.FileMode = os.ModeExclusive
	creationStateFilePath string      = "foo.lock"
)

var unixToGregorianOffset = time.Date(1970, 01, 01, 0, 0, 00, 0, time.UTC).Sub(time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC))

// getRFC4122Time returns a 60-bit count of 100-nanosecond intervals since 00:00:00.00 October 15th, 1582
func getRFC4122Time() int64 {
	currentTime := time.Now().UTC().Add(unixToGregorianOffset).UnixNano()
	currentTime /= 100
	return currentTime & 0x0FFFFFFFFFFFFFFF
}

var clockSeqVal uint16
var clockSeqKey sync.Mutex

func getClockSequence() (uint16, error) {
	clockSeqKey.Lock()
	defer clockSeqKey.Unlock()

	if 0 == clockSeqVal {
		var temp [2]byte
		if parity, err := rand.Read(temp[:]); !(2 == parity && nil == err) {
			return 0, err
		}
		clockSeqVal = uint16(temp[0])<<8 | uint16(temp[1])
	}
	clockSeqVal++
	return clockSeqVal, nil
}

func getMACAddress() ([6]byte, error) {
	var retval [6]byte
	var err error
	var hostNICs []net.Interface

	hostNICs, err = net.Interfaces()
	if nil == err {
		found := false
		for _, nic := range hostNICs {
			var parity int

			parity, err = fmt.Sscanf(
				strings.ToLower(nic.HardwareAddr.String()),
				"%02x:%02x:%02x:%02x:%02x:%02x",
				&retval[0],
				&retval[1],
				&retval[2],
				&retval[3],
				&retval[4],
				&retval[5])
			if parity == len(retval) {
				found = true
				break
			}
		}
		if !found {
			err = fmt.Errorf("No suitable address found")
		}
	}
	return retval, err
}

func version1() (GUID, error) {
	var retval GUID
	var localMAC [6]byte
	var err error
	var clockSeq uint16

	currentTime := getRFC4122Time()

	retval.timeLow = uint32(currentTime)
	retval.timeMid = uint16(currentTime >> 32)
	retval.timeHighAndVersion = uint16(currentTime >> 48)
	if err = retval.setVersion(1); err != nil {
		return emptyGUID, err
	}

	if localMAC, err = getMACAddress(); nil != err {
		return emptyGUID, err
	}
	copy(retval.node[:], localMAC[:])

	if clockSeq, err = getClockSequence(); nil != err {
		return emptyGUID, err
	}

	retval.clockSeqLow = uint8(clockSeq)
	retval.clockSeqHighAndReserved = uint8(clockSeq >> 8)

	retval.setReservedBits()

	return retval, nil
}

func version4() (GUID, error) {
	var retval GUID
	var bits [10]byte

	if parity, err := rand.Read(bits[:]); !(len(bits) == parity && err == nil) {
		return emptyGUID, err
	}
	retval.timeHighAndVersion |= uint16(bits[0]) | uint16(bits[1])<<8
	retval.timeMid |= uint16(bits[2]) | uint16(bits[3])<<8
	retval.timeLow |= uint32(bits[4]) | uint32(bits[5])<<8 | uint32(bits[6])<<16 | uint32(bits[7])<<24
	retval.clockSeqHighAndReserved = uint8(bits[8])
	retval.clockSeqLow = uint8(bits[9])

	//Randomly set clock-sequence, reserved, and node
	if written, err := rand.Read(retval.node[:]); !(nil == err && written == len(retval.node)) {
		retval = emptyGUID
		return retval, err
	}

	if err := retval.setVersion(4); nil != err {
		return emptyGUID, err
	}
	retval.setReservedBits()

	return retval, nil
}

func (guid *GUID) setVersion(version uint16) error {
	if version > 5 {
		return fmt.Errorf("While setting GUID version, unsupported version: %d", version)
	}
	guid.timeHighAndVersion = (guid.timeHighAndVersion & 0x0fff) | version<<12
	return nil
}

func (guid *GUID) setReservedBits() {
	guid.clockSeqHighAndReserved = (guid.clockSeqHighAndReserved & 0x3f) | 0x80
}
<|endoftext|>