<commit_before>package main

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"errors"
	"fmt"
	libgit "github.com/driusan/git"
	"github.com/driusan/go-git/zlib"
	"io"
	"os"
)

var debug bool = false
var ObjectExists = errors.New("Object already exists")

type packfile struct {
	Signature [4]byte
	Version   uint32
	Size      uint32
}

type PackEntryType uint8
type PackEntrySize uint64
type ObjectReference []byte

const (
	OBJ_COMMIT    PackEntryType = 1
	OBJ_TREE      PackEntryType = 2
	OBJ_BLOB      PackEntryType = 3
	OBJ_TAG       PackEntryType = 4
	_             PackEntryType = 5 // reserved for future use
	OBJ_OFS_DELTA PackEntryType = 6
	OBJ_REF_DELTA PackEntryType = 7
)

func (p packfile) ReadHeaderSize(r io.Reader) (PackEntryType, PackEntrySize, ObjectReference) {
	b := make([]byte, 1)
	var i uint
	var size PackEntrySize
	var entrytype PackEntryType
	refDelta := make([]byte, 20)
	for {
		r.Read(b)
		if i == 0 {
			// Extract bits 2-4, which contain the type
			// 0x70 is the bitmask for bits 2-4, then shift
			// it over 4 bits so that it fits into the entry
			// type uint8 and can be compared against the
			// consts
			switch entrytype = PackEntryType((b[0] & 0x70) >> 4); entrytype {
			case OBJ_COMMIT:
			case OBJ_TREE:
			case OBJ_BLOB:
			case OBJ_TAG:
				fmt.Printf("Tag!\n")
			case OBJ_OFS_DELTA:
				fmt.Printf("OFS_DELTA!\n")
			case OBJ_REF_DELTA:

			}
			// on the first byte, bits 5-8 are the size
			size = PackEntrySize(b[0] & 0x0F)
		} else {
			// on anything after the first byte, bit 1
			// tells us if this is the last byte in the
			// header, and bits 2-8 are part of the
			// size.
			// b[0] & 0x7F extracts those bits, but then
			// they need to be shifted by a constant 4 bits
			// (because those 4 bits were from the first byte),
			// plus 7 for each of the previous bytes to get
			// the location for bits 2-8 from this byte.
			var tmp uint64 = uint64(b[0] & 0x7f)
			size |= PackEntrySize(tmp << (4 + ((i - 1) * 7)))
		}
		if b[0] <= 128 {
			break
		}
		i += 1
	}
	if entrytype == OBJ_REF_DELTA {
		n, err := r.Read(refDelta)
		if n != 20 || err != nil {
			panic(err)
		}
	}
	return entrytype, size, refDelta
}

// Returns
func (p packfile) ReadEntryDataStream(r io.ReadSeeker) (uncompressed []byte, compressed []byte) {
	b := new(bytes.Buffer)
	bookmark, _ := r.Seek(0, 1)
	zr, err := zlib.NewReader(r)
	if err != nil {
		panic(err)
	}
	defer zr.Close()
	io.Copy(b, zr)

	// Go's zlib implementation is greedy, so we need some hacks to
	// get r back to the right place in the file.
	// We use a modified version of compress/zlib which exposes the
	// digest. Before reading, we take a bookmark of the address
	// that we're starting at, then after reading we go back there.
	// Starting from there, look through the reader until we find the
	// compressed object's zlib digest.
	// This is stupid, but necessary because git's packfile format
	// is *very* stupid.
	digest := zr.Digest.Sum32()
	r.Seek(bookmark, 0)
	address := make([]byte, 4)
	var i int64
	var finalAddress int64
	for {
		n, err := r.Read(address)
		// This probably means we reached the end of the io.Reader.
		// It might be the last read, so break out of the loop instead
		// of getting caught in an infinite loop.
		if n < 4 || err != nil {
			break
		}
		var intAddress uint32 = (uint32(address[3]) | uint32(address[2])<<8 | uint32(address[1])<<16 | uint32(address[0])<<24)
		if intAddress == digest {
			finalAddress = bookmark + i + 4
			break
		}
		// Advance a byte
		i += 1
		r.Seek(bookmark+i, 0)

	}
	r.Seek(bookmark, 0)
	compressed = make([]byte, finalAddress-bookmark)
	r.Read(compressed)
	r.Seek(finalAddress, 0)
	return b.Bytes(), compressed

}

func writeObject(repo *libgit.Repository, objType string, rawdata []byte) error {
	obj := []byte(fmt.Sprintf("%s %d\000", objType, len(rawdata)))
	obj = append(obj, rawdata...)
	sha := sha1.Sum(obj)

	if have, _, err := repo.HaveObject(fmt.Sprintf("%x", sha)); have == true || err != nil {
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %s\n", err)
			return err

		}

		//fmt.Fprintf(os.Stderr, "Already have object %x\n", sha)
		return ObjectExists

	}
	directory := fmt.Sprintf("%x", sha[0:1])
	file := fmt.Sprintf("%x", sha[1:])

	fmt.Printf("Putting %x in %s/%s\n", sha, directory, file)
	os.MkdirAll(repo.Path+"/objects/"+directory, os.FileMode(0755))
	f, err := os.Create(repo.Path + "/objects/" + directory + "/" + file)
	if err != nil {
		return err
	}
	defer f.Close()
	w := zlib.NewWriter(f)
	if _, err := w.Write(obj); err != nil {
		return err
	}
	defer w.Close()
	return nil
}

type VariableLengthInt uint64

func ReadVariable(src io.Reader) uint64 {
	b := make([]byte, 1)
	var val uint64
	var i uint = 0
	for {
		src.Read(b)
		val |= uint64(b[0]&127) << (i * 7)
		if b[0] < 128 {
			break
		}
		i += 1
	}
	return val
}

type deltaeval struct {
	value []byte
}

func (d *deltaeval) Insert(src io.Reader, length uint8) {

	val := make([]byte, length)
	n, err := src.Read(val)
	if err != nil || n != int(length) {
		panic(fmt.Sprintf("Couldn't read %d bytes: %s", length, err))
	}
	if debug == true {
		fmt.Printf("Inserted %s (Hex: % x)\n", val, val)
		morecontext := make([]byte, 100)
		_, _ = src.Read(morecontext)
		fmt.Printf("The next 100 bytes are : %s (% x", morecontext, morecontext)
	}
	d.value = append(d.value, val...)
}
func (d *deltaeval) Copy(repo *libgit.Repository, src ObjectReference, offset, length uint64) {
	id, _ := libgit.NewId(src)
	_, _, r, err := repo.GetRawObject(id, false)
	if err != nil {
		panic(err)
	}
	defer r.Close()
	if offset > 0 {

		tmp := make([]byte, offset)
		n, err := r.Read(tmp)
		if debug == true {
			fmt.Printf("Read %d bytes to throw away (want %d)\n", n, offset)
			fmt.Printf("Throwing away %s", tmp)
			fmt.Printf("\n--\n")
		}
		if uint64(n) != offset || err != nil {
			panic("Couldn't read offset correctly")
		}
	}

	reading := make([]byte, length)
	n, err := r.Read(reading)
	if debug == true {
		fmt.Printf("Read %s (Hex: % x)\n", reading, reading)
	}
	if uint64(n) != length || err != nil {
		panic("Error copying data")
	}
	d.value = append(d.value, reading...)
}
func (d *deltaeval) DoInstruction(repo *libgit.Repository, delta io.Reader, ref ObjectReference) {
	b := make([]byte, 1)

	delta.Read(b)
	if b[0] == 0 {
		panic("Unexpected delta opcode: 0")
	}
	if b[0] >= 128 {
		var offset, length uint64
		offset = 0
		length = 0
		lenBuf := make([]byte, 1)
		if b[0]&0x01 != 0 {
			delta.Read(lenBuf)
			offset = uint64(lenBuf[0])
		}
		if b[0]&0x02 != 0 {
			delta.Read(lenBuf)
			offset |= uint64(lenBuf[0]) << 8
		}
		if b[0]&0x04 != 0 {
			delta.Read(lenBuf)
			offset |= uint64(lenBuf[0]) << 16
		}
		if b[0]&0x08 != 0 {
			delta.Read(lenBuf)
			offset |= uint64(lenBuf[0]) << 24
		}

		if b[0]&0x10 != 0 {
			delta.Read(lenBuf)
			length = uint64(lenBuf[0])
		}
		if b[0]&0x20 != 0 {
			delta.Read(lenBuf)
			length |= uint64(lenBuf[0]) << 8
		}
		if b[0]&0x40 != 0 {
			delta.Read(lenBuf)
			length |= uint64(lenBuf[0]) << 16
		}

		if length == 0 {
			length = 0x10000
		}
		d.Copy(repo, ref, uint64(offset), length)
	} else {
		d.Insert(delta, uint8(b[0]))
	}
}

// Calculate the final reslt of a REF_DELTA. This is largely
// based on the delta algorithm as described at:
// http://stefan.saasen.me/articles/git-clone-in-haskell-from-the-bottom-up/#pack_file_format
func calculateDelta(repo *libgit.Repository, reference ObjectReference, delta []byte) (PackEntryType, []byte) {
	deltaStream := bytes.NewBuffer(delta)

	// Read 2 variable length strings for the source and target buffer
	// length

	// read the source length, but we don't care. We just want to advance
	// the deltaStream pointer the proper amount.
	ReadVariable(deltaStream)
	// Read the target length so we know when we've finished processing
	// the delta stream.
	targetLength := ReadVariable(deltaStream)

	d := deltaeval{}
	for {
		d.DoInstruction(repo, deltaStream, reference)
		if targetLength == uint64(len(d.value)) {
			break
		}
		if len(d.value) > int(targetLength) {
			panic("Read too much data from delta stream")
		}
	}
	// GetRawObject to find the underlying type of the original
	// reference
	id, err := libgit.NewId(reference)
	if err != nil {
		panic(err)
	}
	objt, _, _, err := repo.GetRawObject(id, true)
	if err != nil {
		panic(err)
	}
	switch objt {
	case libgit.ObjectCommit:
		return OBJ_COMMIT, d.value
	case libgit.ObjectTree:
		return OBJ_TREE, d.value
	case libgit.ObjectBlob:
		return OBJ_BLOB, d.value
	case libgit.ObjectTag:
		return OBJ_TAG, d.value
	}
	panic("Unhandle object type while calculating delta.")
	return 0, nil

}
func unpack(repo *libgit.Repository, r io.ReadSeeker) {
	var p packfile
	binary.Read(r, binary.BigEndian, &p)
	if p.Signature != [4]byte{'P', 'A', 'C', 'K'} {
		return //err
	}
	if p.Version != 2 {
		return //err
	}
	for i := uint32(0); i < p.Size; i += 1 {
		t, s, ref := p.ReadHeaderSize(r)
		rawdata, _ := p.ReadEntryDataStream(r)
		switch t {
		case OBJ_COMMIT:
			writeObject(repo, "commit", rawdata)
		case OBJ_TREE:
			writeObject(repo, "tree", rawdata)
		case OBJ_BLOB:
			writeObject(repo, "blob", rawdata)
		case OBJ_REF_DELTA:
			t, deltadata := calculateDelta(repo, ref, rawdata)
			switch t {
			case OBJ_COMMIT:
				writeObject(repo, "commit", deltadata)
			case OBJ_TREE:
				writeObject(repo, "tree", deltadata)
			case OBJ_BLOB:
				writeObject(repo, "blob", deltadata)
			default:
				panic("TODO: Unhandled type for REF_DELTA")
			}
		default:
			panic(fmt.Sprintf("TODO: Unhandled type", t))
		}

		if len(rawdata) != int(s) {
			panic(fmt.Sprintf("Incorrect size of entry %d: %d not %d", i, len(rawdata), s))
		}
	}
}
<commit_msg>Fixed bug reading header size<commit_after>package main

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"errors"
	"fmt"
	libgit "github.com/driusan/git"
	"github.com/driusan/go-git/zlib"
	"io"
	"os"
)

var debug bool = false
var ObjectExists = errors.New("Object already exists")

type packfile struct {
	Signature [4]byte
	Version   uint32
	Size      uint32
}

type PackEntryType uint8
type PackEntrySize uint64
type ObjectReference []byte

const (
	OBJ_COMMIT    PackEntryType = 1
	OBJ_TREE      PackEntryType = 2
	OBJ_BLOB      PackEntryType = 3
	OBJ_TAG       PackEntryType = 4
	_             PackEntryType = 5 // reserved for future use
	OBJ_OFS_DELTA PackEntryType = 6
	OBJ_REF_DELTA PackEntryType = 7
)

func (p packfile) ReadHeaderSize(r io.Reader) (PackEntryType, PackEntrySize, ObjectReference) {
	b := make([]byte, 1)
	var i uint
	var size PackEntrySize
	var entrytype PackEntryType
	refDelta := make([]byte, 20)
	for {
		r.Read(b)
		if i == 0 {
			// Extract bits 2-4, which contain the type
			// 0x70 is the bitmask for bits 2-4, then shift
			// it over 4 bits so that it fits into the entry
			// type uint8 and can be compared against the
			// consts
			switch entrytype = PackEntryType((b[0] & 0x70) >> 4); entrytype {
			case OBJ_COMMIT:
			case OBJ_TREE:
			case OBJ_BLOB:
			case OBJ_TAG:
				fmt.Printf("Tag!\n")
			case OBJ_OFS_DELTA:
				fmt.Printf("OFS_DELTA!\n")
			case OBJ_REF_DELTA:

			}
			// on the first byte, bits 5-8 are the size
			size = PackEntrySize(b[0] & 0x0F)
		} else {
			// on anything after the first byte, bit 1
			// tells us if this is the last byte in the
			// header, and bits 2-8 are part of the
			// size.
			// b[0] & 0x7F extracts those bits, but then
			// they need to be shifted by a constant 4 bits
			// (because those 4 bits were from the first byte),
			// plus 7 for each of the previous bytes to get
			// the location for bits 2-8 from this byte.
			var tmp uint64 = uint64(b[0] & 0x7f)
			size |= PackEntrySize(tmp << (4 + ((i - 1) * 7)))
		}
		if b[0] < 128 {
			break
		}
		i += 1
	}
	if entrytype == OBJ_REF_DELTA {
		n, err := r.Read(refDelta)
		if n != 20 || err != nil {
			panic(err)
		}
	}
	return entrytype, size, refDelta
}

