<commit_before>package weave

import (
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	"log"
	"net"
	"syscall"
	"time"
)

func (conn *LocalConnection) ensureForwarders() error {
	if conn.forwardChan != nil || conn.forwardChanDF != nil {
		return nil
	}
	// only thing that can error, so do it earlier
	rawUDPSender, err := NewRawUDPSender(conn)
	if err != nil {
		return err
	}

	usingPassword := conn.SessionKey != nil
	var encryptor, encryptorDF Encryptor
	if usingPassword {
		encryptor = NewNaClEncryptor(conn.local.NameByte, conn, false)
		encryptorDF = NewNaClEncryptor(conn.local.NameByte, conn, true)
	} else {
		encryptor = NewNonEncryptor(conn.local.NameByte)
		encryptorDF = NewNonEncryptor(conn.local.NameByte)
	}

	// The forward chans in the conn struct are read by other
	// processes, so we have to use locks.
	var (
		forwardChan   = make(chan *ForwardedFrame, ChannelSize)
		forwardChanDF = make(chan *ForwardedFrame, ChannelSize)
		stopForward   = make(chan interface{}, 0)
		stopForwardDF = make(chan interface{}, 0)
	)
	conn.Lock()
	conn.forwardChan = forwardChan
	conn.forwardChanDF = forwardChanDF
	conn.stopForward = stopForward
	conn.stopForwardDF = stopForwardDF
	conn.Unlock()
	go conn.forwarderLoop(forwardChan, stopForward, encryptor, NewSimpleUDPSender(conn), DefaultPMTU)
	go conn.forwarderLoop(forwardChanDF, stopForwardDF, encryptorDF, rawUDPSender, conn.clientPMTU)

	return nil
}

func (conn *LocalConnection) stopForwarders() {
	conn.Lock()
	conn.forwardChan = nil
	conn.forwardChanDF = nil
	conn.Unlock()
	// Now signal the forwarder loops to exit. They will drain the
	// forwarder chans in order to unblock any router processes
	// blocked on sending.
	if conn.stopForward != nil {
		conn.stopForward <- nil
		conn.stopForwardDF <- nil
	}
}

func (conn *LocalConnection) forwarderLoop(forwardCh <-chan *ForwardedFrame, stop <-chan interface{}, enc Encryptor, udpSender UDPSender, pmtu int) {
	defer udpSender.Shutdown()
	// The pmtu governs the max size of msg we can pass to the
	// udpSender. udpSender will add in the UDP/IP headers
	var pmtuVerifyTick <-chan time.Time
	var clientPMTU int
	maxPayload := pmtu - UDPOverhead
	calculateClientPMTU := func(ftbe FrameTooBigError) {
		maxPayload = ftbe.PMTU - UDPOverhead
		clientPMTU = maxPayload - enc.PacketOverhead() - enc.FrameOverhead() - EthernetOverhead
	}
	updateClientPMTU := func() {
		for gotNewPMTU := true; gotNewPMTU; {
			conn.setClientPMTU(clientPMTU)
			pmtuVerifyFrame := &ForwardedFrame{
				srcPeer: conn.local,
				dstPeer: conn.remote,
				frame:   make([]byte, clientPMTU+EthernetOverhead)}
			gotNewPMTU = false
			for cnt := 0; cnt < 10; cnt += 1 {
				enc.AppendFrame(pmtuVerifyFrame)
				err := udpSender.Send(enc.Bytes())
				if err != nil {
					if ftbe, ok := err.(FrameTooBigError); ok {
						calculateClientPMTU(ftbe)
						gotNewPMTU = true
						break
					}
				}
			}
		}
		pmtuVerifyTick = time.After(PMTUVerifyTimeout)
	}
	appendFrame := func(frame *ForwardedFrame) bool {
		frameLen := len(frame.frame)
		if enc.TotalLen()+enc.FrameOverhead()+frameLen > maxPayload {
			return false
		}
		enc.AppendFrame(frame)
		return true
	}
	flush := func() {
		err := udpSender.Send(enc.Bytes())
		if err != nil {
			if ftbe, ok := err.(FrameTooBigError); ok {
				calculateClientPMTU(ftbe)
				updateClientPMTU()
			} else if PosixError(err) == syscall.ENOBUFS {
				// TODO handle this better
			} else {
				conn.CheckFatal(err)
			}
		}
	}
	logDrop := func(frame *ForwardedFrame) {
		conn.log("dropping too big frame during forwarding: frame len:", len(frame.frame), "; PMTU:", pmtu)
	}
	// We want to drain before exiting otherwise we could get the
	// packet sniffer or udp listener blocked on sending to a full
	// chan
	drain := func() {
		for {
			select {
			case <-forwardCh:
			default:
				return
			}
		}
	}
	var flushed, ok bool
	var frame *ForwardedFrame
	for {
		flushed = false
		select {
		case <-stop:
			drain()
			return
		case <-pmtuVerifyTick:
			// We only do this case here when we know the buffers are
			// all empty so that we don't risk appending verify-frames
			// to other data.
			pmtuVerifyTick = nil
			if !conn.isClientPMTUVerfied() {
				clientPMTU -= 8
				maxPayload -= 8
				updateClientPMTU()
			}
		case frame = <-forwardCh:
			if !appendFrame(frame) {
				logDrop(frame)
				continue
			}
			for !flushed {
				select {
				case frame, ok = <-forwardCh:
					if !ok {
						return
					}
					if !appendFrame(frame) {
						flush()
						if !appendFrame(frame) {
							logDrop(frame)
							flushed = true
						}
					}
				default:
					flush()
					flushed = true
				}
			}
		}
	}
}

// Called from peer.Relay[Broadcast] which is itself invoked from
// router (both UDP listener process and sniffer process). Also called
// from connection's heartbeat process, and from the connection's TCP
// receiver process.
func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *EthernetDecoder) error {
	conn.RLock()
	var (
		forwardChan   = conn.forwardChan
		forwardChanDF = conn.forwardChanDF
		clientPMTU    = conn.clientPMTU
		stackFrag     = conn.stackFrag
	)
	conn.RUnlock()

	if forwardChan == nil || forwardChanDF == nil {
		conn.log("Cannot forward frame yet - awaiting contact")
		return nil
	}
	if df {
		if len(frame.frame)-EthernetOverhead <= clientPMTU {
			forwardChanDF <- frame
			return nil
		} else {
			return FrameTooBigError{PMTU: clientPMTU, frame: frame}
		}
	} else {
		if stackFrag || dec == nil || len(dec.decoded) < 2 {
			forwardChan <- frame
			return nil
		}
		// Don't have trustworthy stack, so we're going to have to
		// send it DF in any case.
		if len(frame.frame)-EthernetOverhead <= clientPMTU {
			forwardChanDF <- frame
			return nil
		}
		conn.Router.LogFrame("Fragmenting", frame.frame, &dec.eth)
		// We can't trust the stack to fragment, we have IP, and we
		// have a frame that's too big for the MTU, so we have to
		// fragment it ourself.
		return fragment(dec.eth, dec.ip, clientPMTU, frame, func(segFrame *ForwardedFrame) {
			forwardChanDF <- segFrame
		})
	}
}

func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFrame, forward func(*ForwardedFrame)) error {
	// We are not doing any sort of NAT, so we don't need to worry
	// about checksums of IP payload (eg UDP checksum).
	headerSize := int(ip.IHL) * 4
	// &^ is bit clear (AND NOT). So here we're clearing the lowest 3
	// bits.
	maxSegmentSize := (pmtu - EthernetOverhead - headerSize) &^ 7
	opts := gopacket.SerializeOptions{
		FixLengths:       false,
		ComputeChecksums: true}
	payloadSize := int(ip.Length) - headerSize
	payload := ip.BaseLayer.Payload[:payloadSize]
	offsetBase := int(ip.FragOffset) << 3
	origFlags := ip.Flags
	ip.Flags = ip.Flags | layers.IPv4MoreFragments
	ip.Length = uint16(headerSize + maxSegmentSize)
	if eth.EthernetType == layers.EthernetTypeLLC {
		// using LLC, so must set eth length correctly. eth length
		// is just the length of the payload
		eth.Length = ip.Length
	} else {
		eth.Length = 0
	}
	for offset := 0; offset < payloadSize; offset += maxSegmentSize {
		var segmentPayload []byte
		if len(payload) <= maxSegmentSize {
			// last one
			segmentPayload = payload
			ip.Length = uint16(len(payload) + headerSize)
			ip.Flags = origFlags
			if eth.EthernetType == layers.EthernetTypeLLC {
				eth.Length = ip.Length
			} else {
				eth.Length = 0
			}
		} else {
			segmentPayload = payload[:maxSegmentSize]
			payload = payload[maxSegmentSize:]
		}
		ip.FragOffset = uint16((offset + offsetBase) >> 3)
		buf := gopacket.NewSerializeBuffer()
		segPayload := gopacket.Payload(segmentPayload)
		err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
		if err != nil {
			return err
		}
		// make copies of the frame we received
		var segFrame ForwardedFrame = *frame
		segFrame.frame = buf.Bytes()
		forward(&segFrame)
	}
	return nil
}

// UDP Senders

func NewSimpleUDPSender(conn *LocalConnection) *SimpleUDPSender {
	return &SimpleUDPSender{udpConn: conn.Router.UDPListener, conn: conn}
}

func (sender *SimpleUDPSender) Send(msg []byte) error {
	_, err := sender.udpConn.WriteToUDP(msg, sender.conn.RemoteUDPAddr())
	return err
}

func (sender *SimpleUDPSender) Shutdown() error {
	return nil
}

func NewRawUDPSender(conn *LocalConnection) (*RawUDPSender, error) {
	ipSocket, err := dialIP(conn)
	if err != nil {
		return nil, err
	}
	udpHeader := &layers.UDP{SrcPort: layers.UDPPort(Port)}
	ipBuf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{
		FixLengths: true,
		// UDP header is calculated with a phantom IP
		// header. Yes, it's totally nuts. Thankfully, for UDP
		// over IPv4, the checksum is optional. It's not
		// optional for IPv6, but we'll ignore that for
		// now. TODO
		ComputeChecksums: false}

	return &RawUDPSender{
		ipBuf:     ipBuf,
		opts:      opts,
		udpHeader: udpHeader,
		socket:    ipSocket,
		conn:      conn}, nil
}

func (sender *RawUDPSender) Send(msg []byte) error {
	payload := gopacket.Payload(msg)
	sender.udpHeader.DstPort = layers.UDPPort(sender.conn.RemoteUDPAddr().Port)

	err := gopacket.SerializeLayers(sender.ipBuf, sender.opts, sender.udpHeader, &payload)
	if err != nil {
		return err
	}
	packet := sender.ipBuf.Bytes()
	_, err = sender.socket.Write(packet)
	if err == nil || PosixError(err) != syscall.EMSGSIZE {
		return err
	}
	f, err := sender.socket.File()
	if err != nil {
		return err
	}
	defer f.Close()
	fd := int(f.Fd())
	log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was",
		len(packet), "bytes, payload was", len(msg), "bytes)")
	pmtu, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU)
	if err != nil {
		return err
	}
	return FrameTooBigError{PMTU: pmtu}
}

func (sender *RawUDPSender) Shutdown() error {
	defer func() { sender.socket = nil }()
	return sender.socket.Close()
}

func dialIP(conn *LocalConnection) (*net.IPConn, error) {
	ipLocalAddr, err := ipAddr(conn.TCPConn.LocalAddr())
	if err != nil {
		return nil, err
	}
	ipRemoteAddr, err := ipAddr(conn.TCPConn.RemoteAddr())
	if err != nil {
		return nil, err
	}
	ipSocket, err := net.DialIP("ip4:UDP", ipLocalAddr, ipRemoteAddr)
	if err != nil {
		return nil, err
	}
	f, err := ipSocket.File()
	if err != nil {
		return nil, err
	}
	defer f.Close()
	fd := int(f.Fd())
	// This Makes sure all packets we send out have DF set on them.
	err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
	if err != nil {
		return nil, err
	}
	return ipSocket, nil
}