// Returns
func (p packfile) ReadEntryDataStream(r io.ReadSeeker) (uncompressed []byte, compressed []byte) {
	b := new(bytes.Buffer)
	bookmark, _ := r.Seek(0, 1)
	zr, err := zlib.NewReader(r)
	if err != nil {
		panic(err)
	}
	defer zr.Close()
	io.Copy(b, zr)

	// Go's zlib implementation is greedy, so we need some hacks to
	// get r back to the right place in the file.
	// We use a modified version of compress/zlib which exposes the
	// digest. Before reading, we take a bookmark of the address
	// that we're starting at, then after reading we go back there.
	// Starting from there, look through the reader until we find the
	// compressed object's zlib digest.
	// This is stupid, but necessary because git's packfile format
	// is *very* stupid.
	digest := zr.Digest.Sum32()
	r.Seek(bookmark, 0)
	address := make([]byte, 4)
	var i int64
	var finalAddress int64
	for {
		n, err := r.Read(address)
		// This probably means we reached the end of the io.Reader.
		// It might be the last read, so break out of the loop instead
		// of getting caught in an infinite loop.
		if n < 4 || err != nil {
			break
		}
		var intAddress uint32 = (uint32(address[3]) | uint32(address[2])<<8 | uint32(address[1])<<16 | uint32(address[0])<<24)
		if intAddress == digest {
			finalAddress = bookmark + i + 4
			break
		}
		// Advance a byte
		i += 1
		r.Seek(bookmark+i, 0)

	}
	r.Seek(bookmark, 0)
	compressed = make([]byte, finalAddress-bookmark)
	r.Read(compressed)
	r.Seek(finalAddress, 0)
	return b.Bytes(), compressed

}

func writeObject(repo *libgit.Repository, objType string, rawdata []byte) error {
	obj := []byte(fmt.Sprintf("%s %d\000", objType, len(rawdata)))
	obj = append(obj, rawdata...)
	sha := sha1.Sum(obj)

	if have, _, err := repo.HaveObject(fmt.Sprintf("%x", sha)); have == true || err != nil {
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %s\n", err)
			return err

		}

		//fmt.Fprintf(os.Stderr, "Already have object %x\n", sha)
		return ObjectExists

	}
	directory := fmt.Sprintf("%x", sha[0:1])
	file := fmt.Sprintf("%x", sha[1:])

	fmt.Printf("Putting %x in %s/%s\n", sha, directory, file)
	os.MkdirAll(repo.Path+"/objects/"+directory, os.FileMode(0755))
	f, err := os.Create(repo.Path + "/objects/" + directory + "/" + file)
	if err != nil {
		return err
	}
	defer f.Close()
	w := zlib.NewWriter(f)
	if _, err := w.Write(obj); err != nil {
		return err
	}
	defer w.Close()
	return nil
}

type VariableLengthInt uint64

func ReadVariable(src io.Reader) uint64 {
	b := make([]byte, 1)
	var val uint64
	var i uint = 0
	for {
		src.Read(b)
		val |= uint64(b[0]&127) << (i * 7)
		if b[0] < 128 {
			break
		}
		i += 1
	}
	return val
}

type deltaeval struct {
	value []byte
}

func (d *deltaeval) Insert(src io.Reader, length uint8) {

	val := make([]byte, length)
	n, err := src.Read(val)
	if err != nil || n != int(length) {
		panic(fmt.Sprintf("Couldn't read %d bytes: %s", length, err))
	}
	if debug == true {
		fmt.Printf("Inserted %s (Hex: % x)\n", val, val)
		morecontext := make([]byte, 100)
		_, _ = src.Read(morecontext)
		fmt.Printf("The next 100 bytes are : %s (% x", morecontext, morecontext)
	}
	d.value = append(d.value, val...)
}
func (d *deltaeval) Copy(repo *libgit.Repository, src ObjectReference, offset, length uint64) {
	id, _ := libgit.NewId(src)
	_, _, r, err := repo.GetRawObject(id, false)
	if err != nil {
		panic(err)
	}
	defer r.Close()
	if offset > 0 {

		tmp := make([]byte, offset)
		n, err := r.Read(tmp)
		if debug == true {
			fmt.Printf("Read %d bytes to throw away (want %d)\n", n, offset)
			fmt.Printf("Throwing away %s", tmp)
			fmt.Printf("\n--\n")
		}
		if uint64(n) != offset || err != nil {
			panic("Couldn't read offset correctly")
		}
	}

	reading := make([]byte, length)
	n, err := r.Read(reading)
	if debug == true {
		fmt.Printf("Read %s (Hex: % x)\n", reading, reading)
	}
	if uint64(n) != length || err != nil {
		panic("Error copying data")
	}
	d.value = append(d.value, reading...)
}
func (d *deltaeval) DoInstruction(repo *libgit.Repository, delta io.Reader, ref ObjectReference) {
	b := make([]byte, 1)

	delta.Read(b)
	if b[0] == 0 {
		panic("Unexpected delta opcode: 0")
	}
	if b[0] >= 128 {
		var offset, length uint64
		offset = 0
		length = 0
		lenBuf := make([]byte, 1)
		if b[0]&0x01 != 0 {
			delta.Read(lenBuf)
			offset = uint64(lenBuf[0])
		}
		if b[0]&0x02 != 0 {
			delta.Read(lenBuf)
			offset |= uint64(lenBuf[0]) << 8
		}
		if b[0]&0x04 != 0 {
			delta.Read(lenBuf)
			offset |= uint64(lenBuf[0]) << 16
		}
		if b[0]&0x08 != 0 {
			delta.Read(lenBuf)
			offset |= uint64(lenBuf[0]) << 24
		}

		if b[0]&0x10 != 0 {
			delta.Read(lenBuf)
			length = uint64(lenBuf[0])
		}
		if b[0]&0x20 != 0 {
			delta.Read(lenBuf)
			length |= uint64(lenBuf[0]) << 8
		}
		if b[0]&0x40 != 0 {
			delta.Read(lenBuf)
			length |= uint64(lenBuf[0]) << 16
		}

		if length == 0 {
			length = 0x10000
		}
		d.Copy(repo, ref, uint64(offset), length)
	} else {
		d.Insert(delta, uint8(b[0]))
	}
}

// Calculate the final reslt of a REF_DELTA. This is largely
// based on the delta algorithm as described at:
// http://stefan.saasen.me/articles/git-clone-in-haskell-from-the-bottom-up/#pack_file_format
func calculateDelta(repo *libgit.Repository, reference ObjectReference, delta []byte) (PackEntryType, []byte) {
	deltaStream := bytes.NewBuffer(delta)

	// Read 2 variable length strings for the source and target buffer
	// length

	// read the source length, but we don't care. We just want to advance
	// the deltaStream pointer the proper amount.
	ReadVariable(deltaStream)
	// Read the target length so we know when we've finished processing
	// the delta stream.
	targetLength := ReadVariable(deltaStream)

	d := deltaeval{}
	for {
		d.DoInstruction(repo, deltaStream, reference)
		if targetLength == uint64(len(d.value)) {
			break
		}
		if len(d.value) > int(targetLength) {
			panic("Read too much data from delta stream")
		}
	}
	// GetRawObject to find the underlying type of the original
	// reference
	id, err := libgit.NewId(reference)
	if err != nil {
		panic(err)
	}
	objt, _, _, err := repo.GetRawObject(id, true)
	if err != nil {
		panic(err)
	}
	switch objt {
	case libgit.ObjectCommit:
		return OBJ_COMMIT, d.value
	case libgit.ObjectTree:
		return OBJ_TREE, d.value
	case libgit.ObjectBlob:
		return OBJ_BLOB, d.value
	case libgit.ObjectTag:
		return OBJ_TAG, d.value
	}
	panic("Unhandle object type while calculating delta.")
	return 0, nil

}
func unpack(repo *libgit.Repository, r io.ReadSeeker) {
	var p packfile
	binary.Read(r, binary.BigEndian, &p)
	if p.Signature != [4]byte{'P', 'A', 'C', 'K'} {
		return //err
	}
	if p.Version != 2 {
		return //err
	}
	for i := uint32(0); i < p.Size; i += 1 {
		t, s, ref := p.ReadHeaderSize(r)
		rawdata, _ := p.ReadEntryDataStream(r)
		switch t {
		case OBJ_COMMIT:
			writeObject(repo, "commit", rawdata)
		case OBJ_TREE:
			writeObject(repo, "tree", rawdata)
		case OBJ_BLOB:
			writeObject(repo, "blob", rawdata)
		case OBJ_REF_DELTA:
			t, deltadata := calculateDelta(repo, ref, rawdata)
			switch t {
			case OBJ_COMMIT:
				writeObject(repo, "commit", deltadata)
			case OBJ_TREE:
				writeObject(repo, "tree", deltadata)
			case OBJ_BLOB:
				writeObject(repo, "blob", deltadata)
			default:
				panic("TODO: Unhandled type for REF_DELTA")
			}
		default:
			panic(fmt.Sprintf("TODO: Unhandled type", t))
		}

		if len(rawdata) != int(s) {
			panic(fmt.Sprintf("Incorrect size of entry %d: %d not %d", i, len(rawdata), s))
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
)

type Config struct {
	BuildNumber     int
	ProjectRoot     string
	UserSitesDomain string
	ContainerSubnet string
	VmPool          string
	Version         string
	Client          struct {
		StaticFilesBaseUrl string
	}
	Mongo string
	Mq    struct {
		Host          string
		Port          int
		ComponentUser string
		Password      string
		Vhost         string
	}
	Neo4j struct {
		Read    string
		Write   string
		Port    int
		Enabled bool
	}
	Broker struct {
		IP              string
		Port            int
		CertFile        string
		KeyFile         string
		AuthExchange    string
		AuthAllExchange string
	}
	Loggr struct {
		Push   bool
		Url    string
		ApiKey string
	}
	Librato struct {
		Push     bool
		Email    string
		Token    string
		Interval int
	}
	Opsview struct {
		Push bool
		Host string
	}
	Kontrold struct {
		Vhost    string
		Overview struct {
			ApiPort    int
			ApiHost    string
			Port       int
			SwitchHost string
		}
		Api struct {
			Port int
		}
		Proxy struct {
			Port    int
			PortSSL int
			FTPIP   string
		}
	}
	FollowFeed struct {
		Host          string
		Port          int
		ComponentUser string
		Password      string
		Vhost         string
	}
	Statsd struct {
		Use  bool
		Ip   string
		Port int
	}
}

var FileProfile string
var PillarProfile string
var Profile string
var Current Config
var LogDebug bool
var Uuid string
var Host string
var BrokerDomain string
var Region string
var VMProxies bool // used to enable ports for users
var Skip int
var Count int

func init() {
	flag.StringVar(&FileProfile, "c", "", "Configuration profile from file")
	flag.StringVar(&FileProfile, "config", "", "Alias for -c")
	flag.StringVar(&PillarProfile, "p", "", "Configuration profile from saltstack pillar")
	flag.BoolVar(&LogDebug, "d", false, "Log debug messages")
	flag.StringVar(&Uuid, "u", "", "Enable kontrol mode")
	flag.StringVar(&Host, "h", "", "Hostname to be resolved")
	flag.StringVar(&BrokerDomain, "a", "", "Send kontrol a custom domain istead of os.Hostname")
	flag.StringVar(&BrokerDomain, "domain", "", "Alias for -a")
	flag.StringVar(&Region, "r", "", "Region")
	flag.IntVar(&Skip, "s", 0, "Define how far to skip ahead")
	flag.IntVar(&Count, "l", 1000, "Count for items to process")

	flag.BoolVar(&VMProxies, "v", false, "Enable ports for VM users (1024-10000)")

	flag.Parse()
	if flag.NArg() != 0 {
		flag.PrintDefaults()
		os.Exit(1)
	}
	if FileProfile == "" && PillarProfile == "" {
		fmt.Println("Please specify a configuration profile via -c or -p.")
		flag.PrintDefaults()
		os.Exit(1)
	}
	if FileProfile != "" && PillarProfile != "" {
		fmt.Println("The flags -c and -p are exclusive.")
		flag.PrintDefaults()
		os.Exit(1)
	}
	var configCommand *exec.Cmd
	if FileProfile != "" {
		Profile = FileProfile
		configCommand = exec.Command("node", "-e", "require('koding-config-manager').printJson('main."+FileProfile+"')")
	}
	if PillarProfile != "" {
		Profile = PillarProfile
		configCommand = exec.Command("salt-call", "pillar.get", PillarProfile, "--output=json", "--log-level=warning")
	}

	configJSON, err := configCommand.CombinedOutput()
	if err != nil {
		fmt.Printf("Could not execute configuration source: %s\nConfiguration source output:\n%s\n", err.Error(), configJSON)
		os.Exit(1)
	}

	err = json.Unmarshal(configJSON, &Current)
	if err == nil && (Current == Config{}) {
		err = fmt.Errorf("Empty configuration.")
	}
	if err != nil {
		fmt.Printf("Could not unmarshal configuration: %s\nConfiguration source output:\n%s\n", err.Error(), configJSON)
		os.Exit(1)
	}

	sigChannel := make(chan os.Signal)
	signal.Notify(sigChannel, syscall.SIGUSR2)
	go func() {
		for _ = range sigChannel {
			LogDebug = !LogDebug
			fmt.Printf("config.LogDebug: %v\n", LogDebug)
		}
	}()

}
<commit_msg>config: refactor and add ability to read from environment variable<commit_after>package config

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
)

type Config struct {
	BuildNumber     int
	ProjectRoot     string
	UserSitesDomain string
	ContainerSubnet string
	VmPool          string
	Version         string
	Client          struct {
		StaticFilesBaseUrl string
	}
	Mongo string
	Mq    struct {
		Host          string
		Port          int
		ComponentUser string
		Password      string
		Vhost         string
	}
	Neo4j struct {
		Read    string
		Write   string
		Port    int
		Enabled bool
	}
	Broker struct {
		IP              string
		Port            int
		CertFile        string
		KeyFile         string
		AuthExchange    string
		AuthAllExchange string
	}
	Loggr struct {
		Push   bool
		Url    string
		ApiKey string
	}
	Librato struct {
		Push     bool
		Email    string
		Token    string
		Interval int
	}
	Opsview struct {
		Push bool
		Host string
	}
	Kontrold struct {
		Vhost    string
		Overview struct {
			ApiPort    int
			ApiHost    string
			Port       int
			SwitchHost string
		}
		Api struct {
			Port int
		}
		Proxy struct {
			Port    int
			PortSSL int
			FTPIP   string
		}
	}
	FollowFeed struct {
		Host          string
		Port          int
		ComponentUser string
		Password      string
		Vhost         string
	}
	Statsd struct {
		Use  bool
		Ip   string
		Port int
	}
}

var FileProfile string
var PillarProfile string
var Profile string
var Current Config
var LogDebug bool
var Uuid string
var Host string
var BrokerDomain string
var Region string
var VMProxies bool // used to enable ports for users
var Skip int
var Count int

func init() {
	flag.StringVar(&FileProfile, "c", "", "Configuration profile from file")
	flag.StringVar(&FileProfile, "config", "", "Alias for -c")
	flag.StringVar(&PillarProfile, "p", "", "Configuration profile from saltstack pillar")
	flag.BoolVar(&LogDebug, "d", false, "Log debug messages")
	flag.StringVar(&Uuid, "u", "", "Enable kontrol mode")
	flag.StringVar(&Host, "h", "", "Hostname to be resolved")
	flag.StringVar(&BrokerDomain, "a", "", "Send kontrol a custom domain istead of os.Hostname")
	flag.StringVar(&BrokerDomain, "domain", "", "Alias for -a")
	flag.StringVar(&Region, "r", "", "Region")
	flag.IntVar(&Skip, "s", 0, "Define how far to skip ahead")
	flag.IntVar(&Count, "l", 1000, "Count for items to process")
	flag.BoolVar(&VMProxies, "v", false, "Enable ports for VM users (1024-10000)")

	flag.Parse()

	err := readConfig()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	sigChannel := make(chan os.Signal)
	signal.Notify(sigChannel, syscall.SIGUSR2)
	go func() {
		for _ = range sigChannel {
			LogDebug = !LogDebug
			fmt.Printf("config.LogDebug: %v\n", LogDebug)
		}
	}()

}

// readConfig reads and unmarshalls the appropriate config into the Config
// struct (which is used in many applications). It either reads the config
// from the koding-config-manager or from salt-pillar with command line flag
// -c and -p. They are exclusive, which means you only can use one. If there
// is no flag specified it tries to get the config from the environment
// variable "CONFIG".
func readConfig() error {
	if flag.NArg() != 0 {
		return errors.New("You passed extra unused arguments.")
	}

	if FileProfile == "" && PillarProfile == "" {
		// this is needed also if you can't pass a flag into other packages, like testing.
		// otherwise it's impossible to inject the config paramater. For example:
		// this doesn't work  : go test -c "vagrant"
		// but this will work : CONFIG="vagrant" go test
		envProfile := os.Getenv("CONFIG")
		if envProfile == "" {
			return errors.New("Please specify a configuration profile via -c or -p or set a CONFIG environment.")
		}

		FileProfile = envProfile
	}

	if FileProfile != "" && PillarProfile != "" {
		return errors.New("The flags -c and -p are exclusive.")
	}

	if FileProfile != "" {
		Profile = FileProfile
		err := readConfigManager(FileProfile)
		if err != nil {
			return err
		}
	}

	if PillarProfile != "" {
		Profile = PillarProfile
		err := readPillar(PillarProfile)
		if err != nil {
			return err
		}
	}

	return nil

}

func readConfigManager(profile string) error {
	cmd := exec.Command("node", "-e", "require('koding-config-manager').printJson('main."+profile+"')")
	return initializeConfig(cmd)
}

func readPillar(profile string) error {
	cmd := exec.Command("salt-call", "pillar.get", profile, "--output=json", "--log-level=warning")
	return initializeConfig(cmd)
}

func initializeConfig(cmd *exec.Cmd) error {
	config, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("Could not execute configuration source: %s\nConfiguration source output:\n%s\n",
			err.Error(), config)
	}

	err = json.Unmarshal(config, &Current)
	if err == nil && (Current == Config{}) {
		err = fmt.Errorf("Empty configuration.")
	}

	if err != nil {
		return fmt.Errorf("Could not unmarshal configuration: %s\nConfiguration source output:\n%s\n",
			err.Error(), config)
	}

	// successfully unmarshalled into Current
	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
)

type Config struct {
	ProjectRoot string
	GoConfig    struct {
		HomePrefix string
		UseLVE     bool
	}
	Mq struct {
		Host          string
		ComponentUser string
		Password      string
	}
	Loggr struct {
		Push   bool
		Url    string
		ApiKey string
	}
	Librato struct {
		Push     bool
		Email    string
		Token    string
		Interval int
	}
}

var Current Config

func LoadConfig(profile string) {
	j, err := exec.Command("node", "-e", "require('koding-config-manager').printJson('main."+profile+"')").CombinedOutput()
	if err != nil {
		fmt.Printf("Could execute Koding config manager: %s\n", err.Error())
		os.Exit(1)
	}

	if err := json.Unmarshal(j, &Current); err != nil {
		fmt.Printf("Koding config manager output:\n%s\nCould not unmarshal config: %s\n", j, err.Error())
		os.Exit(1)
	}
}
<commit_msg>Missing word.<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
)

type Config struct {
	ProjectRoot string
	GoConfig    struct {
		HomePrefix string
		UseLVE     bool
	}
	Mq struct {
		Host          string
		ComponentUser string
		Password      string
	}
	Loggr struct {
		Push   bool
		Url    string
		ApiKey string
	}
	Librato struct {
		Push     bool
		Email    string
		Token    string
		Interval int
	}
}

var Current Config