func ipAddr(addr net.Addr) (*net.IPAddr, error) {
	host, _, err := net.SplitHostPort(addr.String())
	if err != nil {
		return nil, err
	}
	return &net.IPAddr{
		IP:   net.ParseIP(host),
		Zone: ""}, nil
}
<commit_msg>tiny refactor: rename, move<commit_after>package weave

import (
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	"log"
	"net"
	"syscall"
	"time"
)

func (conn *LocalConnection) ensureForwarders() error {
	if conn.forwardChan != nil || conn.forwardChanDF != nil {
		return nil
	}
	udpSender := NewSimpleUDPSender(conn)
	udpSenderDF, err := NewRawUDPSender(conn) // only thing that can error, so do it early
	if err != nil {
		return err
	}

	usingPassword := conn.SessionKey != nil
	var encryptor, encryptorDF Encryptor
	if usingPassword {
		encryptor = NewNaClEncryptor(conn.local.NameByte, conn, false)
		encryptorDF = NewNaClEncryptor(conn.local.NameByte, conn, true)
	} else {
		encryptor = NewNonEncryptor(conn.local.NameByte)
		encryptorDF = NewNonEncryptor(conn.local.NameByte)
	}

	// The forward chans in the conn struct are read by other
	// processes, so we have to use locks.
	var (
		forwardChan   = make(chan *ForwardedFrame, ChannelSize)
		forwardChanDF = make(chan *ForwardedFrame, ChannelSize)
		stopForward   = make(chan interface{}, 0)
		stopForwardDF = make(chan interface{}, 0)
	)
	conn.Lock()
	conn.forwardChan = forwardChan
	conn.forwardChanDF = forwardChanDF
	conn.stopForward = stopForward
	conn.stopForwardDF = stopForwardDF
	conn.Unlock()
	go conn.forwarderLoop(forwardChan, stopForward, encryptor, udpSender, DefaultPMTU)
	go conn.forwarderLoop(forwardChanDF, stopForwardDF, encryptorDF, udpSenderDF, conn.clientPMTU)

	return nil
}

func (conn *LocalConnection) stopForwarders() {
	conn.Lock()
	conn.forwardChan = nil
	conn.forwardChanDF = nil
	conn.Unlock()
	// Now signal the forwarder loops to exit. They will drain the
	// forwarder chans in order to unblock any router processes
	// blocked on sending.
	if conn.stopForward != nil {
		conn.stopForward <- nil
		conn.stopForwardDF <- nil
	}
}

func (conn *LocalConnection) forwarderLoop(forwardCh <-chan *ForwardedFrame, stop <-chan interface{}, enc Encryptor, udpSender UDPSender, pmtu int) {
	defer udpSender.Shutdown()
	// The pmtu governs the max size of msg we can pass to the
	// udpSender. udpSender will add in the UDP/IP headers
	var pmtuVerifyTick <-chan time.Time
	var clientPMTU int
	maxPayload := pmtu - UDPOverhead
	calculateClientPMTU := func(ftbe FrameTooBigError) {
		maxPayload = ftbe.PMTU - UDPOverhead
		clientPMTU = maxPayload - enc.PacketOverhead() - enc.FrameOverhead() - EthernetOverhead
	}
	updateClientPMTU := func() {
		for gotNewPMTU := true; gotNewPMTU; {
			conn.setClientPMTU(clientPMTU)
			pmtuVerifyFrame := &ForwardedFrame{
				srcPeer: conn.local,
				dstPeer: conn.remote,
				frame:   make([]byte, clientPMTU+EthernetOverhead)}
			gotNewPMTU = false
			for cnt := 0; cnt < 10; cnt += 1 {
				enc.AppendFrame(pmtuVerifyFrame)
				err := udpSender.Send(enc.Bytes())
				if err != nil {
					if ftbe, ok := err.(FrameTooBigError); ok {
						calculateClientPMTU(ftbe)
						gotNewPMTU = true
						break
					}
				}
			}
		}
		pmtuVerifyTick = time.After(PMTUVerifyTimeout)
	}
	appendFrame := func(frame *ForwardedFrame) bool {
		frameLen := len(frame.frame)
		if enc.TotalLen()+enc.FrameOverhead()+frameLen > maxPayload {
			return false
		}
		enc.AppendFrame(frame)
		return true
	}
	flush := func() {
		err := udpSender.Send(enc.Bytes())
		if err != nil {
			if ftbe, ok := err.(FrameTooBigError); ok {
				calculateClientPMTU(ftbe)
				updateClientPMTU()
			} else if PosixError(err) == syscall.ENOBUFS {
				// TODO handle this better
			} else {
				conn.CheckFatal(err)
			}
		}
	}
	logDrop := func(frame *ForwardedFrame) {
		conn.log("dropping too big frame during forwarding: frame len:", len(frame.frame), "; PMTU:", pmtu)
	}
	// We want to drain before exiting otherwise we could get the
	// packet sniffer or udp listener blocked on sending to a full
	// chan
	drain := func() {
		for {
			select {
			case <-forwardCh:
			default:
				return
			}
		}
	}
	var flushed, ok bool
	var frame *ForwardedFrame
	for {
		flushed = false
		select {
		case <-stop:
			drain()
			return
		case <-pmtuVerifyTick:
			// We only do this case here when we know the buffers are
			// all empty so that we don't risk appending verify-frames
			// to other data.
			pmtuVerifyTick = nil
			if !conn.isClientPMTUVerfied() {
				clientPMTU -= 8
				maxPayload -= 8
				updateClientPMTU()
			}
		case frame = <-forwardCh:
			if !appendFrame(frame) {
				logDrop(frame)
				continue
			}
			for !flushed {
				select {
				case frame, ok = <-forwardCh:
					if !ok {
						return
					}
					if !appendFrame(frame) {
						flush()
						if !appendFrame(frame) {
							logDrop(frame)
							flushed = true
						}
					}
				default:
					flush()
					flushed = true
				}
			}
		}
	}
}

// Called from peer.Relay[Broadcast] which is itself invoked from
// router (both UDP listener process and sniffer process). Also called
// from connection's heartbeat process, and from the connection's TCP
// receiver process.
func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *EthernetDecoder) error {
	conn.RLock()
	var (
		forwardChan   = conn.forwardChan
		forwardChanDF = conn.forwardChanDF
		clientPMTU    = conn.clientPMTU
		stackFrag     = conn.stackFrag
	)
	conn.RUnlock()

	if forwardChan == nil || forwardChanDF == nil {
		conn.log("Cannot forward frame yet - awaiting contact")
		return nil
	}
	if df {
		if len(frame.frame)-EthernetOverhead <= clientPMTU {
			forwardChanDF <- frame
			return nil
		} else {
			return FrameTooBigError{PMTU: clientPMTU, frame: frame}
		}
	} else {
		if stackFrag || dec == nil || len(dec.decoded) < 2 {
			forwardChan <- frame
			return nil
		}
		// Don't have trustworthy stack, so we're going to have to
		// send it DF in any case.
		if len(frame.frame)-EthernetOverhead <= clientPMTU {
			forwardChanDF <- frame
			return nil
		}
		conn.Router.LogFrame("Fragmenting", frame.frame, &dec.eth)
		// We can't trust the stack to fragment, we have IP, and we
		// have a frame that's too big for the MTU, so we have to
		// fragment it ourself.
		return fragment(dec.eth, dec.ip, clientPMTU, frame, func(segFrame *ForwardedFrame) {
			forwardChanDF <- segFrame
		})
	}
}

func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFrame, forward func(*ForwardedFrame)) error {
	// We are not doing any sort of NAT, so we don't need to worry
	// about checksums of IP payload (eg UDP checksum).
	headerSize := int(ip.IHL) * 4
	// &^ is bit clear (AND NOT). So here we're clearing the lowest 3
	// bits.
	maxSegmentSize := (pmtu - EthernetOverhead - headerSize) &^ 7
	opts := gopacket.SerializeOptions{
		FixLengths:       false,
		ComputeChecksums: true}
	payloadSize := int(ip.Length) - headerSize
	payload := ip.BaseLayer.Payload[:payloadSize]
	offsetBase := int(ip.FragOffset) << 3
	origFlags := ip.Flags
	ip.Flags = ip.Flags | layers.IPv4MoreFragments
	ip.Length = uint16(headerSize + maxSegmentSize)
	if eth.EthernetType == layers.EthernetTypeLLC {
		// using LLC, so must set eth length correctly. eth length
		// is just the length of the payload
		eth.Length = ip.Length
	} else {
		eth.Length = 0
	}
	for offset := 0; offset < payloadSize; offset += maxSegmentSize {
		var segmentPayload []byte
		if len(payload) <= maxSegmentSize {
			// last one
			segmentPayload = payload
			ip.Length = uint16(len(payload) + headerSize)
			ip.Flags = origFlags
			if eth.EthernetType == layers.EthernetTypeLLC {
				eth.Length = ip.Length
			} else {
				eth.Length = 0
			}
		} else {
			segmentPayload = payload[:maxSegmentSize]
			payload = payload[maxSegmentSize:]
		}
		ip.FragOffset = uint16((offset + offsetBase) >> 3)
		buf := gopacket.NewSerializeBuffer()
		segPayload := gopacket.Payload(segmentPayload)
		err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
		if err != nil {
			return err
		}
		// make copies of the frame we received
		var segFrame ForwardedFrame = *frame
		segFrame.frame = buf.Bytes()
		forward(&segFrame)
	}
	return nil
}

// UDP Senders

func NewSimpleUDPSender(conn *LocalConnection) *SimpleUDPSender {
	return &SimpleUDPSender{udpConn: conn.Router.UDPListener, conn: conn}
}

func (sender *SimpleUDPSender) Send(msg []byte) error {
	_, err := sender.udpConn.WriteToUDP(msg, sender.conn.RemoteUDPAddr())
	return err
}

func (sender *SimpleUDPSender) Shutdown() error {
	return nil
}

func NewRawUDPSender(conn *LocalConnection) (*RawUDPSender, error) {
	ipSocket, err := dialIP(conn)
	if err != nil {
		return nil, err
	}
	udpHeader := &layers.UDP{SrcPort: layers.UDPPort(Port)}
	ipBuf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{
		FixLengths: true,
		// UDP header is calculated with a phantom IP
		// header. Yes, it's totally nuts. Thankfully, for UDP
		// over IPv4, the checksum is optional. It's not
		// optional for IPv6, but we'll ignore that for
		// now. TODO
		ComputeChecksums: false}

	return &RawUDPSender{
		ipBuf:     ipBuf,
		opts:      opts,
		udpHeader: udpHeader,
		socket:    ipSocket,
		conn:      conn}, nil
}

func (sender *RawUDPSender) Send(msg []byte) error {
	payload := gopacket.Payload(msg)
	sender.udpHeader.DstPort = layers.UDPPort(sender.conn.RemoteUDPAddr().Port)

	err := gopacket.SerializeLayers(sender.ipBuf, sender.opts, sender.udpHeader, &payload)
	if err != nil {
		return err
	}
	packet := sender.ipBuf.Bytes()
	_, err = sender.socket.Write(packet)
	if err == nil || PosixError(err) != syscall.EMSGSIZE {
		return err
	}
	f, err := sender.socket.File()
	if err != nil {
		return err
	}
	defer f.Close()
	fd := int(f.Fd())
	log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was",
		len(packet), "bytes, payload was", len(msg), "bytes)")
	pmtu, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU)
	if err != nil {
		return err
	}
	return FrameTooBigError{PMTU: pmtu}
}

func (sender *RawUDPSender) Shutdown() error {
	defer func() { sender.socket = nil }()
	return sender.socket.Close()
}