func LoadConfig(profile string) {
	j, err := exec.Command("node", "-e", "require('koding-config-manager').printJson('main."+profile+"')").CombinedOutput()
	if err != nil {
		fmt.Printf("Could not execute Koding config manager: %s\n", err.Error())
		os.Exit(1)
	}

	if err := json.Unmarshal(j, &Current); err != nil {
		fmt.Printf("Koding config manager output:\n%s\nCould not unmarshal config: %s\n", j, err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Package xkcd allows access to metadata for xkcd comics.
package xkcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

// Comic is a struct that contains information about an xkcd comic.
type Comic struct {
	Num        int    `json:"num"`
	Title      string `json:"title"`
	SafeTitle  string `json:"safe_title"`
	Img        string `json:"img"`
	Alt        string `json:"alt"`
	Year       string `json:"year"`
	Month      string `json:"month"`
	Day        string `json:"day"`
	News       string `json:"news"`
	Link       string `json:"link"`
	Transcript string `json:"transcript"`
}

// New reads from an io.Reader and returns a *Comic struct. Assumes text
// is UTF-8. WARNING: this function will improperly decode comics
// retrieved directly from xkcd.com as xkcd.com provides ISO8859-1
// encoded JSON instead of UTF-8 encoded JSON. Use Get or GetCurrent for
// retrieving comics directly from xkcd.com.
func New(r io.Reader) (*Comic, error) {
	d := json.NewDecoder(r)
	c := new(Comic)
	err := d.Decode(c)
	return c, err
}

// newFixEncoding reads from an io.Reader and returns a *Comic struct.
// This version of New properly handles ISO8859-1 encoded text, like
// that delivered by xkcd.com.
func newFixEncoding(r io.Reader) (*Comic, error) {
	c, err := New(r)
	fixComic(c)
	return c, err
}

const (
	currentURL  = "http://xkcd.com/info.0.json"
	templateURL = "http://xkcd.com/%v/info.0.json"
)

// Get fetches information about the xkcd comic number `n'.
func Get(n int) (*Comic, error) {
	url := fmt.Sprintf(templateURL, n)
	return getByURL(url)
}

// GetCurrent fetches information for the newest xkcd comic.
func GetCurrent() (*Comic, error) {
	return getByURL(currentURL)
}

var ErrNotFound = errors.New("Error retrieving comic")

// getByURL returns information downloaded from `url'.
func getByURL(url string) (*Comic, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, ErrNotFound
	}
	return newFixEncoding(resp.Body)
}

// Image retrieves the comic image from the xkcd server and returns it
// as an image.Image. Comics in GIF format won't contain the entire
// image, see image/gif.Decode for more information.
func (c *Comic) Image() (*Image, error) {
	return getImageFromURL(c.Img)
}
<commit_msg>Add comment to ErrNotFound.<commit_after>// Package xkcd allows access to metadata for xkcd comics.
package xkcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

// Comic is a struct that contains information about an xkcd comic.
type Comic struct {
	Num        int    `json:"num"`
	Title      string `json:"title"`
	SafeTitle  string `json:"safe_title"`
	Img        string `json:"img"`
	Alt        string `json:"alt"`
	Year       string `json:"year"`
	Month      string `json:"month"`
	Day        string `json:"day"`
	News       string `json:"news"`
	Link       string `json:"link"`
	Transcript string `json:"transcript"`
}

// New reads from an io.Reader and returns a *Comic struct. Assumes text
// is UTF-8. WARNING: this function will improperly decode comics
// retrieved directly from xkcd.com as xkcd.com provides ISO8859-1
// encoded JSON instead of UTF-8 encoded JSON. Use Get or GetCurrent for
// retrieving comics directly from xkcd.com.
func New(r io.Reader) (*Comic, error) {
	d := json.NewDecoder(r)
	c := new(Comic)
	err := d.Decode(c)
	return c, err
}

// newFixEncoding reads from an io.Reader and returns a *Comic struct.
// This version of New properly handles ISO8859-1 encoded text, like
// that delivered by xkcd.com.
func newFixEncoding(r io.Reader) (*Comic, error) {
	c, err := New(r)
	fixComic(c)
	return c, err
}

const (
	currentURL  = "http://xkcd.com/info.0.json"
	templateURL = "http://xkcd.com/%v/info.0.json"
)

// Get fetches information about the xkcd comic number `n'.
func Get(n int) (*Comic, error) {
	url := fmt.Sprintf(templateURL, n)
	return getByURL(url)
}

// GetCurrent fetches information for the newest xkcd comic.
func GetCurrent() (*Comic, error) {
	return getByURL(currentURL)
}

// ErrNotFound is returned when the xkcd server returns a 4xx response
// code (like 404).
var ErrNotFound = errors.New("Error retrieving comic")

// getByURL returns information downloaded from `url'.
func getByURL(url string) (*Comic, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, ErrNotFound
	}
	return newFixEncoding(resp.Body)
}

// Image retrieves the comic image from the xkcd server and returns it
// as an image.Image. Comics in GIF format won't contain the entire
// image, see image/gif.Decode for more information.
func (c *Comic) Image() (*Image, error) {
	return getImageFromURL(c.Img)
}
<|endoftext|>


<commit_before>package xlog

import (
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

// Curr version 1.2
// author xuyoug
// mail:xuyoug@yeah.net
// 2015-05-26
//

// 调用方式
// 依次传入：文件名、buf大小、log切换大小格式的字符串、和按时间切换的类型
// l, err := xlog.NewXlog("log/log-{date}-{index}.log", 10000, "10M", xlog.TruncByday)
// 	defer l.Close()
// 	if err != nil {
// 		fmt.Println(err)
// 	}
// l.Log("hello", 123, "asd")
// l.WriteString("test","test")
// l.Witef("this is %d day",i)

// 不定义日志级别（这个东西很坑，大家需求都不一样，请需要的朋友在外层自定义实现）
// 统一在log每一行前加上了时间，精确到秒

// version 1.1
// 2015-04-27
// 再次优化程序结构
// 增加注释
// ticker的优化
// 设定超时后不自动切换log
// 修复设置不按大小切换后，文件名中仍然具有计数的bug
// 不按时间切换log时的bug
// 不按大小切换log的实现
// 修复一些其它BUG
// version 1.0
// 修复bug
// 可以应用于生产环境
// 2015-4-26
// version 0.1
// fix cpu占用的bug
// 结构的优化

type TruncType string

const (
	TruncByday  TruncType = "day"
	TruncByhour TruncType = "hour"
	TruncBymin  TruncType = "min"
	TruncByNo   TruncType = "no"
)

const (
	DefaultLogName string = "Log.log"
	DefaultLogSize int64  = 50 * 1000 * 1000
)

//getFileSize获取文件大小
func getFileSize(file *os.File) (size int64, err error) {
	var tmpfileinfo os.FileInfo
	tmpfileinfo, err = file.Stat()
	if err != nil {
		return 0, err
	}
	return tmpfileinfo.Size(), err
}

//getFmtSize根据设置的大小格式，判断切换大小
func getFmtSize(s string) (size int64, err error) {
	st := strings.ToUpper(s)
	if st == "" {
		return DefaultLogSize, nil
	}
	var ts string
	var ti int64
	_, err = fmt.Sscanf(st, "%d%s", &ti, &ts)
	if err != nil {
		if err == io.EOF {
			_, err = fmt.Sscanf(st, "%d", &ti)
		}
		if err != nil {
			panic("Error in format switch size")
			return 0, err
		}
	}
	if ti == 0 {
		return 0, nil
	}
	switch ts {
	case "K", "KB":
		return ti * 1000, nil
	case "M", "MB":
		return ti * 1000 * 1000, nil
	case "G", "GB":
		return ti * 1000 * 1000 * 1000, nil
	case "B", "":
		return ti, nil
	default:
		return DefaultLogSize, nil
	}
	return 0, nil
}

//进行当前应该使用的log名的获取
func (x *Xlog) getBaseLog() (d string) {
	truncnameold := x.truncname
	if x.Trunc == "" || x.Logname == "" {
		x.Logname = DefaultLogName
	}
	switch x.Trunc {
	case TruncByday:
		x.truncname = time.Now().Format("20060102")
	case TruncByhour:
		x.truncname = time.Now().Format("2006010215")
	case TruncBymin:
		x.truncname = time.Now().Format("200601021504")
	case TruncByNo:
		x.truncname = time.Now().Format("20060102") //不按时间切换的话则按启动日期记录
	default:
		x.truncname = ""
	}
	if truncnameold != x.truncname { //按时间切换之后，日志计数重新开始
		x.curIndex = 0
	}
	d = strings.Replace(x.Logname, "{DATE}", x.truncname, -1)
	d = strings.Replace(d, "{date}", x.truncname, -1)

	if x.swcsize == 0 { //不按大小切换的话，直接不理睬
		d = strings.Replace(d, "{index}", "", -1)
		d = strings.Replace(d, "{INDEX}", "", -1)
		return d
	}
	index := fmt.Sprintf("%03d", x.curIndex)
	d = strings.Replace(d, "{INDEX}", index, -1)
	d = strings.Replace(d, "{index}", index, -1)
	return d
}

//进行log切换
func (x *Xlog) switchlog() (is bool) {
	//判断是否需要根据大小切换
	//对于自动到达相同log组最后一个的情况，如果需要判断大小仍然进行大小判断，不判断的话就继续写
	var err error
	tmpi1 := x.curIndex
	if x.swcsize != 0 {
		x.checkSize()
	}
	tmpi2 := x.curIndex
	oldLogname := x.curLogname
	//判断是否需要根据时间切换
	var newLogname string
	if x.Trunc != TruncByNo {
		newLogname = x.getBaseLog()
	} else {
		newLogname = oldLogname
	}
	//如果都不需要则不切换log
	if tmpi1 == tmpi2 || oldLogname == newLogname {
		return false
	}

	// open new file fist
	var tmp_curFile *os.File
	tmp_curFile, err = os.OpenFile(newLogname, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777)
	var oldcurFile *os.File
	if err != nil {
		panic("Error in switch log (open new file)")
	} else {
		oldcurFile = x.curFile
		// if there is no err then use new file
		x.curFile = tmp_curFile
		x.curLogname = newLogname
	}
	// close old file then
	err = oldcurFile.Close()
	if err != nil {
		panic("Error in switch log (close old file)")
	}
	return true
}

//检查文件大小是否达到切换标准，达到的话标记一下。不切换的话不标记
func (x *Xlog) checkSize() {
	size, err := getFileSize(x.curFile)
	if x.swcsize != 0 && (size >= x.swcsize || err != nil) {
		x.curIndex += 1
	}
}

//判断文件是否存在
func (x *Xlog) isExistFile() bool {
	_, err := os.Stat(x.getBaseLog())
	return err == nil || os.IsExist(err)
}

//已经存在相同log组的话到达最后一个log
func (x *Xlog) gotoLastFile() {
	for {
		if x.isExistFile() {
			x.curIndex += 1
			continue
		}
		x.curIndex -= 1
		if x.curIndex < 0 {
			x.curIndex = 0
		}
		break
	}
}

//Xlog结构体
type Xlog struct {
	Logname     string
	curLogname  string
	curFile     *os.File
	curIndex    int
	c           chan string
	Trunc       TruncType
	Switchsize  string
	swcsize     int64
	logCurindex int
	ticker      *time.Ticker
	truncname   string
}

//初始化函数 依次传入：文件名、buf大小、log切换大小格式的字符串、和按时间切换的类型
func NewXlog(s string, bufsize int, swcsizes string, tr TruncType) (*Xlog, error) {
	x := new(Xlog)
	var err error
	x.Logname = s
	x.logCurindex = 0
	x.Trunc = tr
	x.Switchsize = swcsizes
	x.swcsize, _ = getFmtSize(swcsizes)
	x.curIndex = 0
	x.c = make(chan string, bufsize)
	if x.swcsize != 0 {
		x.gotoLastFile()
	}
	x.curLogname = x.getBaseLog()
	if x.swcsize != 0 || x.Trunc != TruncByNo {
		x.ticker = time.NewTicker(time.Millisecond * 200) //每200ms进行一次切换检测
	}
	x.curFile, err = os.OpenFile(x.curLogname, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777)
	go x.pushlog()
	return x, err
}

//Close关闭log对象：关闭缓存chan，关闭ticker
//注意：要当所有的内容写完之后才会进行关闭
func (x *Xlog) Close() (err error) {
	for {
		if x.GetBufDep() == 0 {
			close(x.c)
			err = x.curFile.Close()
			x.ticker.Stop()
			return err
		} else {
			time.Sleep(time.Nanosecond * 10000)
		}
	}
}

//对外提供，获取缓存chan的深度
func (x *Xlog) GetBufDep() int {
	return len(x.c)
}

//对外提供的3个写入方法
func (x *Xlog) WriteString(s ...interface{}) {
	tmps := fmt.Sprintln(s...)
	tmps = time.Now().Format("2006-01-02 15:04:05 ") + tmps
	x.c <- tmps
}

func (x *Xlog) Log(s ...interface{}) {
	x.WriteString(s...)
}

func (x *Xlog) Writef(s string, i ...interface{}) {
	tmps := fmt.Sprintf(s, i...)
	tmps = time.Now().Format("2006-01-02 15:04:05 ") + tmps
	x.c <- tmps
}

//作为独立gorotiune执行  负责获取缓存并写入文件
//负责ticker的调度
//实现无操作时的休眠
func (x *Xlog) pushlog() {
	var sleeptimes int
	var issleep bool = false
	for {
		select {
		case s, ok := <-x.c:
			if issleep { //从休眠中唤醒需要先切换log、重新设置ticker
				x.switchlog()
				x.ticker = time.NewTicker(time.Millisecond * 200)
			}
			if ok {
				x.curFile.WriteString(s)
				sleeptimes = 0 //写入log后重置休眠计数器
			}
			//对于ticker的处理，加入了休眠计数
		case <-x.ticker.C:
			if sleeptimes > 1000 { //1000次tick都无写入则进入休眠
				x.ticker.Stop()
				issleep = true
			}
			x.switchlog()
			sleeptimes++
		}
	}
}

//错误检查，如遇到错误就打印出来
func checkErr(err error) {
	if err != nil {
		fmt.Println("Error:", err.Error())
	}
}
<commit_msg>20150526<commit_after>package xlog

import (
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

// Curr version 1.2
// author xuyoug
// mail:xuyoug@yeah.net
// 2015-05-26
//

// 调用方式
// 依次传入：文件名、buf大小、log切换大小格式的字符串、和按时间切换的类型
// l, err := xlog.NewXlog("log/log-{date}-{index}.log", 10000, "10M", xlog.TruncByday)
// 	defer l.Close()
// 	if err != nil {
// 		fmt.Println(err)
// 	}
// l.Log("hello", 123, "asd")
// l.WriteString("test","test")
// l.Witef("this is %d day",i)

// 不定义日志级别（这个东西很坑，大家需求都不一样，请需要的朋友在外层自定义实现）
// 统一在log每一行前加上了时间，精确到秒

// version 1.1
// 2015-04-27
// 再次优化程序结构
// 增加注释
// ticker的优化
// 设定超时后不自动切换log
// 修复设置不按大小切换后，文件名中仍然具有计数的bug
// 不按时间切换log时的bug
// 不按大小切换log的实现
// 修复一些其它BUG
// version 1.0
// 修复bug
// 可以应用于生产环境
// 2015-4-26
// version 0.1
// fix cpu占用的bug
// 结构的优化

type TruncType string

const (
	TruncByday  TruncType = "day"
	TruncByhour TruncType = "hour"
	TruncBymin  TruncType = "min"
	TruncByNo   TruncType = "no"
)

const (
	DefaultLogName string = "Log.log"
	DefaultLogSize int64  = 50 * 1000 * 1000
)

//getFileSize获取文件大小
func getFileSize(file *os.File) (size int64, err error) {
	var tmpfileinfo os.FileInfo
	tmpfileinfo, err = file.Stat()
	if err != nil {
		return 0, err
	}
	return tmpfileinfo.Size(), err
}

//getFmtSize根据设置的大小格式，判断切换大小
func getFmtSize(s string) (size int64, err error) {
	st := strings.ToUpper(s)
	if st == "" {
		return DefaultLogSize, nil
	}
	var ts string
	var ti int64
	_, err = fmt.Sscanf(st, "%d%s", &ti, &ts)
	if err != nil {
		if err == io.EOF {
			_, err = fmt.Sscanf(st, "%d", &ti)
		}
		if err != nil {
			panic("Error in format switch size")
			return 0, err
		}
	}
	if ti == 0 {
		return 0, nil
	}
	switch ts {
	case "K", "KB":
		return ti * 1000, nil
	case "M", "MB":
		return ti * 1000 * 1000, nil
	case "G", "GB":
		return ti * 1000 * 1000 * 1000, nil
	case "B", "":
		return ti, nil
	default:
		return DefaultLogSize, nil
	}
	return 0, nil
}

//getBaseLog进行当前应该使用的log名的获取
func (x *Xlog) getBaseLog() (d string) {
	truncnameold := x.truncname
	if x.Trunc == "" || x.Logname == "" {
		x.Logname = DefaultLogName
	}
	switch x.Trunc {
	case TruncByday:
		x.truncname = time.Now().Format("20060102")
	case TruncByhour:
		x.truncname = time.Now().Format("2006010215")
	case TruncBymin:
		x.truncname = time.Now().Format("200601021504")
	case TruncByNo:
		x.truncname = time.Now().Format("20060102") //不按时间切换的话则按启动日期记录
	default:
		x.truncname = ""
	}
	if truncnameold != x.truncname { //按时间切换之后，日志计数重新开始
		x.curIndex = 0
	}
	d = strings.Replace(x.Logname, "{DATE}", x.truncname, -1)
	d = strings.Replace(d, "{date}", x.truncname, -1)

	if x.swcsize == 0 { //不按大小切换的话，直接不理睬
		d = strings.Replace(d, "{index}", "", -1)
		d = strings.Replace(d, "{INDEX}", "", -1)
		return d
	}
	index := fmt.Sprintf("%03d", x.curIndex)
	d = strings.Replace(d, "{INDEX}", index, -1)
	d = strings.Replace(d, "{index}", index, -1)
	return d
}

//switchlog进行log切换
func (x *Xlog) switchlog() (is bool) {
	//判断是否需要根据大小切换
	//对于自动到达相同log组最后一个的情况，如果需要判断大小仍然进行大小判断，不判断的话就继续写
	var err error
	tmpi1 := x.curIndex
	if x.swcsize != 0 {
		x.checkSize()
	}
	tmpi2 := x.curIndex
	oldLogname := x.curLogname
	//判断是否需要根据时间切换
	var newLogname string
	if x.Trunc != TruncByNo {
		newLogname = x.getBaseLog()
	} else {
		newLogname = oldLogname
	}
	//如果都不需要则不切换log
	if tmpi1 == tmpi2 || oldLogname == newLogname {
		return false
	}

	// open new file fist
	var tmp_curFile *os.File
	tmp_curFile, err = os.OpenFile(newLogname, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777)
	var oldcurFile *os.File
	if err != nil {
		panic("Error in switch log (open new file)")
	} else {
		oldcurFile = x.curFile
		// if there is no err then use new file
		x.curFile = tmp_curFile
		x.curLogname = newLogname
	}
	// close old file then
	err = oldcurFile.Close()
	if err != nil {
		panic("Error in switch log (close old file)")
	}
	return true
}

//checkSize检查文件大小是否达到切换标准，达到的话标记一下。不切换的话不标记
func (x *Xlog) checkSize() {
	size, err := getFileSize(x.curFile)
	if x.swcsize != 0 && (size >= x.swcsize || err != nil) {
		x.curIndex += 1
	}
}

//isExistFile判断文件是否存在
func (x *Xlog) isExistFile() bool {
	_, err := os.Stat(x.getBaseLog())
	return err == nil || os.IsExist(err)
}

//gotoLastFile用于启动时如果已经存在相同log组的话到达最后一个log
func (x *Xlog) gotoLastFile() {
	for {
		if x.isExistFile() {
			x.curIndex += 1
			continue
		}
		x.curIndex -= 1
		if x.curIndex < 0 {
			x.curIndex = 0
		}
		break
	}
}

//Xlog对象定义
type Xlog struct {
	Logname     string
	curLogname  string
	curFile     *os.File
	curIndex    int
	c           chan string
	Trunc       TruncType
	Switchsize  string
	swcsize     int64
	logCurindex int
	ticker      *time.Ticker
	truncname   string
}

//NewXlog初始化函数 依次传入：文件名、buf大小、log切换大小格式的字符串、和按时间切换的类型
func NewXlog(s string, bufsize int, swcsizes string, tr TruncType) (*Xlog, error) {
	x := new(Xlog)
	var err error
	x.Logname = s
	x.logCurindex = 0
	x.Trunc = tr
	x.Switchsize = swcsizes
	x.swcsize, _ = getFmtSize(swcsizes)
	x.curIndex = 0
	x.c = make(chan string, bufsize)
	if x.swcsize != 0 {
		x.gotoLastFile()
	}
	x.curLogname = x.getBaseLog()
	if x.swcsize != 0 || x.Trunc != TruncByNo {
		x.ticker = time.NewTicker(time.Millisecond * 200) //每200ms进行一次切换检测
	}
	x.curFile, err = os.OpenFile(x.curLogname, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777)
	go x.pushlog()
	return x, err
}

//Close关闭log对象：关闭缓存chan，关闭ticker 注意：要当所有的内容写完之后才会进行关闭
func (x *Xlog) Close() (err error) {
	for {
		if x.GetBufDep() == 0 {
			close(x.c)
			err = x.curFile.Close()
			x.ticker.Stop()
			return err
		} else {
			time.Sleep(time.Nanosecond * 10000)
		}
	}
}

//GetBufDep对外提供，获取缓存chan的深度
func (x *Xlog) GetBufDep() int {
	return len(x.c)
}

//WriteString对外提供的3个写入方法之一
func (x *Xlog) WriteString(s ...interface{}) {
	tmps := fmt.Sprintln(s...)
	tmps = time.Now().Format("2006-01-02 15:04:05 ") + tmps
	x.c <- tmps
}

//Log对外提供的3个写入方法之一
func (x *Xlog) Log(s ...interface{}) {
	x.WriteString(s...)
}

//Writef对外提供的3个写入方法之一
func (x *Xlog) Writef(s string, i ...interface{}) {
	tmps := fmt.Sprintf(s, i...)
	tmps = time.Now().Format("2006-01-02 15:04:05 ") + tmps
	x.c <- tmps
}

//pushlog作为独立gorotiune执行  负责获取缓存并写入文件
//负责ticker的调度
//实现无操作时的休眠
func (x *Xlog) pushlog() {
	var sleeptimes int
	var issleep bool = false
	for {
		select {
		case s, ok := <-x.c:
			if issleep { //从休眠中唤醒需要先切换log、重新设置ticker
				x.switchlog()
				x.ticker = time.NewTicker(time.Millisecond * 200)
			}
			if ok {
				x.curFile.WriteString(s)
				sleeptimes = 0 //写入log后重置休眠计数器
			}
			//对于ticker的处理，加入了休眠计数
		case <-x.ticker.C:
			if sleeptimes > 1000 { //1000次tick都无写入则进入休眠
				x.ticker.Stop()
				issleep = true
			}
			x.switchlog()
			sleeptimes++
		}
	}
}

//God luck！
<|endoftext|>


<commit_before>package sms

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"

	xco "github.com/mndrix/go-xco"
)

// xmppProcess is the piece which interacts with the XMPP network and
// converts those communications into values which the rest of
// sms-over-xmpp can understand.
type xmppProcess struct {
	// where to connect to the XMPP server
	host string
	port int

	// credentials for XMPP auth
	name   string
	secret string

	// channels for communicating with the Gateway process
	gatewayRx chan<- *xco.Message
	gatewayTx <-chan *xco.Message

	// channels for communicating with the XMPP server
	xmppTx chan<- interface{}

	// contacted records whether the local and remote JIDs,
	// respectively, have contacted each other during the life of this
	// process.
	contacted map[xco.Address]map[xco.Address]bool

	// users records XMPP details about each local user.
	//
	// The map key is the user's bare JID.
	users map[xco.Address]*xmppUser
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's
// health.  if that channel closes, the XMPP process has died.
func (x *xmppProcess) run() <-chan struct{} {
	x.users = make(map[xco.Address]*xmppUser)
	opts := xco.Options{
		Name:         x.name,
		SharedSecret: x.secret,
		Address:      fmt.Sprintf("%s:%d", x.host, x.port),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go x.loop(opts, healthCh)
	return healthCh
}

func (x *xmppProcess) loop(opts xco.Options, healthCh chan<- struct{}) {
	defer func() { close(healthCh) }()

	c, err := xco.NewComponent(opts)
	if err != nil {
		log.Printf("can't create internal XMPP component: %s", err)
		return
	}

	tx, rx, errx := c.RunAsync()
	x.xmppTx = tx
	for {
		select {
		case st := <-rx:
			switch stanza := st.(type) {
			case *xco.Message:
				log.Printf("Message: %+v", stanza)
				if stanza.Body == "" {
					log.Printf("  ignoring message with empty body")
					break
				}
				local := &stanza.Header.To
				remote := &stanza.Header.From
				if x.isFirstContact(local, remote) {
					x.hadContact(local, remote)
					p := x.requestSubscription(local, remote)
					x.send(p)
				}
				go func() { x.gatewayRx <- stanza }()
			case *xco.Iq:
				if stanza.IsDiscoInfo() {
					log.Printf("Disco: %+v", stanza)
					ids, features := x.describeService()
					stanza, err = stanza.DiscoInfoReply(ids, features)
					if err == nil {
						x.send(stanza)
					}
				} else {
					log.Printf("Iq: %+v", stanza)
				}
			case *xco.Presence:
				log.Printf("Presence: %+v", stanza)
				local := &stanza.Header.To
				remote := &stanza.Header.From
				contact := x.user(local).contact(remote)

				switch stanza.Type {
				case "probe":
					stanza = x.presenceAvailable(stanza)
					x.send(stanza)
				case "subscribe", "unsubscribe":
					stanzas := x.handleSubscribeUnsubscribe(stanza)
					x.send(stanzas...)
				case "subscribed":
					if contact.subTo == pending {
						contact.subTo = yes
					}
				case "unsubscribed":
					contact.subTo = no
				}
			case *xml.StartElement:
				log.Printf("Unknown: %+v", stanza)
			default:
				panic(fmt.Sprintf("Unexpected stanza type: %#v", st))
			}
		case stanza := <-x.gatewayTx:
			local := &stanza.Header.From
			remote := &stanza.Header.To
			if x.isFirstContact(local, remote) {
				x.hadContact(local, remote)
				//p := x.requestSubscription(local, remote)
				x.send( /* p, */ stanza)
			} else {
				x.send(stanza)
			}
		case err = <-errx:
		}

		if err != nil {
			break
		}
	}

	log.Printf("lost XMPP connection: %s", err)
}

// send stanzas to the remote XMPP server.  The transmission happens
// asynchronously.
func (x *xmppProcess) send(stanzas ...interface{}) {
	// bookkeeping for outgoing stanzas
	for _, s := range stanzas {
		switch stanza := s.(type) {
		case *xco.Presence:
			local := &stanza.Header.From
			remote := &stanza.Header.To
			contact := x.user(local).contact(remote)

			switch stanza.Type {
			case "subscribe":
				if contact.subTo == no {
					contact.subTo = pending
				}
			case "unsubscribe":
				contact.subTo = no
			case "subscribed":
				contact.subFrom = yes
			case "unsubscribed":
				contact.subFrom = no
			}
		}
	}

	// perform actual transmission asynchronously
	go func() {
		for _, stanza := range stanzas {
			x.xmppTx <- stanza
		}
	}()
}

func (x *xmppProcess) describeService() ([]xco.DiscoIdentity, []xco.DiscoFeature) {
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	features := []xco.DiscoFeature{
		{
			Var: "urn:xmpp:receipts",
		},
	}
	return ids, features
}

// isFirstContact returns true if the entities local and remote have
// not had any contact during the life of this process.  Contact
// includes both messages and subscriptions.
func (x *xmppProcess) isFirstContact(local, remote *xco.Address) bool {
	if x.contacted == nil {
		return true
	}

	local = local.Bare()
	remotes := x.contacted[*local]
	if remotes == nil {
		return true
	}

	remote = remote.Bare()
	return !remotes[*remote]
}

// hadContact records the fact that local and remote have contacted
// each other.  It could be for the first time or any subsequent time.
func (x *xmppProcess) hadContact(local, remote *xco.Address) {
	local = local.Bare()
	remote = remote.Bare()
	if x.contacted == nil {
		x.contacted = make(map[xco.Address]map[xco.Address]bool)
	}
	remotes := x.contacted[*local]
	if remotes == nil {
		remotes = make(map[xco.Address]bool)
		x.contacted[*local] = remotes
	}
	remotes[*remote] = true
}

func (x *xmppProcess) presenceAvailable(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	return stanza
}

func (x *xmppProcess) handleSubscribeUnsubscribe(p *xco.Presence) []interface{} {
	// RFC says to use bare JIDs
	local := (&p.Header.To).Bare()
	remote := (&p.Header.From).Bare()
	contact := x.user(local).contact(remote)

	stanza := &xco.Presence{
		Header: xco.Header{
			From: *local,
			To:   *remote,
			ID:   NewId(),
		},
	}
	switch p.Type {
	case "subscribe":
		if contact.subFrom == no {
			contact.subFrom = pending
		}
		stanza.Type = "subscribed"
		stanzas := []interface{}{
			stanza,
			x.presenceAvailable(p),
		}
		if x.isFirstContact(local, remote) {
			x.hadContact(local, remote)
			//stanzas = append(stanzas, x.requestSubscription(local, remote))
		}
		return stanzas
	case "unsubscribe":
		contact.subFrom = no
		stanza.Type = "unavailable"
		return []interface{}{stanza}
	}

	return nil
}

func (x *xmppProcess) requestSubscription(local, remote *xco.Address) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: *local,
			To:   *remote,
			ID:   NewId(),
		},
		Type: "subscribe",
	}
	return stanza
}

// user returns the XMPP user for a local JID.  Creates an empty one
// if none exists.
func (x *xmppProcess) user(a *xco.Address) *xmppUser {
	local := *(a.Bare())
	user, ok := x.users[local]
	if !ok {
		user = &xmppUser{}
		x.users[local] = user
	}
	return user
}
<commit_msg>Change meaning of isFirstContact<commit_after>package sms

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"

	xco "github.com/mndrix/go-xco"
)

// xmppProcess is the piece which interacts with the XMPP network and
// converts those communications into values which the rest of
// sms-over-xmpp can understand.
type xmppProcess struct {
	// where to connect to the XMPP server
	host string
	port int

	// credentials for XMPP auth
	name   string
	secret string

	// channels for communicating with the Gateway process
	gatewayRx chan<- *xco.Message
	gatewayTx <-chan *xco.Message

	// channels for communicating with the XMPP server
	xmppTx chan<- interface{}

	// contacted records whether the local and remote JIDs,
	// respectively, have sent a chat message between themselves
	// during the life of this process.  A single message in either
	// direction is enough.
	contacted map[xco.Address]map[xco.Address]bool

	// users records XMPP details about each local user.
	//
	// The map key is the user's bare JID.
	users map[xco.Address]*xmppUser
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's
// health.  if that channel closes, the XMPP process has died.
func (x *xmppProcess) run() <-chan struct{} {
	x.users = make(map[xco.Address]*xmppUser)
	opts := xco.Options{
		Name:         x.name,
		SharedSecret: x.secret,
		Address:      fmt.Sprintf("%s:%d", x.host, x.port),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go x.loop(opts, healthCh)
	return healthCh
}

func (x *xmppProcess) loop(opts xco.Options, healthCh chan<- struct{}) {
	defer func() { close(healthCh) }()

	c, err := xco.NewComponent(opts)
	if err != nil {
		log.Printf("can't create internal XMPP component: %s", err)
		return
	}

	tx, rx, errx := c.RunAsync()
	x.xmppTx = tx
	for {
		select {
		case st := <-rx:
			switch stanza := st.(type) {
			case *xco.Message:
				log.Printf("Message: %+v", stanza)
				if stanza.Body == "" {
					log.Printf("  ignoring message with empty body")
					break
				}
				local := &stanza.Header.To
				remote := &stanza.Header.From
				if x.isFirstContact(local, remote) {
					x.hadContact(local, remote)
					p := x.requestSubscription(local, remote)
					x.send(p)
				}
				go func() { x.gatewayRx <- stanza }()
			case *xco.Iq:
				if stanza.IsDiscoInfo() {
					log.Printf("Disco: %+v", stanza)
					ids, features := x.describeService()
					stanza, err = stanza.DiscoInfoReply(ids, features)
					if err == nil {
						x.send(stanza)
					}
				} else {
					log.Printf("Iq: %+v", stanza)
				}
			case *xco.Presence:
				log.Printf("Presence: %+v", stanza)
				local := &stanza.Header.To
				remote := &stanza.Header.From
				contact := x.user(local).contact(remote)

				switch stanza.Type {
				case "probe":
					stanza = x.presenceAvailable(stanza)
					x.send(stanza)
				case "subscribe", "unsubscribe":
					stanzas := x.handleSubscribeUnsubscribe(stanza)
					x.send(stanzas...)
				case "subscribed":
					if contact.subTo == pending {
						contact.subTo = yes
					}
				case "unsubscribed":
					contact.subTo = no
				}
			case *xml.StartElement:
				log.Printf("Unknown: %+v", stanza)
			default:
				panic(fmt.Sprintf("Unexpected stanza type: %#v", st))
			}
		case stanza := <-x.gatewayTx:
			local := &stanza.Header.From
			remote := &stanza.Header.To
			if x.isFirstContact(local, remote) {
				//p := x.requestSubscription(local, remote)
				x.send( /* p, */ stanza)
			} else {
				x.send(stanza)
			}
		case err = <-errx:
		}

		if err != nil {
			break
		}
	}

	log.Printf("lost XMPP connection: %s", err)
}

// send stanzas to the remote XMPP server.  The transmission happens
// asynchronously.
func (x *xmppProcess) send(stanzas ...interface{}) {
	// bookkeeping for outgoing stanzas
	for _, s := range stanzas {
		switch stanza := s.(type) {
		case *xco.Message:
			local := &stanza.Header.From
			remote := &stanza.Header.To
			x.hadContact(local, remote)
		case *xco.Presence:
			local := &stanza.Header.From
			remote := &stanza.Header.To
			contact := x.user(local).contact(remote)

			switch stanza.Type {
			case "subscribe":
				if contact.subTo == no {
					contact.subTo = pending
				}
			case "unsubscribe":
				contact.subTo = no
			case "subscribed":
				contact.subFrom = yes
			case "unsubscribed":
				contact.subFrom = no
			}
		}
	}

	// perform actual transmission asynchronously
	go func() {
		for _, stanza := range stanzas {
			x.xmppTx <- stanza
		}
	}()
}

func (x *xmppProcess) describeService() ([]xco.DiscoIdentity, []xco.DiscoFeature) {
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	features := []xco.DiscoFeature{
		{
			Var: "urn:xmpp:receipts",
		},
	}
	return ids, features
}

// isFirstContact returns true if the entities local and remote have
// not sent any messages to each other during the life of this
// process.  Only chat messages count, not presence or iq, etc.
func (x *xmppProcess) isFirstContact(local, remote *xco.Address) bool {
	if x.contacted == nil {
		return true
	}

	local = local.Bare()
	remotes := x.contacted[*local]
	if remotes == nil {
		return true
	}

	remote = remote.Bare()
	return !remotes[*remote]
}

// hadContact records the fact that a chat message has been sent
// between local and remote.  This could be a single message in either
// direction.
func (x *xmppProcess) hadContact(local, remote *xco.Address) {
	local = local.Bare()
	remote = remote.Bare()
	if x.contacted == nil {
		x.contacted = make(map[xco.Address]map[xco.Address]bool)
	}
	remotes := x.contacted[*local]
	if remotes == nil {
		remotes = make(map[xco.Address]bool)
		x.contacted[*local] = remotes
	}
	remotes[*remote] = true
}

func (x *xmppProcess) presenceAvailable(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	return stanza
}

func (x *xmppProcess) handleSubscribeUnsubscribe(p *xco.Presence) []interface{} {
	// RFC says to use bare JIDs
	local := (&p.Header.To).Bare()
	remote := (&p.Header.From).Bare()
	contact := x.user(local).contact(remote)

	stanza := &xco.Presence{
		Header: xco.Header{
			From: *local,
			To:   *remote,
			ID:   NewId(),
		},
	}
	switch p.Type {
	case "subscribe":
		if contact.subFrom == no {
			contact.subFrom = pending
		}
		stanza.Type = "subscribed"
		stanzas := []interface{}{
			stanza,
			x.presenceAvailable(p),
		}
		if x.isFirstContact(local, remote) {
			//stanzas = append(stanzas, x.requestSubscription(local, remote))
		}
		return stanzas
	case "unsubscribe":
		contact.subFrom = no
		stanza.Type = "unavailable"
		return []interface{}{stanza}
	}

	return nil
}

func (x *xmppProcess) requestSubscription(local, remote *xco.Address) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: *local,
			To:   *remote,
			ID:   NewId(),
		},
		Type: "subscribe",
	}
	return stanza
}