func dialIP(conn *LocalConnection) (*net.IPConn, error) {
	ipLocalAddr, err := ipAddr(conn.TCPConn.LocalAddr())
	if err != nil {
		return nil, err
	}
	ipRemoteAddr, err := ipAddr(conn.TCPConn.RemoteAddr())
	if err != nil {
		return nil, err
	}
	ipSocket, err := net.DialIP("ip4:UDP", ipLocalAddr, ipRemoteAddr)
	if err != nil {
		return nil, err
	}
	f, err := ipSocket.File()
	if err != nil {
		return nil, err
	}
	defer f.Close()
	fd := int(f.Fd())
	// This Makes sure all packets we send out have DF set on them.
	err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
	if err != nil {
		return nil, err
	}
	return ipSocket, nil
}

func ipAddr(addr net.Addr) (*net.IPAddr, error) {
	host, _, err := net.SplitHostPort(addr.String())
	if err != nil {
		return nil, err
	}
	return &net.IPAddr{
		IP:   net.ParseIP(host),
		Zone: ""}, nil
}
<|endoftext|>


<commit_before>package elo

import (
	"testing"

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

func TestElo(t *testing.T) {
	config := Config{K: 32.0}

	type opponentOutcome struct {
		ro Rating
		o  Outcome
	}

	for _, tc := range []struct {
		r, rf Rating // Initial, final rating.
		games []opponentOutcome
	}{
		{r: 1613, rf: 1601, games: []opponentOutcome{
			{ro: 1609, o: Loss},
			{ro: 1477, o: Draw},
			{ro: 1388, o: Win},
			{ro: 1586, o: Win},
			{ro: 1720, o: Loss},
		}},
	} {
		r := tc.r
		for _, g := range tc.games {
			r = Update(r, g.ro, g.o, config)
		}
		assert.Equal(t, tc.rf, r)
	}
}
<commit_msg>Fix test.<commit_after>package elo

import (
	"testing"

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

func TestElo(t *testing.T) {
	config := Config{K: 32.0}

	type opponentOutcome struct {
		ro Rating
		o  Outcome
	}

	for _, tc := range []struct {
		r, rf Rating // Initial, final rating.
		games []opponentOutcome
	}{
		{r: 1613, rf: 1603, games: []opponentOutcome{
			{ro: 1609, o: Loss},
			{ro: 1477, o: Draw},
			{ro: 1388, o: Win},
			{ro: 1586, o: Win},
			{ro: 1720, o: Loss},
		}},
	} {
		r := tc.r
		for _, g := range tc.games {
			r = Update(r, g.ro, g.o, config)
		}
		assert.InDelta(t, float64(tc.rf), float64(r), .5)
	}
}
<|endoftext|>


<commit_before>package meritop

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strconv"

	"github.com/coreos/go-etcd/etcd"
)

// This interface is used by application during taskgraph configuration phase.
type Bootstrap interface {
	// These allow application developer to set the task configuration so framework
	// implementation knows which task to invoke at each node.
	SetTaskBuilder(taskBuilder TaskBuilder)

	// This allow the application to specify how tasks are connection at each epoch
	SetTopology(topology Topology)

	// After all the configure is done, driver need to call start so that all
	// nodes will get into the event loop to run the application.
	Start()
}

// Note that framework can decide how update can be done, and how to serve the updatelog.
type BackedUpFramework interface {
	// Ask framework to do update on this update on this task, which consists
	// of one primary and some backup copies.
	Update(taskID uint64, log UpdateLog)
}

type Framework interface {
	// These two are useful for task to inform the framework their status change.
	// metaData has to be really small, since it might be stored in etcd.
	// Flags that parent/child's metadata of the current task is ready.
	FlagParentMetaReady(meta string)
	FlagChildMetaReady(meta string)

	// This allow the task implementation query its neighbors.
	GetTopology() Topology

	// Some task can inform all participating tasks to exit.
	Exit()

	// This method will result in local node abort, the same task can be
	// retried by some other node. Only useful for panic inside user code.
	AbortTask()

	// Some task can inform all participating tasks to new epoch
	IncEpoch()

	GetLogger() log.Logger

	// Request data from parent or children.
	DataRequest(toID uint64, meta string)

	// This is used to figure out taskid for current node
	GetTaskID() uint64
}

// One need to pass in at least these two for framework to start. The config
// is used to pass on to task implementation for its configuration.
func NewBootStrap(jobName string, etcds []string, config Config) Bootstrap {
	return &framework{
		name:       jobName,
		etcdURLs:   etcds,
		taskConfig: config,
	}
}

type taskRole int

const (
	roleNone taskRole = iota
	roleParent
	roleChild
)

const (
	dataRequestPrefix string = "/datareq"
	dataRequestTaskID string = "taskID"
	dataRequestReq    string = "req"
)

type framework struct {
	// These should be passed by outside world
	name       string
	etcdURLs   []string
	taskConfig Config

	// user defined interfaces
	builder  TaskBuilder
	topology Topology

	task          Task
	taskID        uint64
	epoch         uint64
	etcdClient    *etcd.Client
	stops         []chan bool
	ln            net.Listener
	addressMap    map[uint64]string // taskId -> node address. Maybe in etcd later.
	dataRespChan  chan *dataResponse
	dataCloseChan chan struct{}
}

type dataResponse struct {
	taskID uint64
	req    string
	data   []byte
}

func (f *framework) SetTaskBuilder(taskBuilder TaskBuilder) {
	f.builder = taskBuilder
}

func (f *framework) SetTopology(topology Topology) {
	f.topology = topology
}

func (f *framework) parentOrChild(taskID uint64) taskRole {
	for _, id := range f.topology.GetParents(f.epoch) {
		if taskID == id {
			return roleParent
		}
	}

	for _, id := range f.topology.GetChildren(f.epoch) {
		if taskID == id {
			return roleChild
		}
	}
	return roleNone
}

func (f *framework) Start() {
	f.etcdClient = etcd.NewClient(f.etcdURLs)
	f.topology.SetTaskID(f.taskID)
	f.epoch = 0
	f.stops = make([]chan bool, 0)
	f.dataRespChan = make(chan *dataResponse, 100)
	f.dataCloseChan = make(chan struct{})

	// Get the task implementation for this node (indentified by taskID)
	// f.task = f.builder.GetTask(f.taskID)

	// setup etcd watches
	// - create self's parent and child meta flag
	// - watch parents' child meta flag
	// - watch children's parent meta flag
	f.etcdClient.Create(MakeParentMetaPath(f.name, f.GetTaskID()), "", 0)
	f.etcdClient.Create(MakeChildMetaPath(f.name, f.GetTaskID()), "", 0)
	parentStops := f.watchAll(roleParent, f.topology.GetParents(f.epoch))
	childStops := f.watchAll(roleChild, f.topology.GetChildren(f.epoch))
	f.stops = append(f.stops, parentStops...)
	f.stops = append(f.stops, childStops...)

	go f.startHttp()
	go f.dataResponseReceiver()

	// After framework init finished, it should init task.
	f.task.Init(f.taskID, f, nil)
	f.task.SetEpoch(f.epoch)
}

type dataReqHandler struct {
	f *framework
}

func (h *dataReqHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != dataRequestPrefix {
		http.Error(w, "bad path", http.StatusBadRequest)
		return
	}
	// parse url query
	q := r.URL.Query()
	fromIDStr := q.Get(dataRequestTaskID)
	fromID, err := strconv.ParseUint(fromIDStr, 0, 64)
	if err != nil {
		http.Error(w, "taskID couldn't be parsed", http.StatusBadRequest)
		return
	}
	req := q.Get(dataRequestReq)
	// ask task to serve data
	var b []byte
	switch h.f.parentOrChild(fromID) {
	case roleParent:
		b = h.f.task.ServeAsChild(fromID, req)
	case roleChild:
		b = h.f.task.ServeAsParent(fromID, req)
	default:
		http.Error(w, "taskID isn't a parent or child of this task", http.StatusBadRequest)
		return
	}

	if _, err := w.Write(b); err != nil {
		log.Printf("response write errored: %v", err)
	}
}

// Framework http server for data request.
// Each request will be in the format: "/datareq?taskID=XXX&req=XXX".
// "taskID" indicates the requesting task. "req" is the meta data for this request.
// On success, it should respond with requested data in http body.
func (f *framework) startHttp() {
	log.Printf("framework: serving http on %s", f.ln.Addr())
	if err := http.Serve(f.ln, &dataReqHandler{f}); err != nil {
		log.Fatalf("http.Serve() returns error: %v\n", err)
	}
}

// Framework event loop handles data response for requests sent in DataRequest().
func (f *framework) dataResponseReceiver() {
	for {
		select {
		case dataResp := <-f.dataRespChan:
			switch f.parentOrChild(dataResp.taskID) {
			case roleParent:
				go f.task.ParentDataReady(dataResp.taskID, dataResp.req, dataResp.data)
			case roleChild:
				go f.task.ChildDataReady(dataResp.taskID, dataResp.req, dataResp.data)
			default:
				panic("unimplemented")
			}
		case <-f.dataCloseChan:
			return
		}
	}
}

func (f *framework) stop() {
	close(f.dataCloseChan)
	for _, c := range f.stops {
		close(c)
	}
}

func (f *framework) FlagParentMetaReady(meta string) {
	f.etcdClient.Set(
		MakeParentMetaPath(f.name, f.GetTaskID()),
		meta,
		0)
}

func (f *framework) FlagChildMetaReady(meta string) {
	f.etcdClient.Set(
		MakeChildMetaPath(f.name, f.GetTaskID()),
		meta,
		0)
}

func (f *framework) IncEpoch() {
	f.epoch += 1
}

func (f *framework) watchAll(who taskRole, taskIDs []uint64) []chan bool {
	stops := make([]chan bool, len(taskIDs))

	for i, taskID := range taskIDs {
		receiver := make(chan *etcd.Response, 10)
		stop := make(chan bool, 1)
		stops[i] = stop

		var watchPath string
		var taskCallback func(uint64, string)
		switch who {
		case roleParent:
			// Watch parent's child.
			watchPath = MakeChildMetaPath(f.name, taskID)
			taskCallback = f.task.ParentMetaReady
		case roleChild:
			// Watch child's parent.
			watchPath = MakeParentMetaPath(f.name, taskID)
			taskCallback = f.task.ChildMetaReady
		default:
			panic("unimplemented")
		}

		go f.etcdClient.Watch(watchPath, 0, false, receiver, stop)
		go func(receiver <-chan *etcd.Response, taskID uint64) {
			for {
				resp, ok := <-receiver
				if !ok {
					return
				}
				if resp.Action != "set" {
					continue
				}
				taskCallback(taskID, resp.Node.Value)
			}
		}(receiver, taskID)
	}
	return stops
}

func (f *framework) DataRequest(toID uint64, req string) {
	// getAddressFromTaskID
	addr, ok := f.addressMap[toID]
	if !ok {
		log.Fatalf("ID = %d not found", toID)
		return
	}
	u := url.URL{
		Scheme: "http",
		Host:   addr,
		Path:   dataRequestPrefix,
	}
	q := u.Query()
	q.Add(dataRequestTaskID, strconv.FormatUint(f.taskID, 10))
	q.Add(dataRequestReq, req)
	u.RawQuery = q.Encode()
	urlStr := u.String()
	// send request
	// pass the response to the awaiting event loop for data response
	go func(urlStr string) {
		resp, err := http.Get(urlStr)
		if err != nil {
			log.Fatalf("http.Get(%s) returns error: %v", urlStr, err)
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			log.Fatalf("response code = %d, assume = %d", resp.StatusCode, 200)
		}
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatalf("ioutil.ReadAll(%v) returns error: %v", resp.Body, err)
		}
		dataResp := &dataResponse{
			taskID: toID,
			req:    req,
			data:   data,
		}
		f.dataRespChan <- dataResp
	}(urlStr)
}

func (f *framework) GetTopology() Topology {
	panic("unimplemented")
}

func (f *framework) Exit() {
}

func (f *framework) AbortTask() {
	panic("unimplemented")
}

func (f *framework) GetLogger() log.Logger {
	panic("unimplemented")
}

func (f *framework) GetTaskID() uint64 {
	return f.taskID
}
<commit_msg>provide hint on how the start should behave<commit_after>package meritop

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strconv"

	"github.com/coreos/go-etcd/etcd"
)