// user returns the XMPP user for a local JID.  Creates an empty one
// if none exists.
func (x *xmppProcess) user(a *xco.Address) *xmppUser {
	local := *(a.Bare())
	user, ok := x.users[local]
	if !ok {
		user = &xmppUser{}
		x.users[local] = user
	}
	return user
}
<|endoftext|>


<commit_before>package shared

type GatewayStatus struct {
	EUI               string   `json:"eui,omitempty"`
	Time              string   `json:"time,omitempty"`
	Latitude          *float64 `json:"latitude,omitempty"`
	Longitude         *float64 `json:"longitude,omitempty"`
	Altitude          *float64 `json:"altitude,omitempty"`
	RxCount           *int     `json:"rxCount,omitempty"`
	RxOk              *int     `json:"rxOk,omitempty"`
	RxForwarded       *int     `json:"rxForwarded,omitempty"`
	AckRatio          *float64 `json:"ackRatio,omitempty"`
	DatagramsReceived *int     `json:"datagramsReceived,omitempty"`
	DatagramsSent     *int     `json:"datagramsSent,omitempty"`
}

type RxPacket struct {
	Time string `json:"time,omitempty"`
	Data string `json:"data,omitempty"`
}
<commit_msg>Included gateway EUI in RxPacket<commit_after>package shared

type GatewayStatus struct {
	Eui               string   `json:"eui,omitempty"`
	Time              string   `json:"time,omitempty"`
	Latitude          *float64 `json:"latitude,omitempty"`
	Longitude         *float64 `json:"longitude,omitempty"`
	Altitude          *float64 `json:"altitude,omitempty"`
	RxCount           *int     `json:"rxCount,omitempty"`
	RxOk              *int     `json:"rxOk,omitempty"`
	RxForwarded       *int     `json:"rxForwarded,omitempty"`
	AckRatio          *float64 `json:"ackRatio,omitempty"`
	DatagramsReceived *int     `json:"datagramsReceived,omitempty"`
	DatagramsSent     *int     `json:"datagramsSent,omitempty"`
}

type RxPacket struct {
	GatewayEui string `json:"gatewayEui,omitempty"`
	Time       string `json:"time,omitempty"`
	Data       string `json:"data,omitempty"`
}
<|endoftext|>


<commit_before>package user

import (
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/api/spaces"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/flags"
	"github.com/cloudfoundry/cli/plugin/models"
)

type SpaceUsers struct {
	ui          terminal.UI
	config      core_config.Reader
	spaceRepo   spaces.SpaceRepository
	userRepo    api.UserRepository
	orgReq      requirements.OrganizationRequirement
	pluginModel *[]plugin_models.GetSpaceUsers_Model
	pluginCall  bool
}

type userPrinter interface {
	printUsers()
}

type pluginPrinter struct {
	userPrinter
	usersMap               map[string]plugin_models.GetSpaceUsers_Model
	userLister             func(spaceGuid string, role string) ([]models.UserFields, error)
	spaceRoleToDisplayName map[string]string
	space                  models.Space
	pluginModel            *[]plugin_models.GetSpaceUsers_Model
}

type uiPrinter struct {
	userPrinter
	ui                     terminal.UI
	userLister             func(spaceGuid string, role string) ([]models.UserFields, error)
	spaceRoleToDisplayName map[string]string
	space                  models.Space
}

func init() {
	command_registry.Register(&SpaceUsers{})
}

func (cmd *SpaceUsers) MetaData() command_registry.CommandMetadata {
	return command_registry.CommandMetadata{
		Name:        "space-users",
		Description: T("Show space users by role"),
		Usage:       T("CF_NAME space-users ORG SPACE"),
	}
}

func (cmd *SpaceUsers) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) (reqs []requirements.Requirement, err error) {
	if len(fc.Args()) != 2 {
		cmd.ui.Failed(T("Incorrect Usage. Requires arguments\n\n") + command_registry.Commands.CommandUsage("space-users"))
	}

	cmd.orgReq = requirementsFactory.NewOrganizationRequirement(fc.Args()[0])

	reqs = []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
		cmd.orgReq,
	}
	return
}

func (cmd *SpaceUsers) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command {
	cmd.ui = deps.Ui
	cmd.config = deps.Config
	cmd.userRepo = deps.RepoLocator.GetUserRepository()
	cmd.spaceRepo = deps.RepoLocator.GetSpaceRepository()
	cmd.pluginCall = pluginCall
	cmd.pluginModel = deps.PluginModels.SpaceUsers

	return cmd
}

func (cmd *SpaceUsers) Execute(c flags.FlagContext) {
	spaceName := c.Args()[1]
	org := cmd.orgReq.GetOrganization()

	space, err := cmd.spaceRepo.FindByNameInOrg(spaceName, org.Guid)
	if err != nil {
		cmd.ui.Failed(err.Error())
	}

	cmd.ui.Say(T("Getting users in org {{.TargetOrg}} / space {{.TargetSpace}} as {{.CurrentUser}}",
		map[string]interface{}{
			"TargetOrg":   terminal.EntityNameColor(org.Name),
			"TargetSpace": terminal.EntityNameColor(space.Name),
			"CurrentUser": terminal.EntityNameColor(cmd.config.Username()),
		}))

	printer := cmd.getPrinter(space)
	printer.printUsers()
}

func (cmd *SpaceUsers) getPrinter(space models.Space) userPrinter {
	var spaceRoleToDisplayName = map[string]string{
		models.SPACE_MANAGER:   T("SPACE MANAGER"),
		models.SPACE_DEVELOPER: T("SPACE DEVELOPER"),
		models.SPACE_AUDITOR:   T("SPACE AUDITOR"),
	}

	if cmd.pluginCall {
		return &pluginPrinter{
			space:                  space,
			pluginModel:            cmd.pluginModel,
			usersMap:               make(map[string]plugin_models.GetSpaceUsers_Model),
			userLister:             cmd.getUserLister(),
			spaceRoleToDisplayName: spaceRoleToDisplayName,
		}
	}
	return &uiPrinter{
		ui:                     cmd.ui,
		space:                  space,
		userLister:             cmd.getUserLister(),
		spaceRoleToDisplayName: spaceRoleToDisplayName,
	}
}

func (cmd *SpaceUsers) getUserLister() func(spaceGuid string, role string) ([]models.UserFields, error) {
	if cmd.config.IsMinApiVersion("2.21.0") {
		return cmd.userRepo.ListUsersInSpaceForRoleWithNoUAA
	}
	return cmd.userRepo.ListUsersInSpaceForRole
}

func (p *pluginPrinter) printUsers() {
	for role, _ := range p.spaceRoleToDisplayName {
		users, _ := p.userLister(p.space.Guid, role)
		for _, user := range users {
			u, found := p.usersMap[user.Username]
			if !found {
				u = plugin_models.GetSpaceUsers_Model{}
				u.Username = user.Username
				u.Guid = user.Guid
				u.IsAdmin = user.IsAdmin
				u.Roles = make([]string, 1)
				u.Roles[0] = role
				p.usersMap[user.Username] = u
			} else {
				u.Roles = append(u.Roles, role)
				p.usersMap[user.Username] = u
			}
		}

	}
	for _, v := range p.usersMap {
		*(p.pluginModel) = append(*(p.pluginModel), v)
	}
}

func (p *uiPrinter) printUsers() {
	for role, displayName := range p.spaceRoleToDisplayName {
		users, err := p.userLister(p.space.Guid, role)
		if err != nil {
			p.ui.Failed(T("Failed fetching space-users for role {{.SpaceRoleToDisplayName}}.\n{{.Error}}",
				map[string]interface{}{
					"Error":                  err.Error(),
					"SpaceRoleToDisplayName": displayName,
				}))
			return
		}
		p.ui.Say("")
		p.ui.Say("%s", terminal.HeaderColor(displayName))

		if len(users) == 0 {
			p.ui.Say("none")
		} else {
			for _, user := range users {
				p.ui.Say("  %s", user.Username)
			}
		}
	}
}
<commit_msg>Deduplicate call from either side of a branch<commit_after>package user

import (
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/api/spaces"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/flags"
	"github.com/cloudfoundry/cli/plugin/models"
)

type SpaceUsers struct {
	ui          terminal.UI
	config      core_config.Reader
	spaceRepo   spaces.SpaceRepository
	userRepo    api.UserRepository
	orgReq      requirements.OrganizationRequirement
	pluginModel *[]plugin_models.GetSpaceUsers_Model
	pluginCall  bool
}

type userPrinter interface {
	printUsers()
}

type pluginPrinter struct {
	userPrinter
	usersMap               map[string]plugin_models.GetSpaceUsers_Model
	userLister             func(spaceGuid string, role string) ([]models.UserFields, error)
	spaceRoleToDisplayName map[string]string
	space                  models.Space
	pluginModel            *[]plugin_models.GetSpaceUsers_Model
}

type uiPrinter struct {
	userPrinter
	ui                     terminal.UI
	userLister             func(spaceGuid string, role string) ([]models.UserFields, error)
	spaceRoleToDisplayName map[string]string
	space                  models.Space
}