// This interface is used by application during taskgraph configuration phase.
type Bootstrap interface {
	// These allow application developer to set the task configuration so framework
	// implementation knows which task to invoke at each node.
	SetTaskBuilder(taskBuilder TaskBuilder)

	// This allow the application to specify how tasks are connection at each epoch
	SetTopology(topology Topology)

	// After all the configure is done, driver need to call start so that all
	// nodes will get into the event loop to run the application.
	Start()
}

// Note that framework can decide how update can be done, and how to serve the updatelog.
type BackedUpFramework interface {
	// Ask framework to do update on this update on this task, which consists
	// of one primary and some backup copies.
	Update(taskID uint64, log UpdateLog)
}

type Framework interface {
	// These two are useful for task to inform the framework their status change.
	// metaData has to be really small, since it might be stored in etcd.
	// Flags that parent/child's metadata of the current task is ready.
	FlagParentMetaReady(meta string)
	FlagChildMetaReady(meta string)

	// This allow the task implementation query its neighbors.
	GetTopology() Topology

	// Some task can inform all participating tasks to exit.
	Exit()

	// This method will result in local node abort, the same task can be
	// retried by some other node. Only useful for panic inside user code.
	AbortTask()

	// Some task can inform all participating tasks to new epoch
	IncEpoch()

	GetLogger() log.Logger

	// Request data from parent or children.
	DataRequest(toID uint64, meta string)

	// This is used to figure out taskid for current node
	GetTaskID() uint64
}

// One need to pass in at least these two for framework to start. The config
// is used to pass on to task implementation for its configuration.
func NewBootStrap(jobName string, etcds []string, config Config) Bootstrap {
	return &framework{
		name:       jobName,
		etcdURLs:   etcds,
		taskConfig: config,
	}
}

type taskRole int

const (
	roleNone taskRole = iota
	roleParent
	roleChild
)

const (
	dataRequestPrefix string = "/datareq"
	dataRequestTaskID string = "taskID"
	dataRequestReq    string = "req"
)

// This is used as special value to indicate that it is the last epoch, time
// to exit.
const maxUint64 uint64 = ^uint64(0)

type framework struct {
	// These should be passed by outside world
	name       string
	etcdURLs   []string
	taskConfig Config

	// user defined interfaces
	builder  TaskBuilder
	topology Topology

	task          Task
	taskID        uint64
	epoch         uint64
	etcdClient    *etcd.Client
	stops         []chan bool
	ln            net.Listener
	addressMap    map[uint64]string // taskId -> node address. Maybe in etcd later.
	dataRespChan  chan *dataResponse
	dataCloseChan chan struct{}
}

type dataResponse struct {
	taskID uint64
	req    string
	data   []byte
}

func (f *framework) SetTaskBuilder(taskBuilder TaskBuilder) {
	f.builder = taskBuilder
}

func (f *framework) SetTopology(topology Topology) {
	f.topology = topology
}

func (f *framework) parentOrChild(taskID uint64) taskRole {
	for _, id := range f.topology.GetParents(f.epoch) {
		if taskID == id {
			return roleParent
		}
	}

	for _, id := range f.topology.GetChildren(f.epoch) {
		if taskID == id {
			return roleChild
		}
	}
	return roleNone
}

func (f *framework) Start() {
	f.etcdClient = etcd.NewClient(f.etcdURLs)
	f.topology.SetTaskID(f.taskID)
	f.epoch = 0
	f.stops = make([]chan bool, 0)
	f.dataRespChan = make(chan *dataResponse, 100)
	f.dataCloseChan = make(chan struct{})

	// Get the task implementation for this node (indentified by taskID)
	// f.task = f.builder.GetTask(f.taskID)

	// setup etcd watches
	// - create self's parent and child meta flag
	// - watch parents' child meta flag
	// - watch children's parent meta flag
	f.etcdClient.Create(MakeParentMetaPath(f.name, f.GetTaskID()), "", 0)
	f.etcdClient.Create(MakeChildMetaPath(f.name, f.GetTaskID()), "", 0)
	parentStops := f.watchAll(roleParent, f.topology.GetParents(f.epoch))
	childStops := f.watchAll(roleChild, f.topology.GetChildren(f.epoch))
	f.stops = append(f.stops, parentStops...)
	f.stops = append(f.stops, childStops...)

	go f.startHttp()

	// After framework init finished, it should init task.
	f.task.Init(f.taskID, f, nil)
	f.task.SetEpoch(f.epoch)

	// TODO(hongchao)
	// We need to have two levels loop here so that we can effectively
	// stop the work that task is working on for last epoch.
	// for f.epoch != maxUint64 {
	// 	for exmple, this can be run here f.dataResponseReceiver()
	// }
	// you might want to just run the following function directly.
	go f.dataResponseReceiver()
}

type dataReqHandler struct {
	f *framework
}

func (h *dataReqHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != dataRequestPrefix {
		http.Error(w, "bad path", http.StatusBadRequest)
		return
	}
	// parse url query
	q := r.URL.Query()
	fromIDStr := q.Get(dataRequestTaskID)
	fromID, err := strconv.ParseUint(fromIDStr, 0, 64)
	if err != nil {
		http.Error(w, "taskID couldn't be parsed", http.StatusBadRequest)
		return
	}
	req := q.Get(dataRequestReq)
	// ask task to serve data
	var b []byte
	switch h.f.parentOrChild(fromID) {
	case roleParent:
		b = h.f.task.ServeAsChild(fromID, req)
	case roleChild:
		b = h.f.task.ServeAsParent(fromID, req)
	default:
		http.Error(w, "taskID isn't a parent or child of this task", http.StatusBadRequest)
		return
	}

	if _, err := w.Write(b); err != nil {
		log.Printf("response write errored: %v", err)
	}
}

// Framework http server for data request.
// Each request will be in the format: "/datareq?taskID=XXX&req=XXX".
// "taskID" indicates the requesting task. "req" is the meta data for this request.
// On success, it should respond with requested data in http body.
func (f *framework) startHttp() {
	log.Printf("framework: serving http on %s", f.ln.Addr())
	if err := http.Serve(f.ln, &dataReqHandler{f}); err != nil {
		log.Fatalf("http.Serve() returns error: %v\n", err)
	}
}

// Framework event loop handles data response for requests sent in DataRequest().
func (f *framework) dataResponseReceiver() {
	for {
		select {
		case dataResp := <-f.dataRespChan:
			switch f.parentOrChild(dataResp.taskID) {
			case roleParent:
				go f.task.ParentDataReady(dataResp.taskID, dataResp.req, dataResp.data)
			case roleChild:
				go f.task.ChildDataReady(dataResp.taskID, dataResp.req, dataResp.data)
			default:
				panic("unimplemented")
			}
		case <-f.dataCloseChan:
			return
		}
	}
}

func (f *framework) stop() {
	close(f.dataCloseChan)
	for _, c := range f.stops {
		close(c)
	}
}

func (f *framework) FlagParentMetaReady(meta string) {
	f.etcdClient.Set(
		MakeParentMetaPath(f.name, f.GetTaskID()),
		meta,
		0)
}

func (f *framework) FlagChildMetaReady(meta string) {
	f.etcdClient.Set(
		MakeChildMetaPath(f.name, f.GetTaskID()),
		meta,
		0)
}

func (f *framework) IncEpoch() {
	f.epoch += 1
}

func (f *framework) watchAll(who taskRole, taskIDs []uint64) []chan bool {
	stops := make([]chan bool, len(taskIDs))

	for i, taskID := range taskIDs {
		receiver := make(chan *etcd.Response, 10)
		stop := make(chan bool, 1)
		stops[i] = stop

		var watchPath string
		var taskCallback func(uint64, string)
		switch who {
		case roleParent:
			// Watch parent's child.
			watchPath = MakeChildMetaPath(f.name, taskID)
			taskCallback = f.task.ParentMetaReady
		case roleChild:
			// Watch child's parent.
			watchPath = MakeParentMetaPath(f.name, taskID)
			taskCallback = f.task.ChildMetaReady
		default:
			panic("unimplemented")
		}

		go f.etcdClient.Watch(watchPath, 0, false, receiver, stop)
		go func(receiver <-chan *etcd.Response, taskID uint64) {
			for {
				resp, ok := <-receiver
				if !ok {
					return
				}
				if resp.Action != "set" {
					continue
				}
				taskCallback(taskID, resp.Node.Value)
			}
		}(receiver, taskID)
	}
	return stops
}

func (f *framework) DataRequest(toID uint64, req string) {
	// getAddressFromTaskID
	addr, ok := f.addressMap[toID]
	if !ok {
		log.Fatalf("ID = %d not found", toID)
		return
	}
	u := url.URL{
		Scheme: "http",
		Host:   addr,
		Path:   dataRequestPrefix,
	}
	q := u.Query()
	q.Add(dataRequestTaskID, strconv.FormatUint(f.taskID, 10))
	q.Add(dataRequestReq, req)
	u.RawQuery = q.Encode()
	urlStr := u.String()
	// send request
	// pass the response to the awaiting event loop for data response
	go func(urlStr string) {
		resp, err := http.Get(urlStr)
		if err != nil {
			log.Fatalf("http.Get(%s) returns error: %v", urlStr, err)
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			log.Fatalf("response code = %d, assume = %d", resp.StatusCode, 200)
		}
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatalf("ioutil.ReadAll(%v) returns error: %v", resp.Body, err)
		}
		dataResp := &dataResponse{
			taskID: toID,
			req:    req,
			data:   data,
		}
		f.dataRespChan <- dataResp
	}(urlStr)
}

func (f *framework) GetTopology() Topology {
	panic("unimplemented")
}

func (f *framework) Exit() {
}

func (f *framework) AbortTask() {
	panic("unimplemented")
}

func (f *framework) GetLogger() log.Logger {
	panic("unimplemented")
}

func (f *framework) GetTaskID() uint64 {
	return f.taskID
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fs

import (
	"fmt"
	"io"
	"os"
	"syscall"
)

func (fs *fileSystem) CreateNamedPipe(path string, perms os.FileMode) error {
	return syscall.Mkfifo(path, syscallPermissions(perms))
}

func (fs *fileSystem) CreateBlockDevice(
	path string,
	perms os.FileMode,
	devNum int32) error {
	mode := syscallPermissions(perms) | syscall.S_IFBLK
	if err := syscall.Mknod(path, mode, int(devNum)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

func (fs *fileSystem) CreateCharDevice(
	path string,
	perms os.FileMode,
	devNum int32) error {
	mode := syscallPermissions(perms) | syscall.S_IFCHR
	if err := syscall.Mknod(path, mode, int(devNum)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

func (fs *fileSystem) CreateFile(
	path string,
	perms os.FileMode,
) (w io.WriteCloser, err error) {
	err = fmt.Errorf("TODO")
	return
}
<commit_msg>Implemented CreateFile.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fs

import (
	"fmt"
	"io"
	"os"
	"syscall"
)

func (fs *fileSystem) CreateNamedPipe(path string, perms os.FileMode) error {
	return syscall.Mkfifo(path, syscallPermissions(perms))
}

func (fs *fileSystem) CreateBlockDevice(
	path string,
	perms os.FileMode,
	devNum int32) error {
	mode := syscallPermissions(perms) | syscall.S_IFBLK
	if err := syscall.Mknod(path, mode, int(devNum)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

func (fs *fileSystem) CreateCharDevice(
	path string,
	perms os.FileMode,
	devNum int32) error {
	mode := syscallPermissions(perms) | syscall.S_IFCHR
	if err := syscall.Mknod(path, mode, int(devNum)); err != nil {
		return fmt.Errorf("syscall.Mknod: %v", err)
	}

	return nil
}

func (fs *fileSystem) CreateFile(
	path string,
	perms os.FileMode,
) (w io.WriteCloser, err error) {
	w, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perms)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/julienschmidt/httprouter"
)

func defaultPath() string {
	home := os.Getenv("HOME")
	if home == "" {
		log.Fatal("HOME environment variable not set")
	}
	return filepath.Join(home, "Music", "listentothis")
}

func init() {
	rand.Seed(time.Now().Unix())
}

func main() {
	path := defaultPath()

	r := httprouter.New()
	r.GET("/", Index)
	r.GET("/song/:filename", Index)
	r.POST("/song/:filename", PostProcess)
	r.ServeFiles("/files/*filepath", http.Dir(path))

	log.Fatal(http.ListenAndServe("127.0.0.1:8080", r))
}

func Index(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
	var err error
	filename := p.ByName("filename")
	if filename == "" {
		filename, err = randomFilename()
		if err == ErrNotFound {
			// TODO: react gracefully here
			http.Error(w, "no files found", http.StatusNotFound)
			return
		} else if err != nil {
			http.Error(w, fmt.Sprintf("failed to select random filename: %v", err), http.StatusInternalServerError)
			return
		}
	}

	err = playTemplate.Execute(w, struct {
		Filename string
	}{
		Filename: filename,
	})
	if err != nil {
		http.Error(w, "failed to execute template", http.StatusInternalServerError)
	}
}

var ErrNotFound = fmt.Errorf("not found")

func randomFilename() (string, error) {
	matches, err := filepath.Glob(filepath.Join(defaultPath(), "*.ogg"))
	if err != nil {
		return "", err
	}
	if len(matches) == 0 {
		return "", ErrNotFound
	}
	n := rand.Intn(len(matches))
	return filepath.Base(matches[n]), nil
}

func PostProcess(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
	filename := p.ByName("filename")
	if filename == "" {
		http.Error(w, "missing required filename parameter", http.StatusBadRequest)
		return
	}

	panic("TODO")
}

var playTemplate = template.Must(template.New("play").Parse(`

<link href="https://cdnjs.cloudflare.com/ajax/libs/jplayer/2.9.2/skin/blue.monday/css/jplayer.blue.monday.min.css" rel="stylesheet" type="text/css" />

{{ .Filename }}

<form action="/song/{{ .Filename }}" method="post">
	<input type="hidden" name="action" value="keep" />
	<input type="submit" value="Keep" />
</form>

				<button class="jp-mute" role="button" tabindex="0">mute</button>
				<button class="jp-volume-max" role="button" tabindex="0">max volume</button>

					<button class="jp-play" role="button" tabindex="0">play</button>
					<button class="jp-stop" role="button" tabindex="0">stop</button>

					<button class="jp-repeat" role="button" tabindex="0">repeat</button>

			Update Required
			To play the media you will need to either update your browser to a recent version or update your Flash plugin.

<form action="/" method="get" />
	<input type="submit" value="Next" />
</form>

<form action="/song/{{ .Filename }}" method="post">
	<input type="hidden" name="action" value="delete" />
	<input type="submit" value="Trash" />
</form>

`))
<commit_msg>Autoplay on document ready, refresh random song page on player ended.<commit_after>package main

import (
	"fmt"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/julienschmidt/httprouter"
)

func defaultPath() string {
	home := os.Getenv("HOME")
	if home == "" {
		log.Fatal("HOME environment variable not set")
	}
	return filepath.Join(home, "Music", "listentothis")
}

func init() {
	rand.Seed(time.Now().Unix())
}

func main() {
	path := defaultPath()

	r := httprouter.New()
	r.GET("/", Index)
	r.GET("/song/:filename", Index)
	r.POST("/song/:filename", PostProcess)
	r.ServeFiles("/files/*filepath", http.Dir(path))

	log.Fatal(http.ListenAndServe("127.0.0.1:8080", r))
}

func Index(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
	var err error
	filename := p.ByName("filename")
	if filename == "" {
		filename, err = randomFilename()
		if err == ErrNotFound {
			// TODO: react gracefully here
			http.Error(w, "no files found", http.StatusNotFound)
			return
		} else if err != nil {
			http.Error(w, fmt.Sprintf("failed to select random filename: %v", err), http.StatusInternalServerError)
			return
		}
	}

	err = playTemplate.Execute(w, struct {
		Filename string
	}{
		Filename: filename,
	})
	if err != nil {
		http.Error(w, "failed to execute template", http.StatusInternalServerError)
	}
}

var ErrNotFound = fmt.Errorf("not found")

func randomFilename() (string, error) {
	matches, err := filepath.Glob(filepath.Join(defaultPath(), "*.ogg"))
	if err != nil {
		return "", err
	}
	if len(matches) == 0 {
		return "", ErrNotFound
	}
	n := rand.Intn(len(matches))
	return filepath.Base(matches[n]), nil
}

func PostProcess(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
	filename := p.ByName("filename")
	if filename == "" {
		http.Error(w, "missing required filename parameter", http.StatusBadRequest)
		return
	}

	panic("TODO")
}

// TODO: make this all fancy and shit
var playTemplate = template.Must(template.New("play").Parse(`

<link href="https://cdnjs.cloudflare.com/ajax/libs/jplayer/2.9.2/skin/blue.monday/css/jplayer.blue.monday.min.css" rel="stylesheet" type="text/css" />

{{ .Filename }}

<form action="/song/{{ .Filename }}" method="post">
	<input type="hidden" name="action" value="keep" />
	<input type="submit" value="Keep" />
</form>

				<button class="jp-mute" role="button" tabindex="0">mute</button>
				<button class="jp-volume-max" role="button" tabindex="0">max volume</button>

					<button class="jp-play" role="button" tabindex="0">play</button>
					<button class="jp-stop" role="button" tabindex="0">stop</button>

					<button class="jp-repeat" role="button" tabindex="0">repeat</button>

			Update Required
			To play the media you will need to either update your browser to a recent version or update your Flash plugin.

<form action="/" method="get" />
	<input type="submit" value="Next" />
</form>

<form action="/song/{{ .Filename }}" method="post">
	<input type="hidden" name="action" value="delete" />
	<input type="submit" value="Trash" />
</form>

`))
<|endoftext|>


<commit_before>package main

import (
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

func main() {
	a := app.New()
	w := a.NewWindow("fyne")

	// w.Resize(fyne.NewSize(300, 300))
	// w.SetFixedSize(true)
	w.SetFullScreen(true)

	hello := widget.NewLabel("Hello Fyne!")

	w.SetContent(container.NewVBox(
		hello,
		widget.NewButton("Hi!", func() {
			hello.SetText("Welcome :)")
			w.CenterOnScreen()
		}),
		widget.NewButton("Quit", func() {
			a.Quit()
		}),
	))

	// w.RequestFocus() // TODO(dvrkps): panic on macos.

	w.ShowAndRun()
}
<commit_msg>fyne: new image from file<commit_after>package main

import (
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

func main() {
	a := app.New()
	w := a.NewWindow("fyne")

	// w.Resize(fyne.NewSize(300, 300))
	// w.SetFixedSize(true)
	w.SetFullScreen(true)

	hello := widget.NewLabel("Hello Fyne!")

	img := canvas.NewImageFromFile("logo.png")
	img.FillMode = canvas.ImageFillOriginal

	w.SetContent(container.NewVBox(
		img,
		hello,
		widget.NewButton("Hi!", func() {
			hello.SetText("Welcome :)")
			w.CenterOnScreen()
		}),
		widget.NewButton("Quit", func() {
			a.Quit()
		}),
	))

	// w.RequestFocus() // TODO(dvrkps): panic on macos.

	w.ShowAndRun()
}
<|endoftext|>


<commit_before>package scheduler

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/jhunt/go-log"

	"github.com/starkandwayne/shield/db"
)

var next = 0

type Chore struct {
	ID       string
	TaskUUID string

	Do func(chore Chore)

	Stdout chan string
	Stderr chan string
	Exit   chan int
	Cancel chan bool
}

func NewChore(id string, do func(Chore)) Chore {
	next += 1
	return Chore{
		ID:       fmt.Sprintf("%s-%08d", time.Now().Format("20060102-150405"), next),
		TaskUUID: id,
		Do:       do,

		Stdout: make(chan string),
		Stderr: make(chan string),
		Exit:   make(chan int),
		Cancel: make(chan bool),
	}
}

func (chore Chore) String() string {
	return fmt.Sprintf("chore %s", chore.ID)
}

func (chore Chore) Infof(msg string, args ...interface{}) {
	log.Debugf(chore.String()+": stdout: "+msg, args...)
	chore.Stdout <- fmt.Sprintf(msg+"\n", args...)
}

func (chore Chore) Errorf(msg string, args ...interface{}) {
	log.Debugf(chore.String()+": stderr: "+msg, args...)
	chore.Stderr <- fmt.Sprintf(msg+"\n", args...)
}

func (chore Chore) UnixExit(rc int) {
	defer func() {
		recover()
	}()

	chore.Exit <- rc
	close(chore.Exit)
	log.Debugf("%s: exiting %d", chore, rc)
}

func (w *Worker) Execute(chore Chore) {
	defer func() {
		if err := recover(); err != nil {
			log.Errorf("%s: %s", chore, err)
			w.db.UpdateTaskLog(chore.TaskUUID, fmt.Sprintf("\n\nERROR: %s\n\n", err))

			log.Errorf("%s: FAILING task '%s' in database", chore, chore.TaskUUID)
			w.db.FailTask(chore.TaskUUID, time.Now())
		}
	}()

	var wait sync.WaitGroup

	w.Reserve(chore.TaskUUID)
	defer w.Release()

	log.Infof("%s: %s executing chore for task '%s'", chore, w, chore.TaskUUID)
	w.db.StartTask(chore.TaskUUID, time.Now())

	log.Debugf("%s: spinning up [stderr] goroutine to watch chore stderr and update the task log...", chore)
	wait.Add(1)
	go func() {
		for s := range chore.Stderr {
			w.db.UpdateTaskLog(chore.TaskUUID, s)
		}
		log.Debugf("%s: no more standard error; [stderr] gooutine shutting down...", chore)
		wait.Done()
	}()

	log.Debugf("%s: spinning up [stdout] goroutine to watch chore stdout and accumulate the output...", chore)
	output := ""
	wait.Add(1)
	go func() {
		for s := range chore.Stdout {
			output += s
		}
		log.Debugf("%s: no more standard output; [stdout] gooutine shutting down...", chore)
		wait.Done()
	}()

	log.Debugf("%s: spinning up [exit] goroutine to watch chore exit status and remember it...", chore)
	rc := 0
	wait.Add(1)
	go func() {
		rc = <-chore.Exit
		log.Debugf("%s: rc %d noted; [exit] goroutine shutting down...", chore, rc)
		wait.Done()
	}()

	log.Debugf("%s: spinning up [main] goroutine to execute chore `do' function...", chore)
	wait.Add(1)
	go func() {
		chore.Do(chore)
		log.Debugf("%s: chore execution complete; [main] goroutine shutting down...", chore)

		chore.UnixExit(0) /* catch-all */
		close(chore.Stderr)
		close(chore.Stdout)
		wait.Done()
	}()

	log.Debugf("%s: waiting for chore to complete...", chore)
	wait.Wait()
	w.db.UpdateTaskLog(chore.TaskUUID, "\n\n------\n")

	task, err := w.db.GetTask(chore.TaskUUID)
	if err != nil {
		panic(fmt.Errorf("failed to retrieve task '%s' from database: %s", chore.TaskUUID, err))
	}

	switch task.Op {
	case db.BackupOperation:
		output = strings.TrimSpace(output)
		log.Infof("%s: parsing output of %s operation, '%s'", chore, task.Op, output)
		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: `%s`\n", output))

		var v struct {
			Key         string `json:"key"`
			Size        int64  `json:"archive_size"`
			Compression string `json:"compression"`
		}
		err := json.Unmarshal([]byte(output), &v)
		if err != nil {
			panic(fmt.Errorf("failed to unmarshal output [%s] from %s operation: %s", output, task.Op, err))
		}

		if v.Key == "" {
			panic(fmt.Errorf("%s: no restore key detected in %s operation output", chore, task.Op))
		}

		if v.Compression == "" {
			/* older shield-pipes will always bzip2; and if they aren't
			   reporting their compression type, it's gotta be bzip2 */
			v.Compression = "bzip2"
		}

		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: restore key  = %s\n", v.Key))
		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: archive size = %d bytes\n", v.Size))
		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: compression  = %s\n", v.Compression))

		log.Infof("%s: restore key for this %s operation is '%s'", chore, task.Op, v.Key)
		_, err = w.db.CreateTaskArchive(task.UUID, task.ArchiveUUID, v.Key, time.Now(),
			"FIXME - cipher", v.Compression, v.Size, task.TenantUUID)
		if err != nil {
			panic(fmt.Errorf("failed to create task archive database record '%s': %s", task.ArchiveUUID, err))
		}

		w.db.UpdateTaskLog(task.UUID, "\nBACKUP: recalculating cloud storage usage statistics...\n")
		store, err := w.db.GetStore(task.StoreUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve store from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")

		} else {
			store.StorageUsed += v.Size
			store.ArchiveCount += 1
			store.DailyIncrease += v.Size
			err := w.db.UpdateStore(store)
			if err != nil {
				log.Errorf("%s: failed to update store in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... store usage statistics updated.\n")
			}
		}

		tenant, err := w.db.GetTenant(task.TenantUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve tenant from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")

		} else {
			tenant.StorageUsed += v.Size
			tenant.ArchiveCount += 1
			tenant.DailyIncrease += v.Size
			_, err := w.db.UpdateTenant(tenant)
			if err != nil {
				log.Errorf("%s: failed to update tenant in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... tenant usage statistics updated.\n")
			}
		}
		w.db.UpdateTaskLog(task.UUID, "\n\n")

	case db.PurgeOperation:
		log.Infof("%s: purged archive '%s' from storage", chore, task.ArchiveUUID)
		err = w.db.PurgeArchive(task.ArchiveUUID)
		if err != nil {
			panic(fmt.Errorf("%s: failed to purge the archive record from the database: %s", chore, err))
		}

		w.db.UpdateTaskLog(task.UUID, "\nBACKUP: recalculating cloud storage usage statistics...\n")
		archive, err := w.db.GetArchive(task.ArchiveUUID)
		if err != nil {
			panic(fmt.Errorf("%s: failed to retrieve archive record from the database: %s", chore, err))
		}

		store, err := w.db.GetStore(task.StoreUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve store from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")

		} else {
			store.StorageUsed -= archive.Size
			store.ArchiveCount -= 1
			store.DailyIncrease -= archive.Size
			err := w.db.UpdateStore(store)
			if err != nil {
				log.Errorf("%s: failed to update store in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... store usage statistics updated.\n")
			}
		}

		tenant, err := w.db.GetTenant(task.TenantUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve tenant from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")

		} else {
			tenant.StorageUsed -= archive.Size
			tenant.ArchiveCount -= 1
			tenant.DailyIncrease -= archive.Size
			_, err := w.db.UpdateTenant(tenant)
			if err != nil {
				log.Errorf("%s: failed to update tenant in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... tenant usage statistics updated.\n")
			}
		}
		w.db.UpdateTaskLog(task.UUID, "\n\n")

	case db.TestStoreOperation:
		var v struct {
			Healthy bool `json:"healthy"`
		}

		err = json.Unmarshal([]byte(output), &v)
		if err != nil {
			panic(fmt.Errorf("failed to unmarshal output [%s] from %s operation: %s", output, task.Op, err))
		}

		store, err := w.db.GetStore(task.StoreUUID)
		if err != nil {
			panic(fmt.Errorf("failed to retrieve store '%s' from database: %s", task.StoreUUID, err))
		}
		if store == nil {
			panic(fmt.Errorf("store '%s' not found in database", task.StoreUUID))
		}

		store.Healthy = v.Healthy
		err = w.db.UpdateStore(store)
		if err != nil {
			panic(fmt.Errorf("failed to update store '%s' record in database: %s", task.StoreUUID, err))
		}

	case db.AgentStatusOperation:
		var v struct {
			Name    string `json:"name"`
			Version string `json:"version"`
			Health  string `json:"health"`
		}

		err = json.Unmarshal([]byte(output), &v)
		if err != nil {
			panic(fmt.Errorf("failed to unmarshal output [%s] from %s operation: %s", output, task.Op, err))
		}

		agents, err := w.db.GetAllAgents(&db.AgentFilter{Address: task.Agent})
		if err != nil {
			panic(fmt.Errorf("failed to retrieve agent '%s' from database: %s", task.Agent, err))
		}
		if len(agents) != 1 {
			panic(fmt.Errorf("found %d agent records for address '%s' (expected 1)", len(agents), task.Agent))
		}

		agent := agents[0]
		agent.Name = v.Name
		agent.Version = v.Version
		agent.Status = v.Health
		agent.RawMeta = output
		err = w.db.UpdateAgent(agent)
		if err != nil {
			panic(fmt.Errorf("failed to update agent '%s' record in database: %s", task.Agent, err))
		}
	}

	if rc == 0 {
		log.Debugf("%s: completing task '%s' in database", chore, chore.TaskUUID)
		w.db.CompleteTask(chore.TaskUUID, time.Now())

	} else {
		log.Debugf("%s: FAILING task '%s' in database", chore, chore.TaskUUID)
		w.db.FailTask(chore.TaskUUID, time.Now())
	}
}
<commit_msg>Fix typos in log messages<commit_after>package scheduler

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/jhunt/go-log"

	"github.com/starkandwayne/shield/db"
)

var next = 0

type Chore struct {
	ID       string
	TaskUUID string

	Do func(chore Chore)

	Stdout chan string
	Stderr chan string
	Exit   chan int
	Cancel chan bool
}

func NewChore(id string, do func(Chore)) Chore {
	next += 1
	return Chore{
		ID:       fmt.Sprintf("%s-%08d", time.Now().Format("20060102-150405"), next),
		TaskUUID: id,
		Do:       do,

		Stdout: make(chan string),
		Stderr: make(chan string),
		Exit:   make(chan int),
		Cancel: make(chan bool),
	}
}

func (chore Chore) String() string {
	return fmt.Sprintf("chore %s", chore.ID)
}

func (chore Chore) Infof(msg string, args ...interface{}) {
	log.Debugf(chore.String()+": stdout: "+msg, args...)
	chore.Stdout <- fmt.Sprintf(msg+"\n", args...)
}

func (chore Chore) Errorf(msg string, args ...interface{}) {
	log.Debugf(chore.String()+": stderr: "+msg, args...)
	chore.Stderr <- fmt.Sprintf(msg+"\n", args...)
}

func (chore Chore) UnixExit(rc int) {
	defer func() {
		recover()
	}()

	chore.Exit <- rc
	close(chore.Exit)
	log.Debugf("%s: exiting %d", chore, rc)
}

func (w *Worker) Execute(chore Chore) {
	defer func() {
		if err := recover(); err != nil {
			log.Errorf("%s: %s", chore, err)
			w.db.UpdateTaskLog(chore.TaskUUID, fmt.Sprintf("\n\nERROR: %s\n\n", err))

			log.Errorf("%s: FAILING task '%s' in database", chore, chore.TaskUUID)
			w.db.FailTask(chore.TaskUUID, time.Now())
		}
	}()

	var wait sync.WaitGroup

	w.Reserve(chore.TaskUUID)
	defer w.Release()

	log.Infof("%s: %s executing chore for task '%s'", chore, w, chore.TaskUUID)
	w.db.StartTask(chore.TaskUUID, time.Now())

	log.Debugf("%s: spinning up [stderr] goroutine to watch chore stderr and update the task log...", chore)
	wait.Add(1)
	go func() {
		for s := range chore.Stderr {
			w.db.UpdateTaskLog(chore.TaskUUID, s)
		}
		log.Debugf("%s: no more standard error; [stderr] goroutine shutting down...", chore)
		wait.Done()
	}()

	log.Debugf("%s: spinning up [stdout] goroutine to watch chore stdout and accumulate the output...", chore)
	output := ""
	wait.Add(1)
	go func() {
		for s := range chore.Stdout {
			output += s
		}
		log.Debugf("%s: no more standard output; [stdout] goroutine shutting down...", chore)
		wait.Done()
	}()

	log.Debugf("%s: spinning up [exit] goroutine to watch chore exit status and remember it...", chore)
	rc := 0
	wait.Add(1)
	go func() {
		rc = <-chore.Exit
		log.Debugf("%s: rc %d noted; [exit] goroutine shutting down...", chore, rc)
		wait.Done()
	}()

	log.Debugf("%s: spinning up [main] goroutine to execute chore `do' function...", chore)
	wait.Add(1)
	go func() {
		chore.Do(chore)
		log.Debugf("%s: chore execution complete; [main] goroutine shutting down...", chore)

		chore.UnixExit(0) /* catch-all */
		close(chore.Stderr)
		close(chore.Stdout)
		wait.Done()
	}()

	log.Debugf("%s: waiting for chore to complete...", chore)
	wait.Wait()
	w.db.UpdateTaskLog(chore.TaskUUID, "\n\n------\n")

	task, err := w.db.GetTask(chore.TaskUUID)
	if err != nil {
		panic(fmt.Errorf("failed to retrieve task '%s' from database: %s", chore.TaskUUID, err))
	}

	switch task.Op {
	case db.BackupOperation:
		output = strings.TrimSpace(output)
		log.Infof("%s: parsing output of %s operation, '%s'", chore, task.Op, output)
		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: `%s`\n", output))

		var v struct {
			Key         string `json:"key"`
			Size        int64  `json:"archive_size"`
			Compression string `json:"compression"`
		}
		err := json.Unmarshal([]byte(output), &v)
		if err != nil {
			panic(fmt.Errorf("failed to unmarshal output [%s] from %s operation: %s", output, task.Op, err))
		}

		if v.Key == "" {
			panic(fmt.Errorf("%s: no restore key detected in %s operation output", chore, task.Op))
		}

		if v.Compression == "" {
			/* older shield-pipes will always bzip2; and if they aren't
			   reporting their compression type, it's gotta be bzip2 */
			v.Compression = "bzip2"
		}

		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: restore key  = %s\n", v.Key))
		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: archive size = %d bytes\n", v.Size))
		w.db.UpdateTaskLog(task.UUID, fmt.Sprintf("BACKUP: compression  = %s\n", v.Compression))

		log.Infof("%s: restore key for this %s operation is '%s'", chore, task.Op, v.Key)
		_, err = w.db.CreateTaskArchive(task.UUID, task.ArchiveUUID, v.Key, time.Now(),
			"FIXME - cipher", v.Compression, v.Size, task.TenantUUID)
		if err != nil {
			panic(fmt.Errorf("failed to create task archive database record '%s': %s", task.ArchiveUUID, err))
		}

		w.db.UpdateTaskLog(task.UUID, "\nBACKUP: recalculating cloud storage usage statistics...\n")
		store, err := w.db.GetStore(task.StoreUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve store from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")

		} else {
			store.StorageUsed += v.Size
			store.ArchiveCount += 1
			store.DailyIncrease += v.Size
			err := w.db.UpdateStore(store)
			if err != nil {
				log.Errorf("%s: failed to update store in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... store usage statistics updated.\n")
			}
		}

		tenant, err := w.db.GetTenant(task.TenantUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve tenant from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")

		} else {
			tenant.StorageUsed += v.Size
			tenant.ArchiveCount += 1
			tenant.DailyIncrease += v.Size
			_, err := w.db.UpdateTenant(tenant)
			if err != nil {
				log.Errorf("%s: failed to update tenant in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... tenant usage statistics updated.\n")
			}
		}
		w.db.UpdateTaskLog(task.UUID, "\n\n")

	case db.PurgeOperation:
		log.Infof("%s: purged archive '%s' from storage", chore, task.ArchiveUUID)
		err = w.db.PurgeArchive(task.ArchiveUUID)
		if err != nil {
			panic(fmt.Errorf("%s: failed to purge the archive record from the database: %s", chore, err))
		}

		w.db.UpdateTaskLog(task.UUID, "\nBACKUP: recalculating cloud storage usage statistics...\n")
		archive, err := w.db.GetArchive(task.ArchiveUUID)
		if err != nil {
			panic(fmt.Errorf("%s: failed to retrieve archive record from the database: %s", chore, err))
		}

		store, err := w.db.GetStore(task.StoreUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve store from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")

		} else {
			store.StorageUsed -= archive.Size
			store.ArchiveCount -= 1
			store.DailyIncrease -= archive.Size
			err := w.db.UpdateStore(store)
			if err != nil {
				log.Errorf("%s: failed to update store in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: store usage statistics were NOT updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... store usage statistics updated.\n")
			}
		}

		tenant, err := w.db.GetTenant(task.TenantUUID)
		if err != nil {
			log.Errorf("%s: failed to retrieve tenant from the database: %s", chore, err)
			w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")

		} else {
			tenant.StorageUsed -= archive.Size
			tenant.ArchiveCount -= 1
			tenant.DailyIncrease -= archive.Size
			_, err := w.db.UpdateTenant(tenant)
			if err != nil {
				log.Errorf("%s: failed to update tenant in the database: %s", chore, err)
				w.db.UpdateTaskLog(task.UUID, "WARNING: tenant usage statistics were not updated...\n")
			} else {
				w.db.UpdateTaskLog(task.UUID, "    ... tenant usage statistics updated.\n")
			}
		}
		w.db.UpdateTaskLog(task.UUID, "\n\n")

	case db.TestStoreOperation:
		var v struct {
			Healthy bool `json:"healthy"`
		}

		err = json.Unmarshal([]byte(output), &v)
		if err != nil {
			panic(fmt.Errorf("failed to unmarshal output [%s] from %s operation: %s", output, task.Op, err))
		}

		store, err := w.db.GetStore(task.StoreUUID)
		if err != nil {
			panic(fmt.Errorf("failed to retrieve store '%s' from database: %s", task.StoreUUID, err))
		}
		if store == nil {
			panic(fmt.Errorf("store '%s' not found in database", task.StoreUUID))
		}

		store.Healthy = v.Healthy
		err = w.db.UpdateStore(store)
		if err != nil {
			panic(fmt.Errorf("failed to update store '%s' record in database: %s", task.StoreUUID, err))
		}

	case db.AgentStatusOperation:
		var v struct {
			Name    string `json:"name"`
			Version string `json:"version"`
			Health  string `json:"health"`
		}

		err = json.Unmarshal([]byte(output), &v)
		if err != nil {
			panic(fmt.Errorf("failed to unmarshal output [%s] from %s operation: %s", output, task.Op, err))
		}

		agents, err := w.db.GetAllAgents(&db.AgentFilter{Address: task.Agent})
		if err != nil {
			panic(fmt.Errorf("failed to retrieve agent '%s' from database: %s", task.Agent, err))
		}
		if len(agents) != 1 {
			panic(fmt.Errorf("found %d agent records for address '%s' (expected 1)", len(agents), task.Agent))
		}

		agent := agents[0]
		agent.Name = v.Name
		agent.Version = v.Version
		agent.Status = v.Health
		agent.RawMeta = output
		err = w.db.UpdateAgent(agent)
		if err != nil {
			panic(fmt.Errorf("failed to update agent '%s' record in database: %s", task.Agent, err))
		}
	}

	if rc == 0 {
		log.Debugf("%s: completing task '%s' in database", chore, chore.TaskUUID)
		w.db.CompleteTask(chore.TaskUUID, time.Now())

	} else {
		log.Debugf("%s: FAILING task '%s' in database", chore, chore.TaskUUID)
		w.db.FailTask(chore.TaskUUID, time.Now())
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package core

import (
	"errors"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
)

var (
	big8               = big.NewInt(8)
	big32              = big.NewInt(32)
	blockedCodeHashErr = errors.New("core: blocked code-hash found during execution")

	// DAO attack chain rupture mechanism
	DAOSoftFork bool // Flag whether to vote for DAO rupture

	ruptureBlock      = uint64(1800000)                // Block number of the voted soft fork
	ruptureTarget     = big.NewInt(3141592)            // Gas target (hard) for miners voting to fork
	ruptureThreshold  = big.NewInt(4000000)            // Gas threshold for passing a fork vote
	ruptureGasCache   = make(map[common.Hash]*big.Int) // Amount of gas in the point of rupture
	ruptureCodeHashes = map[common.Hash]struct{}{
		common.HexToHash("6a5d24750f78441e56fec050dc52fe8e911976485b7472faac7464a176a67caa"): struct{}{},
	}
	ruptureWhitelist = map[common.Address]bool{
		common.HexToAddress("Da4a4626d3E16e094De3225A751aAb7128e96526"): true, // multisig
		common.HexToAddress("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334"): true, // attack contract
	}
	ruptureCacheLimit = 30000 // 1 epoch, 0.5 per possible fork
)

// StateProcessor is a basic Processor, which takes care of transitioning
// state from one point to another.
//
// StateProcessor implements Processor.
type StateProcessor struct {
	config *ChainConfig
	bc     *BlockChain
}

// NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
	return &StateProcessor{
		config: config,
		bc:     bc,
	}
}

// Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles.
//
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
	var (
		receipts     types.Receipts
		totalUsedGas = big.NewInt(0)
		err          error
		header       = block.Header()
		allLogs      vm.Logs
		gp           = new(GasPool).AddGas(block.GasLimit())
	)

	for i, tx := range block.Transactions() {
		statedb.StartRecord(tx.Hash(), block.Hash(), i)
		receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
		if err != nil {
			return nil, nil, totalUsedGas, err
		}
		receipts = append(receipts, receipt)
		allLogs = append(allLogs, logs...)
	}
	AccumulateRewards(statedb, header, block.Uncles())

	return receipts, allLogs, totalUsedGas, err
}

// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment.
//
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
	env := NewEnv(statedb, config, bc, tx, header, cfg)
	_, gas, err := ApplyMessage(env, tx, gp)
	if err != nil {
		return nil, nil, nil, err
	}

	// Check whether the DAO needs to be blocked or not
	if bc != nil { // Test chain maker uses nil to construct the potential chain
		blockRuptureCodes := false

		if number := header.Number.Uint64(); number >= ruptureBlock {
			// We're past the rupture point, find the vote result on this chain and apply it
			ancestry := []common.Hash{header.Hash(), header.ParentHash}
			for _, ok := ruptureGasCache[ancestry[len(ancestry)-1]]; !ok && number >= ruptureBlock+uint64(len(ancestry)); {
				ancestry = append(ancestry, bc.GetHeaderByHash(ancestry[len(ancestry)-1]).ParentHash)
			}
			decider := ancestry[len(ancestry)-1]

			vote, ok := ruptureGasCache[decider]
			if !ok {
				// We've reached the rupture point, retrieve the vote
				vote = bc.GetHeaderByHash(decider).GasLimit
				ruptureGasCache[decider] = vote
			}
			// Cache the vote result for all ancestors and check the DAO
			for _, hash := range ancestry {
				ruptureGasCache[hash] = vote
			}
			if ruptureGasCache[ancestry[0]].Cmp(ruptureThreshold) <= 0 {
				blockRuptureCodes = true
			}
			// Make sure we don't OOM long run due to too many votes caching up
			for len(ruptureGasCache) > ruptureCacheLimit {
				for hash, _ := range ruptureGasCache {
					delete(ruptureGasCache, hash)
					break
				}
			}
		}
		// Verify if the DAO soft fork kicks in
		if blockRuptureCodes {
			if recipient := tx.To(); recipient == nil || !ruptureWhitelist[*recipient] {
				for hash, _ := range env.GetMarkedCodeHashes() {
					if _, blocked := ruptureCodeHashes[hash]; blocked {
						return nil, nil, nil, blockedCodeHashErr
					}
				}
			}
		}
	}
	// Update the state with pending changes
	usedGas.Add(usedGas, gas)
	receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
	receipt.TxHash = tx.Hash()
	receipt.GasUsed = new(big.Int).Set(gas)
	if MessageCreatesContract(tx) {
		from, _ := tx.From()
		receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
	}

	logs := statedb.GetLogs(tx.Hash())
	receipt.Logs = logs
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

	glog.V(logger.Debug).Infoln(receipt)

	return receipt, logs, gas, err
}

// AccumulateRewards credits the coinbase of the given block with the
// mining reward. The total reward consists of the static block reward
// and rewards for included uncles. The coinbase of each uncle block is
// also rewarded.
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
	reward := new(big.Int).Set(BlockReward)
	r := new(big.Int)
	for _, uncle := range uncles {
		r.Add(uncle.Number, big8)
		r.Sub(r, header.Number)
		r.Mul(r, BlockReward)
		r.Div(r, big8)
		statedb.AddBalance(uncle.Coinbase, r)

		r.Div(BlockReward, big32)
		reward.Add(reward, r)
	}
	statedb.AddBalance(header.Coinbase, reward)
}
<commit_msg>Revert "core: update the DAO soft fork proposal to the final block"<commit_after>// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package core

import (
	"errors"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
)

var (
	big8               = big.NewInt(8)
	big32              = big.NewInt(32)
	blockedCodeHashErr = errors.New("core: blocked code-hash found during execution")

	// DAO attack chain rupture mechanism
	DAOSoftFork bool // Flag whether to vote for DAO rupture

	ruptureBlock      = uint64(1775000)                // Block number of the voted soft fork
	ruptureTarget     = big.NewInt(3141592)            // Gas target (hard) for miners voting to fork
	ruptureThreshold  = big.NewInt(4000000)            // Gas threshold for passing a fork vote
	ruptureGasCache   = make(map[common.Hash]*big.Int) // Amount of gas in the point of rupture
	ruptureCodeHashes = map[common.Hash]struct{}{
		common.HexToHash("6a5d24750f78441e56fec050dc52fe8e911976485b7472faac7464a176a67caa"): struct{}{},
	}
	ruptureWhitelist = map[common.Address]bool{
		common.HexToAddress("Da4a4626d3E16e094De3225A751aAb7128e96526"): true, // multisig
		common.HexToAddress("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334"): true, // attack contract
	}
	ruptureCacheLimit = 30000 // 1 epoch, 0.5 per possible fork
)

// StateProcessor is a basic Processor, which takes care of transitioning
// state from one point to another.
//
// StateProcessor implements Processor.
type StateProcessor struct {
	config *ChainConfig
	bc     *BlockChain
}

// NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
	return &StateProcessor{
		config: config,
		bc:     bc,
	}
}

// Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles.
//
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
	var (
		receipts     types.Receipts
		totalUsedGas = big.NewInt(0)
		err          error
		header       = block.Header()
		allLogs      vm.Logs
		gp           = new(GasPool).AddGas(block.GasLimit())
	)

	for i, tx := range block.Transactions() {
		statedb.StartRecord(tx.Hash(), block.Hash(), i)
		receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
		if err != nil {
			return nil, nil, totalUsedGas, err
		}
		receipts = append(receipts, receipt)
		allLogs = append(allLogs, logs...)
	}
	AccumulateRewards(statedb, header, block.Uncles())

	return receipts, allLogs, totalUsedGas, err
}

// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment.
//
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
	env := NewEnv(statedb, config, bc, tx, header, cfg)
	_, gas, err := ApplyMessage(env, tx, gp)
	if err != nil {
		return nil, nil, nil, err
	}

	// Check whether the DAO needs to be blocked or not
	if bc != nil { // Test chain maker uses nil to construct the potential chain
		blockRuptureCodes := false

		if number := header.Number.Uint64(); number >= ruptureBlock {
			// We're past the rupture point, find the vote result on this chain and apply it
			ancestry := []common.Hash{header.Hash(), header.ParentHash}
			for _, ok := ruptureGasCache[ancestry[len(ancestry)-1]]; !ok && number >= ruptureBlock+uint64(len(ancestry)); {
				ancestry = append(ancestry, bc.GetHeaderByHash(ancestry[len(ancestry)-1]).ParentHash)
			}
			decider := ancestry[len(ancestry)-1]

			vote, ok := ruptureGasCache[decider]
			if !ok {
				// We've reached the rupture point, retrieve the vote
				vote = bc.GetHeaderByHash(decider).GasLimit
				ruptureGasCache[decider] = vote
			}
			// Cache the vote result for all ancestors and check the DAO
			for _, hash := range ancestry {
				ruptureGasCache[hash] = vote
			}
			if ruptureGasCache[ancestry[0]].Cmp(ruptureThreshold) <= 0 {
				blockRuptureCodes = true
			}
			// Make sure we don't OOM long run due to too many votes caching up
			for len(ruptureGasCache) > ruptureCacheLimit {
				for hash, _ := range ruptureGasCache {
					delete(ruptureGasCache, hash)
					break
				}
			}
		}
		// Verify if the DAO soft fork kicks in
		if blockRuptureCodes {
			if recipient := tx.To(); recipient == nil || !ruptureWhitelist[*recipient] {
				for hash, _ := range env.GetMarkedCodeHashes() {
					if _, blocked := ruptureCodeHashes[hash]; blocked {
						return nil, nil, nil, blockedCodeHashErr
					}
				}
			}
		}
	}
	// Update the state with pending changes
	usedGas.Add(usedGas, gas)
	receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
	receipt.TxHash = tx.Hash()
	receipt.GasUsed = new(big.Int).Set(gas)
	if MessageCreatesContract(tx) {
		from, _ := tx.From()
		receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
	}

	logs := statedb.GetLogs(tx.Hash())
	receipt.Logs = logs
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

	glog.V(logger.Debug).Infoln(receipt)

	return receipt, logs, gas, err
}

// AccumulateRewards credits the coinbase of the given block with the
// mining reward. The total reward consists of the static block reward
// and rewards for included uncles. The coinbase of each uncle block is
// also rewarded.
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
	reward := new(big.Int).Set(BlockReward)
	r := new(big.Int)
	for _, uncle := range uncles {
		r.Add(uncle.Number, big8)
		r.Sub(r, header.Number)
		r.Mul(r, BlockReward)
		r.Div(r, big8)
		statedb.AddBalance(uncle.Coinbase, r)

		r.Div(BlockReward, big32)
		reward.Add(reward, r)
	}
	statedb.AddBalance(header.Coinbase, reward)
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/gcfg"
	"os"
)