func init() {
	command_registry.Register(&SpaceUsers{})
}

func (cmd *SpaceUsers) MetaData() command_registry.CommandMetadata {
	return command_registry.CommandMetadata{
		Name:        "space-users",
		Description: T("Show space users by role"),
		Usage:       T("CF_NAME space-users ORG SPACE"),
	}
}

func (cmd *SpaceUsers) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) (reqs []requirements.Requirement, err error) {
	if len(fc.Args()) != 2 {
		cmd.ui.Failed(T("Incorrect Usage. Requires arguments\n\n") + command_registry.Commands.CommandUsage("space-users"))
	}

	cmd.orgReq = requirementsFactory.NewOrganizationRequirement(fc.Args()[0])

	reqs = []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
		cmd.orgReq,
	}
	return
}

func (cmd *SpaceUsers) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command {
	cmd.ui = deps.Ui
	cmd.config = deps.Config
	cmd.userRepo = deps.RepoLocator.GetUserRepository()
	cmd.spaceRepo = deps.RepoLocator.GetSpaceRepository()
	cmd.pluginCall = pluginCall
	cmd.pluginModel = deps.PluginModels.SpaceUsers

	return cmd
}

func (cmd *SpaceUsers) Execute(c flags.FlagContext) {
	spaceName := c.Args()[1]
	org := cmd.orgReq.GetOrganization()

	space, err := cmd.spaceRepo.FindByNameInOrg(spaceName, org.Guid)
	if err != nil {
		cmd.ui.Failed(err.Error())
	}

	cmd.ui.Say(T("Getting users in org {{.TargetOrg}} / space {{.TargetSpace}} as {{.CurrentUser}}",
		map[string]interface{}{
			"TargetOrg":   terminal.EntityNameColor(org.Name),
			"TargetSpace": terminal.EntityNameColor(space.Name),
			"CurrentUser": terminal.EntityNameColor(cmd.config.Username()),
		}))

	printer := cmd.getPrinter(space)
	printer.printUsers()
}

func (cmd *SpaceUsers) getPrinter(space models.Space) userPrinter {
	var spaceRoleToDisplayName = map[string]string{
		models.SPACE_MANAGER:   T("SPACE MANAGER"),
		models.SPACE_DEVELOPER: T("SPACE DEVELOPER"),
		models.SPACE_AUDITOR:   T("SPACE AUDITOR"),
	}

	if cmd.pluginCall {
		return &pluginPrinter{
			space:                  space,
			pluginModel:            cmd.pluginModel,
			usersMap:               make(map[string]plugin_models.GetSpaceUsers_Model),
			userLister:             cmd.getUserLister(),
			spaceRoleToDisplayName: spaceRoleToDisplayName,
		}
	}
	return &uiPrinter{
		ui:                     cmd.ui,
		space:                  space,
		userLister:             cmd.getUserLister(),
		spaceRoleToDisplayName: spaceRoleToDisplayName,
	}
}

func (cmd *SpaceUsers) getUserLister() func(spaceGuid string, role string) ([]models.UserFields, error) {
	if cmd.config.IsMinApiVersion("2.21.0") {
		return cmd.userRepo.ListUsersInSpaceForRoleWithNoUAA
	}
	return cmd.userRepo.ListUsersInSpaceForRole
}

func (p *pluginPrinter) printUsers() {
	for role, _ := range p.spaceRoleToDisplayName {
		users, _ := p.userLister(p.space.Guid, role)
		for _, user := range users {
			u, found := p.usersMap[user.Username]
			if found {
				u.Roles = append(u.Roles, role)
			} else {
				u = plugin_models.GetSpaceUsers_Model{}
				u.Username = user.Username
				u.Guid = user.Guid
				u.IsAdmin = user.IsAdmin
				u.Roles = make([]string, 1)
				u.Roles[0] = role
			}
			p.usersMap[user.Username] = u
		}
	}
	for _, v := range p.usersMap {
		*(p.pluginModel) = append(*(p.pluginModel), v)
	}
}