// Configuration is Ourd's configuration
type Configuration struct {
	HTTP struct {
		Host string
	}
	App struct {
		Name   string
		APIKey string `gcfg:"api-key"`
	}
	DB struct {
		ImplName string `gcfg:"implementation"`
		Option   string
	}
	TokenStore struct {
		Path string `gcfg:"path"`
	} `gcfg:"token-store"`
	Subscription struct {
		Enabled bool
	}
	APNS struct {
		Gateway  string
		Cert     string
		Key      string
		CertPath string `gcfg:"cert-path"`
		KeyPath  string `gcfg:"key-path"`
	}
	LOG struct {
		Level string
	}
}

// ReadFileInto reads a configuration from file specified by path
func ReadFileInto(config *Configuration, path string) error {
	if err := gcfg.ReadFileInto(config, path); err != nil {
		return err
	}
	if config.HTTP.Host == "" {
		port := os.Getenv("PORT")
		if port == "" {
			port = "3000"
		}
		config.HTTP.Host = ":" + port
	}

	dbImplName := os.Getenv("DB_IMPL_NAME")
	if dbImplName != "" {
		config.DB.ImplName = dbImplName
	}

	if config.DB.ImplName == "pq" && os.Getenv("DATABASE_URL") != "" {
		config.DB.Option = os.Getenv("DATABASE_URL")
	}

	shouldEnableSubscription := os.Getenv("ENABLE_SUBSCRIPTION")
	if shouldEnableSubscription != "" {
		config.Subscription.Enabled = shouldEnableSubscription == "1"
	}

	cert, key := os.Getenv("APNS_CERTIFICATE"), os.Getenv("APNS_PRIVATE_KEY")
	if cert != "" && key != "" {
		config.APNS.Cert = cert
		config.APNS.Key = key
	}

	return nil
}
<commit_msg>Read API key from environment variable<commit_after>package main

import (
	"code.google.com/p/gcfg"
	"os"
)

// Configuration is Ourd's configuration
type Configuration struct {
	HTTP struct {
		Host string
	}
	App struct {
		Name   string
		APIKey string `gcfg:"api-key"`
	}
	DB struct {
		ImplName string `gcfg:"implementation"`
		Option   string
	}
	TokenStore struct {
		Path string `gcfg:"path"`
	} `gcfg:"token-store"`
	Subscription struct {
		Enabled bool
	}
	APNS struct {
		Gateway  string
		Cert     string
		Key      string
		CertPath string `gcfg:"cert-path"`
		KeyPath  string `gcfg:"key-path"`
	}
	LOG struct {
		Level string
	}
}

// ReadFileInto reads a configuration from file specified by path
func ReadFileInto(config *Configuration, path string) error {
	if err := gcfg.ReadFileInto(config, path); err != nil {
		return err
	}
	if config.HTTP.Host == "" {
		port := os.Getenv("PORT")
		if port == "" {
			port = "3000"
		}
		config.HTTP.Host = ":" + port
	}

	appAPIKey := os.Getenv("API_KEY")
	if appAPIKey != "" {
		config.App.APIKey = appAPIKey
	}

	dbImplName := os.Getenv("DB_IMPL_NAME")
	if dbImplName != "" {
		config.DB.ImplName = dbImplName
	}

	if config.DB.ImplName == "pq" && os.Getenv("DATABASE_URL") != "" {
		config.DB.Option = os.Getenv("DATABASE_URL")
	}

	shouldEnableSubscription := os.Getenv("ENABLE_SUBSCRIPTION")
	if shouldEnableSubscription != "" {
		config.Subscription.Enabled = shouldEnableSubscription == "1"
	}

	cert, key := os.Getenv("APNS_CERTIFICATE"), os.Getenv("APNS_PRIVATE_KEY")
	if cert != "" && key != "" {
		config.APNS.Cert = cert
		config.APNS.Key = key
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
)

const (
	configPath string = "./config.json"
)

var (
	// Config stores the global server
	// configuration parsed from the passed
	// config JSON file
	Config *Configuration

	// config holds the configuration
	// path
	config string
)

// Configuration holds configuration information
// for the service
//
// Hooks, used for the hooked analysis
// endpoint, are given as a map of id's
// to Hooks. (id's are strings that are
// specified within the passed JSON to the
// POST /task endpoint.) The default hook
// can be passed so you won't have to
// manually pass in a hookId when running
// a task. It defaults to the given hook
// when you only pass one hook in the config.
// When padding multiple, it defaults to
// a random hook.
type Configuration struct {
	Port       int16 `json:"port,omitempty"`
	portString string

	Hooks       map[string]Hook `json:"hooks,omitempty"`
	DefaultHook string          `json:"defaultHook,omitempty"`
}

// init grabs the config from the expected
// command line args. Else initializes with
// defaults
func init() {
	// holds config path
	flag.StringVar(&config, "conf", configPath, "Sets the server configuration filepath")
	flag.StringVar(&config, "C", configPath, "(shorthand for -conf)")
}

// ParseConfig must be run after the
// flag.Parse() function has been run
// to set the global configuration
// variable
func ParseConfig() error {
	path, err := filepath.Abs(config)
	if err != nil {
		return fmt.Errorf("ERROR: error generating absolute path from given config path. Does the file exist? %v", err)
	}

	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("ERROR: error opening config file: %v", err)
	}

	// read file into buffer
	// 1KB should be enough for
	// a reasonable config file
	bytes := make([]byte, 1024)
	n, err := f.Read(bytes)
	if err != nil && err != io.EOF {
		return fmt.Errorf("ERROR: error reading config file into buffer: %v", err)
	}

	// unmarshal file into Config struct
	err = json.Unmarshal(bytes[:n], &Config)
	if err != nil {
		return fmt.Errorf("ERROR: error unmarshalling given config file into a Config struct: %v", err)
	}

	if Config.Port == 0 {
		Config.Port = 8080
	}

	Config.portString = fmt.Sprintf(":%v", Config.Port)

	if Config.DefaultHook == "" {
		for id := range Config.Hooks {
			Config.DefaultHook = id
		}
	}

	return nil
}
<commit_msg>If config is a URL now it'll get the config from the URL<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