func (p *uiPrinter) printUsers() {
	for role, displayName := range p.spaceRoleToDisplayName {
		users, err := p.userLister(p.space.Guid, role)
		if err != nil {
			p.ui.Failed(T("Failed fetching space-users for role {{.SpaceRoleToDisplayName}}.\n{{.Error}}",
				map[string]interface{}{
					"Error":                  err.Error(),
					"SpaceRoleToDisplayName": displayName,
				}))
			return
		}
		p.ui.Say("")
		p.ui.Say("%s", terminal.HeaderColor(displayName))

		if len(users) == 0 {
			p.ui.Say("none")
		} else {
			for _, user := range users {
				p.ui.Say("  %s", user.Username)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"github.com/jpgneves/shorty/requests"
	"github.com/jpgneves/shorty/routers"
	"html/template"
	"net/http"
)

type ShortyResource struct{}

func (r ShortyResource) Get(request *requests.Request) *requests.Response {
	return &requests.Response{http.StatusTemporaryRedirect, "http://www.google.com"}
}

func (r ShortyResource) Post(request *requests.Request) *requests.Response {
	return &requests.Response{http.StatusOK, "shorty post"}
}

type SiteResource struct{}

func (r SiteResource) Get(request *requests.Request) *requests.Response {
	t, _ := template.ParseFiles("templates/index.tmpl")
	buf := new(bytes.Buffer)
	t.Execute(buf, request)
	return &requests.Response{http.StatusOK, buf.String()}
}

func (r SiteResource) Post(request *requests.Request) *requests.Response {
	return &requests.Response{http.StatusMethodNotAllowed, ""}
}

func main() {
	router := routers.NewMatchingRouter()
	router.AddRoute("/", new(SiteResource))
	router.AddRoute("/{id}", new(ShortyResource))
	rh := routers.MakeRoutingHandler(router)
	http.ListenAndServe(":55384", rh)
}
<commit_msg>Implement URL shortening and setup resources correctly<commit_after>package main

import (
	"bytes"
	"github.com/jpgneves/shorty/requests"
	"github.com/jpgneves/shorty/routers"
	"html/template"
	"net/http"
	"strconv"
)

type ShortyResource struct{
	cache map[string]string
	rev_cache map[string]string
	counter uint64
}

func (r *ShortyResource) Get(request *requests.Request) *requests.Response {
	if redirect, ok := r.cache[request.Params["id"]]; ok {
		return &requests.Response{http.StatusTemporaryRedirect, &redirect}
	}
	return &requests.Response{http.StatusNotFound, nil}
}

func (r *ShortyResource) Post(request *requests.Request) *requests.Response {
	if url, ok := request.Params["url"]; ok {
		if cached, ok := r.rev_cache[url]; ok {
			return &requests.Response{http.StatusOK, &cached}
		}
		short := r.shorten(url)
		r.rev_cache[url] = short
		r.cache[short] = url
		return &requests.Response{http.StatusOK, &short}
	}
	return &requests.Response{http.StatusBadRequest, nil}
}

func (r *ShortyResource) shorten(url string) string {
	r.counter += 1
	return strconv.FormatUint(r.counter, 36)
}

type SiteResource struct{}

func (r *SiteResource) Get(request *requests.Request) *requests.Response {
	t, _ := template.ParseFiles("templates/index.tmpl")
	buf := new(bytes.Buffer)
	t.Execute(buf, request)
	str := buf.String()
	return &requests.Response{http.StatusOK, &str}
}

func (r *SiteResource) Post(request *requests.Request) *requests.Response {
	return &requests.Response{http.StatusMethodNotAllowed, nil}
}

func main() {
	router := routers.NewMatchingRouter()
	router.AddRoute("/", new(SiteResource))
	shorty := &ShortyResource{make(map[string]string), make(map[string]string), 13370}
	router.AddRoute("/{id}", shorty)
	router.AddRoute("/create/{url}", shorty)
	rh := routers.MakeRoutingHandler(router)
	http.ListenAndServe(":55384", rh)
}
<|endoftext|>


<commit_before>// Package sigmon simplifies os.Signal handling.
package sigmon

import (
	"os"
	"os/signal"
	"sync"
	"syscall"
)

// Signal wraps the string type to reduce confusion when checking Sig.
type Signal string

// Signal constants are string representations of the handled os.Signals.
const (
	SIGHUP  Signal = "HUP"
	SIGINT  Signal = "INT"
	SIGTERM Signal = "TERM"
	SIGUSR1 Signal = "USR1"
	SIGUSR2 Signal = "USR2"
)

type signalJunction struct {
	sync.Mutex
	sighup  chan os.Signal
	sigint  chan os.Signal
	sigterm chan os.Signal
	sigusr1 chan os.Signal
	sigusr2 chan os.Signal
}

func (s *signalJunction) connect() {
	s.Lock()
	defer s.Unlock()

	s.sighup = make(chan os.Signal, 1)
	s.sigint = make(chan os.Signal, 1)
	s.sigterm = make(chan os.Signal, 1)
	s.sigusr1 = make(chan os.Signal, 1)
	s.sigusr2 = make(chan os.Signal, 1)

	signal.Notify(s.sighup, syscall.SIGHUP)
	signal.Notify(s.sigint, syscall.SIGINT)
	signal.Notify(s.sigterm, syscall.SIGTERM)
	notifyUSR(s.sigusr1, s.sigusr2)
}

func (s *signalJunction) disconnect() {
	defer s.cutSig(s.sighup)
	defer s.cutSig(s.sigint)
	defer s.cutSig(s.sigterm)
	defer s.cutSig(s.sigusr1)
	defer s.cutSig(s.sigusr2)
}

func (s *signalJunction) cutSig(c chan os.Signal) {
	defer close(c)
	defer signal.Stop(c)
}

type signalHandler struct {
	sync.Mutex
	handler  func(*SignalMonitor)
	registry chan func(*SignalMonitor)
}

func (s *signalHandler) register(handler func(*SignalMonitor)) {
	select {
	case <-s.registry:
	default:
	}

	s.registry <- handler
}

func (s *signalHandler) set(handler func(*SignalMonitor)) {
	s.Lock()
	defer s.Unlock()

	s.handler = handler
}

func (s *signalHandler) handle(sm *SignalMonitor) {
	s.Lock()
	defer s.Unlock()

	if s.handler != nil {
		s.handler(sm)
	}
}

// SignalMonitor helps manage signal handling.
type SignalMonitor struct {
	sync.Mutex
	sig Signal
	on  bool
	off chan struct{}

	junction *signalJunction
	handler  *signalHandler
}

// New takes a function and returns a SignalMonitor.  When a nil arg is
// provided, no action will be taken during signal handling.  Run must be
// called in order to begin monitoring.
func New(handler func(*SignalMonitor)) (s *SignalMonitor) {
	return &SignalMonitor{
		off:      make(chan struct{}, 1),
		junction: &signalJunction{},
		handler: &signalHandler{
			handler:  handler,
			registry: make(chan func(*SignalMonitor), 1),
		},
	}
}

// Set allows the handler function to be added or removed.  Only the most
// recently passed function will have any relevance.
func (s *SignalMonitor) Set(handler func(*SignalMonitor)) {
	s.handler.register(handler)
}

// Run starts signal monitoring.  If no function has been provided, no action
// will be taken during signal handling.  The os.Signal which was called will
// be stored as a typed string (Signal) within the SignalMonitor for retrieval
// using Sig. Stop should be called within the provided handler functions and
// is not a default behavior.
func (s *SignalMonitor) Run() {
	s.Lock()
	defer s.Unlock()

	if s.on {
		return
	}
	s.on = true

	wg := &sync.WaitGroup{}
	wg.Add(1)

	go s.process(wg)

	wg.Wait()
}

func (s *SignalMonitor) process(wg *sync.WaitGroup) {
	s.junction.connect()
	defer s.junction.disconnect()

	wg.Done()

	for {
		s.monitorWithPriority()
	}
}

func (s *SignalMonitor) monitorWithPriority() {
	select {
	case <-s.off:
		return
	case fn := <-s.handler.registry:
		s.handler.set(fn)
	default:
		s.monitorWithoutPriority()
	}
}

func (s *SignalMonitor) monitorWithoutPriority() {
	select {
	case <-s.off:
		return
	case fn := <-s.handler.registry:
		s.handler.set(fn)
	case <-s.junction.sighup:
		s.handle(SIGHUP)
	case <-s.junction.sigint:
		s.handle(SIGINT)
	case <-s.junction.sigterm:
		s.handle(SIGTERM)
	case <-s.junction.sigusr1:
		s.handle(SIGUSR1)
	case <-s.junction.sigusr2:
		s.handle(SIGUSR2)
	}
}

// Stop ends the goroutine which monitors signals.
func (s *SignalMonitor) Stop() {
	s.Lock()
	defer s.Unlock()

	if s.on {
		s.on = false
		s.off <- struct{}{}
	}
}

func (s *SignalMonitor) setSig(sig Signal) {
	s.Lock()
	defer s.Unlock()

	s.sig = sig
}

// Sig returns a typed string (Signal) representing the most recently called
// os.Signal.
func (s *SignalMonitor) Sig() Signal {
	s.Lock()
	defer s.Unlock()

	return s.sig
}

func (s *SignalMonitor) handle(sig Signal) {
	s.setSig(sig)
	s.handler.handle(s)
}
<commit_msg>Move system signal setup into new signalJunction func.<commit_after>// Package sigmon simplifies os.Signal handling.
package sigmon

import (
	"os"
	"os/signal"
	"sync"
	"syscall"
)

// Signal wraps the string type to reduce confusion when checking Sig.
type Signal string

// Signal constants are string representations of the handled os.Signals.
const (
	SIGHUP  Signal = "HUP"
	SIGINT  Signal = "INT"
	SIGTERM Signal = "TERM"
	SIGUSR1 Signal = "USR1"
	SIGUSR2 Signal = "USR2"
)

type signalJunction struct {
	sync.Mutex
	sighup  chan os.Signal
	sigint  chan os.Signal
	sigterm chan os.Signal
	sigusr1 chan os.Signal
	sigusr2 chan os.Signal
}

func newSignalJunction() *signalJunction {
	return &signalJunction{
		sighup:  make(chan os.Signal, 1),
		sigint:  make(chan os.Signal, 1),
		sigterm: make(chan os.Signal, 1),
		sigusr1: make(chan os.Signal, 1),
		sigusr2: make(chan os.Signal, 1),
	}
}

func (s *signalJunction) connect() {
	s.Lock()
	defer s.Unlock()

	signal.Notify(s.sighup, syscall.SIGHUP)
	signal.Notify(s.sigint, syscall.SIGINT)
	signal.Notify(s.sigterm, syscall.SIGTERM)
	notifyUSR(s.sigusr1, s.sigusr2)
}

func (s *signalJunction) disconnect() {
	defer signal.Stop(s.sighup)
	defer signal.Stop(s.sigint)
	defer signal.Stop(s.sigterm)
	defer signal.Stop(s.sigusr1)
	defer signal.Stop(s.sigusr2)
}

type signalHandler struct {
	sync.Mutex
	handler  func(*SignalMonitor)
	registry chan func(*SignalMonitor)
}

func (s *signalHandler) register(handler func(*SignalMonitor)) {
	select {
	case <-s.registry:
	default:
	}

	s.registry <- handler
}

func (s *signalHandler) set(handler func(*SignalMonitor)) {
	s.Lock()
	defer s.Unlock()

	s.handler = handler
}

func (s *signalHandler) handle(sm *SignalMonitor) {
	s.Lock()
	defer s.Unlock()

	if s.handler != nil {
		s.handler(sm)
	}
}

// SignalMonitor helps manage signal handling.
type SignalMonitor struct {
	sync.Mutex
	sig Signal
	on  bool
	off chan struct{}

	junction *signalJunction
	handler  *signalHandler
}

// New takes a function and returns a SignalMonitor.  When a nil arg is
// provided, no action will be taken during signal handling.  Run must be
// called in order to begin monitoring.
func New(handler func(*SignalMonitor)) (s *SignalMonitor) {
	return &SignalMonitor{
		off:      make(chan struct{}, 1),
		junction: newSignalJunction(),
		handler: &signalHandler{
			handler:  handler,
			registry: make(chan func(*SignalMonitor), 1),
		},
	}
}

// Set allows the handler function to be added or removed.  Only the most
// recently passed function will have any relevance.
func (s *SignalMonitor) Set(handler func(*SignalMonitor)) {
	s.handler.register(handler)
}

// Run starts signal monitoring.  If no function has been provided, no action
// will be taken during signal handling.  The os.Signal which was called will
// be stored as a typed string (Signal) within the SignalMonitor for retrieval
// using Sig. Stop should be called within the provided handler functions and
// is not a default behavior.
func (s *SignalMonitor) Run() {
	s.Lock()
	defer s.Unlock()

	if s.on {
		return
	}
	s.on = true

	wg := &sync.WaitGroup{}
	wg.Add(1)

	go s.process(wg)

	wg.Wait()
}

func (s *SignalMonitor) process(wg *sync.WaitGroup) {
	s.junction.connect()
	defer s.junction.disconnect()

	wg.Done()

	for {
		s.monitorWithPriority()
	}
}

func (s *SignalMonitor) monitorWithPriority() {
	select {
	case <-s.off:
		return
	case fn := <-s.handler.registry:
		s.handler.set(fn)
	default:
		s.monitorWithoutPriority()
	}
}

func (s *SignalMonitor) monitorWithoutPriority() {
	select {
	case <-s.off:
		return
	case fn := <-s.handler.registry:
		s.handler.set(fn)
	case <-s.junction.sighup:
		s.handle(SIGHUP)
	case <-s.junction.sigint:
		s.handle(SIGINT)
	case <-s.junction.sigterm:
		s.handle(SIGTERM)
	case <-s.junction.sigusr1:
		s.handle(SIGUSR1)
	case <-s.junction.sigusr2:
		s.handle(SIGUSR2)
	}
}

// Stop ends the goroutine which monitors signals.
func (s *SignalMonitor) Stop() {
	s.Lock()
	defer s.Unlock()

	if s.on {
		s.on = false
		s.off <- struct{}{}
	}
}

func (s *SignalMonitor) setSig(sig Signal) {
	s.Lock()
	defer s.Unlock()

	s.sig = sig
}

// Sig returns a typed string (Signal) representing the most recently called
// os.Signal.
func (s *SignalMonitor) Sig() Signal {
	s.Lock()
	defer s.Unlock()

	return s.sig
}

func (s *SignalMonitor) handle(sig Signal) {
	s.setSig(sig)
	s.handler.handle(s)
}
<|endoftext|>


<commit_before>package signals

import (
	"fmt"
	"math"
	"time"
)

// function types can represent an analogue y as it varies with x
type Function interface {
	property(x) y
}

// returns a PeriodicLimitedFunction (type Modulated) based on a sine wave,
// with peak y set to Maxy adjusted by dB,
// so dB should always be negative.
func NewTone(period x, dB float64) Modulated {
	return Modulated{Sine{period}, NewConstant(dB)}
}

// the x represents a value from -infinity to +infinity, but is actually limited by its current underlying representation.
// -ve x's are considered imaginary, not used, unless a Delay makes them +ve.
type x time.Duration // current underlying representation

func (i x) String() string {
	return fmt.Sprintf("%9.2f", float32(i)/float32(unitX))
}

// somewhere close to the middle of the resolution range.
const unitX = x(time.Second)

/*
// the y type represents a value between +maxY and -maxY.
type y int32

const maxY y = math.MaxInt32
const yBits = 32
const halfyBits = yBits / 2

//const Halfy=2<<(HalfyBits-1)

// float64 has less resolution than int64 at maxy, so need this to scale float64 sourced functions to never overflow int64
const maxyfloat64 float64 = float64(maxY - 64)   // 512
*/

// the y type represents a value between +maxY and -maxY.
type y int64

const unitY y = math.MaxInt64
const yBits = 64
const halfyBits = yBits / 2

//const Halfy=2<<(HalfyBits-1)

// float64 has less resolution than int64 at maxy, so need this to scale float64 sourced functions to never overflow int64
const maxyfloat64 float64 = float64(unitY - 512)

func (l y) String() string {
	return fmt.Sprintf("%7.2f%%", 100*float32(l)/float32(unitY))
}

// LimitedFunctions are used as Functions that can be assumed is zero after MaxX
type LimitedFunction interface {
	Function
	MaxX() x
}

// Samples are LimitedFunctions that are assumed to be zero before their MinX.
type Sample interface {
	LimitedFunction
	MinX() x
}

// PeriodicalFunctions are functions that can be assumed to repeat over Period().
type PeriodicFunction interface {
	Function
	Period() x
}

// LimitedPeriodicalFunction are Functions that repeat over Period() and dont exceed MaxY().
type PeriodicLimitedFunction interface {
	LimitedFunction
	Period() x
}

// Converters to promote slices of interfaces, needed when using variadic parameters called using a slice since go doesn't automatically promote a narrow interface inside the slice to be able to use a broader interface.
// for example: without these you couldn't use a slice of LimitedFunction's in a variadic call to a func requiring Function's. (when you can use separate LimitedFunction's in the same call.)

// converts  to []Function
func PromoteToFunctions(s interface{}) (out []Function) {
	switch st := s.(type) {
	case []LimitedFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	case []PeriodicLimitedFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	case []PeriodicFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	case []PCMFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	}
	return
}

// converts []LimitedFunction
func PromoteToLimitedFunctions(s interface{}) (out []LimitedFunction) {
	switch st := s.(type) {
	case []PeriodicLimitedFunction:
		out = make([]LimitedFunction, len(st))
		for i := range out {
			out[i] = st[i].(LimitedFunction)
		}
	case []PCMFunction:
		out = make([]LimitedFunction, len(st))
		for i := range out {
			out[i] = st[i].(LimitedFunction)
		}
	}
	return
}

// converts []PCMFunction to []PeriodicFunction
func PromoteToPeriodicFunctions(s interface{}) (out []PeriodicFunction) {
	switch st := s.(type) {
	case []PeriodicLimitedFunction:
		out = make([]PeriodicFunction, len(st))
		for i := range out {
			out[i] = st[i].(PeriodicFunction)
		}
	case []PCMFunction:
		out = make([]PeriodicFunction, len(st))
		for i := range out {
			out[i] = st[i].(PeriodicFunction)
		}
	}
	return
}

// converts []PCMFunction to []PeriodicLimitedFunction
func PromoteToPeriodicLimitedFunctions(s interface{}) (out []PeriodicLimitedFunction) {
	switch st := s.(type) {
	case []PCMFunction:
		out = make([]PeriodicLimitedFunction, len(st))
		for i := range out {
			out[i] = st[i].(PeriodicLimitedFunction)
		}
	}
	return
}

<commit_msg>comments<commit_after>package signals

import (
	"fmt"
	"math"
	"time"
)

// function types can represent an analogue y as it varies with x
type Function interface {
	property(x) y
}

// returns a PeriodicLimitedFunction (type Modulated) based on a sine wave,
// with peak y set to Maxy adjusted by dB,
// so dB should always be negative.
func NewTone(period x, dB float64) Modulated {
	return Modulated{Sine{period}, NewConstant(dB)}
}

// the x represents a value from -infinity to +infinity, but is actually limited by its current underlying representation.
// -ve x's are considered imaginary, not used, unless a Delay makes them +ve.
type x time.Duration // current underlying representation

func (i x) String() string {
	return fmt.Sprintf("%9.2f", float32(i)/float32(unitX))
}

// somewhere close to the middle of the resolution range.
const unitX = x(time.Second)

/*
// the y type represents a value between +maxY and -maxY.
type y int32

const maxY y = math.MaxInt32
const yBits = 32
const halfyBits = yBits / 2

//const Halfy=2<<(HalfyBits-1)

// float64 has less resolution than int64 at maxy, so need this to scale float64 sourced functions to never overflow int64
const maxyfloat64 float64 = float64(maxY - 64)   // 512
*/

// the y type represents a value between +maxY and -maxY.
type y int64

const unitY y = math.MaxInt64
const yBits = 64
const halfyBits = yBits / 2

//const Halfy=2<<(HalfyBits-1)

// float64 has less resolution than int64 at maxy, so need this to scale float64 sourced functions to never overflow int64
const maxyfloat64 float64 = float64(unitY - 512)

func (l y) String() string {
	return fmt.Sprintf("%7.2f%%", 100*float32(l)/float32(unitY))
}

// LimitedFunctions are used as Functions that can be assumed is zero after MaxX
type LimitedFunction interface {
	Function
	MaxX() x
}

// Samples are LimitedFunctions that are assumed to be zero before their MinX.
type Sample interface {
	LimitedFunction
	MinX() x
}

// PeriodicalFunctions are functions that can be assumed to repeat over Period().
type PeriodicFunction interface {
	Function
	Period() x
}

// LimitedPeriodicalFunction are Functions that repeat over Period() and dont exceed MaxY().
type PeriodicLimitedFunction interface {
	LimitedFunction
	Period() x
}

// Converters to promote slices of interfaces, needed when using variadic parameters called using a slice since go doesn't automatically promote a narrow interface inside the slice to be able to use a broader interface.
// for example: without these you couldn't use a slice of LimitedFunction's in a variadic call to a func requiring Function's. (when you can use separate LimitedFunction's in the same call.)

// converts  to []Function
func PromoteToFunctions(s interface{}) (out []Function) {
	switch st := s.(type) {
	case []LimitedFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	case []PeriodicLimitedFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	case []PeriodicFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	case []PCMFunction:
		out = make([]Function, len(st))
		for i := range out {
			out[i] = st[i].(Function)
		}
	}
	return
}

// converts to []LimitedFunction
func PromoteToLimitedFunctions(s interface{}) (out []LimitedFunction) {
	switch st := s.(type) {
	case []PeriodicLimitedFunction:
		out = make([]LimitedFunction, len(st))
		for i := range out {
			out[i] = st[i].(LimitedFunction)
		}
	case []PCMFunction:
		out = make([]LimitedFunction, len(st))
		for i := range out {
			out[i] = st[i].(LimitedFunction)
		}
	}
	return
}

// converts to []PeriodicFunction
func PromoteToPeriodicFunctions(s interface{}) (out []PeriodicFunction) {
	switch st := s.(type) {
	case []PeriodicLimitedFunction:
		out = make([]PeriodicFunction, len(st))
		for i := range out {
			out[i] = st[i].(PeriodicFunction)
		}
	case []PCMFunction:
		out = make([]PeriodicFunction, len(st))
		for i := range out {
			out[i] = st[i].(PeriodicFunction)
		}
	}
	return
}

// converts to []PeriodicLimitedFunction
func PromoteToPeriodicLimitedFunctions(s interface{}) (out []PeriodicLimitedFunction) {
	switch st := s.(type) {
	case []PCMFunction:
		out = make([]PeriodicLimitedFunction, len(st))
		for i := range out {
			out[i] = st[i].(PeriodicLimitedFunction)
		}
	}
	return
}

<|endoftext|>