const (
	configPath string = "./config.json"
)

var (
	// Config stores the global server
	// configuration parsed from the passed
	// config JSON file
	Config *Configuration

	// config holds the configuration
	// path
	config string
)

// Configuration holds configuration information
// for the service
//
// Hooks, used for the hooked analysis
// endpoint, are given as a map of id's
// to Hooks. (id's are strings that are
// specified within the passed JSON to the
// POST /task endpoint.) The default hook
// can be passed so you won't have to
// manually pass in a hookId when running
// a task. It defaults to the given hook
// when you only pass one hook in the config.
// When padding multiple, it defaults to
// a random hook.
type Configuration struct {
	Port       int16 `json:"port,omitempty"`
	portString string

	Hooks       map[string]Hook `json:"hooks,omitempty"`
	DefaultHook string          `json:"defaultHook,omitempty"`
}

// init grabs the config from the expected
// command line args. Else initializes with
// defaults
func init() {
	// holds config path
	flag.StringVar(&config, "conf", configPath, "Sets the server configuration filepath")
	flag.StringVar(&config, "C", configPath, "(shorthand for -conf)")
}

// ParseConfigFromFile must be run after
// the flag.Parse() function has been run
// to set the global configuration
// variable
func ParseConfigFromFile() error {
	path, err := filepath.Abs(config)
	if err != nil {
		return fmt.Errorf("ERROR: error generating absolute path from given config path. Does the file exist? %v", err)
	}

	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("ERROR: error opening config file: %v", err)
	}

	// read file into buffer
	// 1KB should be enough for
	// a reasonable config file
	bytes := make([]byte, 1024)
	n, err := f.Read(bytes)
	if err != nil && err != io.EOF {
		return fmt.Errorf("ERROR: error reading config file into buffer: %v", err)
	}

	// unmarshal file into Config struct
	err = json.Unmarshal(bytes[:n], &Config)
	if err != nil {
		return fmt.Errorf("ERROR: error unmarshalling given config file into a Config struct: %v", err)
	}

	if Config.Port == 0 {
		Config.Port = 8080
	}

	Config.portString = fmt.Sprintf(":%v", Config.Port)

	if Config.DefaultHook == "" {
		for id := range Config.Hooks {
			Config.DefaultHook = id
		}
	}

	return nil
}

// ParseConfigFromURL pulls the config
// from a URL by making a get request
//
// This expects that the config path
// is, in fact, a URL routing to
// either HTTP or HTTPS
func ParseConfigFromURL() error {
	resp, err := http.Get(config)
	if err != nil {
		return fmt.Errorf("ERROR: error making GET request to given url: %v", config)
	}

	data := make([]byte, resp.ContentLength)
	n, err := resp.Body.Read(data)
	if err != nil {
		return fmt.Errorf("ERROR: error reading config data from GET url response. URL: %v", config)
	}

	json.Unmarshal(data[:n], &Config)

	if Config.Port == 0 {
		Config.Port = 8080
	}

	Config.portString = fmt.Sprintf(":%v", Config.Port)

	if Config.DefaultHook == "" {
		for id := range Config.Hooks {
			Config.DefaultHook = id
		}
	}

	return nil
}

// ParseConfig determines whether
// the config is at a URL or a file,
// unmarshals and munges the
// configuration, and returns
// any errors.
func ParseConfig() error {
	var err error

	// check if file
	u, err := url.Parse(config)
	if err != nil || !(u.Scheme == "http" || u.Scheme == "https") {
		err = ParseConfigFromFile()
	} else {
		err = ParseConfigFromURL()
	}

	if err != nil {
		return err
	}

	if Config.Port == 0 {
		Config.Port = 8080
	}

	Config.portString = fmt.Sprintf(":%v", Config.Port)

	if Config.DefaultHook == "" {
		for id := range Config.Hooks {
			Config.DefaultHook = id
		}
	}

	return nil
}
<|endoftext|>