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

package main

import (
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/square/keywhiz-fs"
	klog "github.com/square/keywhiz-fs/log"
	"golang.org/x/sys/unix"
)

var (
	certFile       = flag.String("cert", "", "PEM-encoded certificate file")
	keyFile        = flag.String("key", "client.key", "PEM-encoded private key file")
	caFile         = flag.String("ca", "cacert.crt", "PEM-encoded CA certificates file")
	user           = flag.String("asuser", "keywhiz", "Default user to own files")
	group          = flag.String("group", "keywhiz", "Default group to own files")
	ping           = flag.Bool("ping", false, "Enable startup ping to server")
	debug          = flag.Bool("debug", false, "Enable debugging output")
	timeoutSeconds = flag.Uint("timeout", 20, "Timeout for communication with server")
	logger         *klog.Logger
)

func main() {
	var Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] url mountpoint\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	if flag.NArg() != 2 {
		Usage()
		os.Exit(1)
	}

	serverURL, err := url.Parse(flag.Args()[0])
	if err != nil {
		log.Fatalf("Invalid url: %s\n", err)
		os.Exit(1)
	}
	mountpoint := flag.Args()[1]

	logConfig := klog.Config{*debug, mountpoint}
	logger = klog.New("kwfs_main", logConfig)
	defer logger.Close()

	if *certFile == "" {
		logger.Debugf("Certificate file not specified, assuming certificate also in %s", *keyFile)
		certFile = keyFile
	}

	lockMemory()

	clientTimeout := time.Duration(*timeoutSeconds) * time.Second
	freshThreshold := 200 * time.Millisecond
	backendDeadline := 500 * time.Millisecond
	maxWait := clientTimeout + backendDeadline
	timeouts := keywhizfs.Timeouts{freshThreshold, backendDeadline, maxWait}

	client := keywhizfs.NewClient(*certFile, *keyFile, *caFile, serverURL, clientTimeout, logConfig, *ping)

	ownership := keywhizfs.NewOwnership(*user, *group)
	kwfs, root, err := keywhizfs.NewKeywhizFs(&client, ownership, timeouts, logConfig)
	if err != nil {
		log.Fatalf("KeywhizFs init fail: %v\n", err)
	}

	mountOptions := &fuse.MountOptions{
		AllowOther: true,
		Name:       kwfs.String(),
		Options:    []string{"default_permissions"},
	}

	// Empty Options struct avoids setting a global uid/gid override.
	conn := nodefs.NewFileSystemConnector(root, &nodefs.Options{})
	server, err := fuse.NewServer(conn.RawFS(), mountpoint, mountOptions)
	if err != nil {
		log.Fatalf("Mount fail: %v\n", err)
	}

	server.Serve()
}

// Locks memory, preventing memory from being written to disk as swap
func lockMemory() {
	err := unix.Mlockall(unix.MCL_FUTURE | unix.MCL_CURRENT)
	switch err {
	case nil:
	case unix.ENOSYS:
		logger.Warnf("mlockall() not implemented on this system")
	case unix.ENOMEM:
		logger.Warnf("mlockall() failed with ENOMEM")
	default:
		log.Fatalf("Could not perform mlockall and prevent swapping memory: %v", err)
	}
}
<commit_msg>Handle signals and exit cleanly.<commit_after>// Copyright 2015 Square Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/square/keywhiz-fs"
	klog "github.com/square/keywhiz-fs/log"
	"golang.org/x/sys/unix"
)

var (
	certFile       = flag.String("cert", "", "PEM-encoded certificate file")
	keyFile        = flag.String("key", "client.key", "PEM-encoded private key file")
	caFile         = flag.String("ca", "cacert.crt", "PEM-encoded CA certificates file")
	user           = flag.String("asuser", "keywhiz", "Default user to own files")
	group          = flag.String("group", "keywhiz", "Default group to own files")
	ping           = flag.Bool("ping", false, "Enable startup ping to server")
	debug          = flag.Bool("debug", false, "Enable debugging output")
	timeoutSeconds = flag.Uint("timeout", 20, "Timeout for communication with server")
	logger         *klog.Logger
)

func main() {
	var Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] url mountpoint\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	if flag.NArg() != 2 {
		Usage()
		os.Exit(1)
	}

	serverURL, err := url.Parse(flag.Args()[0])
	if err != nil {
		log.Fatalf("Invalid url: %s\n", err)
		os.Exit(1)
	}
	mountpoint := flag.Args()[1]

	logConfig := klog.Config{*debug, mountpoint}
	logger = klog.New("kwfs_main", logConfig)
	defer logger.Close()

	if *certFile == "" {
		logger.Debugf("Certificate file not specified, assuming certificate also in %s", *keyFile)
		certFile = keyFile
	}

	lockMemory()

	clientTimeout := time.Duration(*timeoutSeconds) * time.Second
	freshThreshold := 200 * time.Millisecond
	backendDeadline := 500 * time.Millisecond
	maxWait := clientTimeout + backendDeadline
	timeouts := keywhizfs.Timeouts{freshThreshold, backendDeadline, maxWait}

	client := keywhizfs.NewClient(*certFile, *keyFile, *caFile, serverURL, clientTimeout, logConfig, *ping)

	ownership := keywhizfs.NewOwnership(*user, *group)
	kwfs, root, err := keywhizfs.NewKeywhizFs(&client, ownership, timeouts, logConfig)
	if err != nil {
		log.Fatalf("KeywhizFs init fail: %v\n", err)
	}

	mountOptions := &fuse.MountOptions{
		AllowOther: true,
		Name:       kwfs.String(),
		Options:    []string{"default_permissions"},
	}

	// Empty Options struct avoids setting a global uid/gid override.
	conn := nodefs.NewFileSystemConnector(root, &nodefs.Options{})
	server, err := fuse.NewServer(conn.RawFS(), mountpoint, mountOptions)
	if err != nil {
		log.Fatalf("Mount fail: %v\n", err)
	}

	// Catch SIGINT and SIGKILL and exit cleanly.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		sig := <-c
		logger.Warnf("Got signal %s, unmounting", sig)
		server.Unmount()
	}()

	server.Serve()
}

// Locks memory, preventing memory from being written to disk as swap
func lockMemory() {
	err := unix.Mlockall(unix.MCL_FUTURE | unix.MCL_CURRENT)
	switch err {
	case nil:
	case unix.ENOSYS:
		logger.Warnf("mlockall() not implemented on this system")
	case unix.ENOMEM:
		logger.Warnf("mlockall() failed with ENOMEM")
	default:
		log.Fatalf("Could not perform mlockall and prevent swapping memory: %v", err)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"gopkg.in/mgo.v2/bson"
)

type Group struct {
	Id                             bson.ObjectId            `bson:"_id" json:"-"`
	Body                           string                   `bson:"body" json:"body"`
	Title                          string                   `bson:"title" json:"title"`
	Slug                           string                   `bson:"slug" json:"slug"`
	Privacy                        string                   `bson:"privacy" json:"privacy"`
	Visibility                     string                   `bson:"visibility" json:"visibility"`
	SocialApiChannelId             string                   `bson:"socialApiChannelId" json:"socialApiChannelId"`
	SocialApiAnnouncementChannelId string                   `bson:"socialApiAnnouncementChannelId" json:"socialApiAnnouncementChannelId"`
	SocialApiDefaultChannelId      string                   `bson:"socialApiDefaultChannelId" json:"socialApiDefaultChannelId"`
	Parent                         []map[string]interface{} `bson:"parent" json:"parent"`
	Customize                      map[string]interface{}   `bson:"customize" json:"customize"`
	Counts                         map[string]interface{}   `bson:"counts" json:"counts"`
	Migration                      string                   `bson:"migration,omitempty" json:"migration"`
	StackTemplate                  []string                 `bson:"stackTemplates,omitempty" json:"stackTemplates"`
	// DefaultChannels holds the default channels for a group, when a user joins
	// to this group, participants will be automatically added to regarding
	// channels
	DefaultChannels []string `bson:"defaultChannels,omitempty" json:"defaultChannels"`
}
<commit_msg>go/koding: add new parameters to group struct for payment<commit_after>package models

import (
	"gopkg.in/mgo.v2/bson"
)

type Group struct {
	Id                             bson.ObjectId            `bson:"_id" json:"-"`
	Body                           string                   `bson:"body" json:"body"`
	Title                          string                   `bson:"title" json:"title"`
	Slug                           string                   `bson:"slug" json:"slug"`
	Privacy                        string                   `bson:"privacy" json:"privacy"`
	Visibility                     string                   `bson:"visibility" json:"visibility"`
	SocialApiChannelId             string                   `bson:"socialApiChannelId" json:"socialApiChannelId"`
	SocialApiAnnouncementChannelId string                   `bson:"socialApiAnnouncementChannelId" json:"socialApiAnnouncementChannelId"`
	SocialApiDefaultChannelId      string                   `bson:"socialApiDefaultChannelId" json:"socialApiDefaultChannelId"`
	Parent                         []map[string]interface{} `bson:"parent" json:"parent"`
	Customize                      map[string]interface{}   `bson:"customize" json:"customize"`
	Counts                         map[string]interface{}   `bson:"counts" json:"counts"`
	Migration                      string                   `bson:"migration,omitempty" json:"migration"`
	StackTemplate                  []string                 `bson:"stackTemplates,omitempty" json:"stackTemplates"`
	// DefaultChannels holds the default channels for a group, when a user joins
	// to this group, participants will be automatically added to regarding
	// channels
	DefaultChannels []string `bson:"defaultChannels,omitempty" json:"defaultChannels"`
	Payment         Payment  `bson:"payment" json:"payment"`
}

type Payment struct {
	Subscription Subscription
	Customer     Customer
}

type Subscription struct {
	// Allowed values are "trialing", "active", "past_due", "canceled", "unpaid".
	State string `bson:"state" json:"state"`
	ID    string `bson:"id" json:"id"`
}

type Customer struct {
	ID string `bson:"id" json:"id"`
}
<|endoftext|>


<commit_before>/*
Problem: We want to send a message and receive a reply

Solution: Let's write a simple "Hello World" client and server.
We'll write them as go tests, so that we can verify that they
work.  To run this test, you can run "go test -run SimpleHelloWorld"
in the same directory as this file.
*/

package main

import (
	"testing"

	"github.com/zeromq/goczmq"
)

// SimpleHelloWorldClient does the following:
// * Creates a ZMQ_REQ socket
// * Sends a "Hello" message
// * Waits for a "World" reply
func SimpleHelloWorldClient(t *testing.T) {
	t.Logf("client starting...")

	// Create a ZMQ_REQ client using a "smart constructor".
	// See: https://godoc.org/github.com/zeromq/goczmq#NewReq

	client, err := goczmq.NewReq("tcp://localhost:5555")
	if err != nil {
		t.Fatalf("client.NewReq error: %s", err)
	}

	// Here, we make sure the socket is destroyed
	// when this function exits. While Go is a garbage
	// collected language, we're binding a C library,
	// so we need to make sure to clean up.

	defer client.Destroy()

	// Let's create a request message. GoCZMQ uses slices
	// of byte slices for messages, because they map
	// very simply to ZeroMQ "frames".

	request := [][]byte{[]byte("Hello")}

	// Send the message and check for any errors.

	err = client.SendMessage(request)
	if err != nil {
		t.Fatalf("client.SendMessage error: %s", err)
	}

	t.Logf("client.SendMessage '%s'", request)

	// Receive the reply message from the server. Note that
	// this RecvMessage() call will block forever waiting
	// for a message.

	reply, err := client.RecvMessage()
	if err != nil {
		t.Fatalf("client.RecvMessage error: %s", err)
	}

	t.Logf("client.RecvMessage: '%s'", reply)
}

// SimpleHelloWorldServer does the following:
// * Creates a ZMQ_REP socket
// * Waits for a "Hello" request
// * Sends a "World" reply
func SimpleHelloWorldServer(t *testing.T) {
	t.Logf("server starting...")

	// Create a ZMQ_REP client using a "smart constructor".
	// See: https://godoc.org/github.com/zeromq/goczmq#NewRep

	server, err := goczmq.NewRep("tcp://*:5555")
	if err != nil {
		t.Fatalf("server.NewRep error: %s", err)
	}

	// Here, we make sure the socket is destroyed
	// when this function exits. While Go is a garbage
	// collected language, we're binding a C library,
	// so we need to make sure to clean up.

	defer server.Destroy()

	// Let's wait for a message from a client. Note that
	// this RecvMessage call will block forever waiting.

	request, err := server.RecvMessage()
	if err != nil {
		t.Fatalf("server.RecvMessage error: %s", err)
	}

	t.Logf("server.RecvMessage: '%s'", request)

	// Here we create a reply message. GoCZMQ uses slices
	// of byte slices for messages, because they map
	// very simply to ZeroMQ "frames".

	reply := [][]byte{[]byte("World")}

	// Send the message and check for any errors.

	err = server.SendMessage(reply)
	if err != nil {
		t.Fatalf("server.SendMessage error: %s", err)
	}

	t.Logf("server.SendMessage: '%s'", reply)
}

// TestSimpleHelloWorld starts SimpleHelloWorldServer in
// a goroutine, then starts a SimpleHelloWorldClient
func TestSimpleHelloWorld(t *testing.T) {
	go SimpleHelloWorldServer(t)
	SimpleHelloWorldClient(t)
}
<commit_msg>Problem: incorrect wording in comment in goczmq hello world example<commit_after>/*
Problem: We want to send a message and receive a reply

Solution: Let's write a simple "Hello World" client and server.
We'll write them as go tests, so that we can verify that they
work.  To run this test, you can run "go test -run SimpleHelloWorld"
in the same directory as this file.
*/

package main

import (
	"testing"

	"github.com/zeromq/goczmq"
)

// SimpleHelloWorldClient does the following:
// * Creates a ZMQ_REQ socket
// * Sends a "Hello" message
// * Waits for a "World" reply
func SimpleHelloWorldClient(t *testing.T) {
	t.Logf("client starting...")

	// Create a ZMQ_REQ client using a "smart constructor".
	// See: https://godoc.org/github.com/zeromq/goczmq#NewReq

	client, err := goczmq.NewReq("tcp://localhost:5555")
	if err != nil {
		t.Fatalf("client.NewReq error: %s", err)
	}

	// Here, we make sure the socket is destroyed
	// when this function exits. While Go is a garbage
	// collected language, we're binding a C library,
	// so we need to make sure to clean up.

	defer client.Destroy()

	// Let's create a request message. GoCZMQ uses slices
	// of byte slices for messages, because they map
	// very simply to ZeroMQ "frames".

	request := [][]byte{[]byte("Hello")}

	// Send the message and check for any errors.

	err = client.SendMessage(request)
	if err != nil {
		t.Fatalf("client.SendMessage error: %s", err)
	}

	t.Logf("client.SendMessage '%s'", request)

	// Receive the reply message from the server. Note that
	// this RecvMessage() call will block forever waiting
	// for a message.

	reply, err := client.RecvMessage()
	if err != nil {
		t.Fatalf("client.RecvMessage error: %s", err)
	}

	t.Logf("client.RecvMessage: '%s'", reply)
}

// SimpleHelloWorldServer does the following:
// * Creates a ZMQ_REP socket
// * Waits for a "Hello" request
// * Sends a "World" reply
func SimpleHelloWorldServer(t *testing.T) {
	t.Logf("server starting...")

	// Create a ZMQ_REP server using a "smart constructor".
	// See: https://godoc.org/github.com/zeromq/goczmq#NewRep

	server, err := goczmq.NewRep("tcp://*:5555")
	if err != nil {
		t.Fatalf("server.NewRep error: %s", err)
	}

	// Here, we make sure the socket is destroyed
	// when this function exits. While Go is a garbage
	// collected language, we're binding a C library,
	// so we need to make sure to clean up.

	defer server.Destroy()

	// Let's wait for a message from a client. Note that
	// this RecvMessage call will block forever waiting.

	request, err := server.RecvMessage()
	if err != nil {
		t.Fatalf("server.RecvMessage error: %s", err)
	}

	t.Logf("server.RecvMessage: '%s'", request)

	// Here we create a reply message. GoCZMQ uses slices
	// of byte slices for messages, because they map
	// very simply to ZeroMQ "frames".

	reply := [][]byte{[]byte("World")}

	// Send the message and check for any errors.

	err = server.SendMessage(reply)
	if err != nil {
		t.Fatalf("server.SendMessage error: %s", err)
	}

	t.Logf("server.SendMessage: '%s'", reply)
}

// TestSimpleHelloWorld starts SimpleHelloWorldServer in
// a goroutine, then starts a SimpleHelloWorldClient
func TestSimpleHelloWorld(t *testing.T) {
	go SimpleHelloWorldServer(t)
	SimpleHelloWorldClient(t)
}
<|endoftext|>


<commit_before>package dnssd

import (
	"fmt"
	"strings"
	"testing"
)

type genericop interface {
	Start() error
	Active() bool
	Stop()
}

func StartStopHelper(t *testing.T, op genericop) {
	if err := op.Start(); err != nil {
		t.Fatalf("Couldn't start op: %v", err)
	}
	if !op.Active() {
		t.Fatal("Op didn't become active")
	}
	op.Stop()
	if op.Active() {
		t.Fatal("Op didn't become inactive")
	}
}

func TestRegisterStartStop(t *testing.T) {
	f := func(op *RegisterOp, e error, add bool, name, serviceType, domain string) {
	}
	StartStopHelper(t, NewRegisterOp("go", "_go-dnssd._tcp", 9, f))
}

func TestRegTxt(t *testing.T) {
	op := &RegisterOp{}
	key, value := "a", "a"
	if err := op.SetTXTPair(key, value); err != nil {
		t.Fatalf(`Unexpected error setting key "%s", value "%s": %v`, key, value, err)
	}
	if l := 2 + len(key) + len(value); op.txt.l != l {
		t.Fatalf(`Expected length %s after setting key "%s", value "%s", got: %v`, l, key, value, op.txt.l)
	}
	if err := op.DeleteTXTPair(key); err != nil {
		t.Fatalf(`Unexpected error deleting key "%s": %v`, key, err)
	}
	if op.txt.l != 0 {
		t.Fatalf(`Expected length 0 after deleting key "%s, got: %v`, key, op.txt.l)
	}
	key = strings.Repeat("a", 128)
	value = key
	if err := op.SetTXTPair(key, value); err != ErrTXTStringLen {
		t.Fatalf("Expected ErrTXTStringLen, got: %v", err)
	}
	key = strings.Repeat("a", 126)
	value = key
	ssize := 2 + len(key) + len(value)
	for i := 0; i < (65535 / ssize); i++ {
		key := fmt.Sprintf("%0126d", i)
		if err := op.SetTXTPair(key, value); err != nil {
			t.Fatalf("Unexpected error setting up for ErrTXTLen: %v", err)
		}
	}
	if err := op.SetTXTPair(key, value); err != ErrTXTLen {
		t.Fatalf("Expected ErrTXTLen, got: %v", err)
	}
	for i := 0; i < (65535 / ssize); i++ {
		key := fmt.Sprintf("%0126d", i)
		if err := op.DeleteTXTPair(key); err != nil {
			t.Fatalf("Unexpected error tearing down from ErrTXTLen test: %v", err)
		}
	}
	if op.txt.l != 0 {
		t.Fatalf("Expected length 0 after tearing down from ErrTXTLen test, got: %v", op.txt.l)
	}
}

func TestBrowseStartStop(t *testing.T) {
	f := func(op *BrowseOp, e error, add bool, interfaceIndex int, name string, serviceType string, domain string) {
	}
	StartStopHelper(t, NewBrowseOp("_go-dnssd._tcp", f))
}

func TestResolveStartStop(t *testing.T) {
	f := func(op *ResolveOp, e error, host string, port int, txt map[string]string) {
	}
	StartStopHelper(t, NewResolveOp(0, "go", "_go-dnssd._tcp", "local", f))
}

func TestDecodeTxtBadLength(t *testing.T) {
	b := []byte{255, 'b', '=', 'b'}
	m := decodeTxt(b)
	if v, p := m["b"]; p != false {
		t.Fatalf(`Expected pair "b" to be missing, instead it's present with value %v`, v)
	}
}

func TestDecodeTxtKeyNoValue(t *testing.T) {
	b := []byte{1, 'a', 2, 'b', '=', 1, '=', 2, '=', 'a'}
	m := decodeTxt(b)
	keys := []string{"a", "b", "=", "=a"}
	for _, k := range keys {
		if v, p := m[k]; v != "" {
			t.Fatalf(`Expected "%s" to return empty string, got %v instead (present: %v)`, k, v, p)
		}
	}
}

func TestDecodeTxtKeyValue(t *testing.T) {
	b := []byte{3, 'a', '=', 'a', 3, 'b', '=', 'b', 5, 'a', 'b', '=', 'a', 'b'}
	m := decodeTxt(b)
	for _, kv := range []string{"a", "b", "ab"} {
		if v, p := m[kv]; v != kv {
			t.Fatalf(`Expected "%s" to return "%s", got %v instead (present: %v)`, kv, kv, v, p)
		}
	}
}

func TestQueryStartStop(t *testing.T) {
	f := func(op *QueryOp, err error, add bool, interfaceIndex int, fullname string, rrtype, rrclass uint16, rdata []byte, ttl uint32) {
	}
	StartStopHelper(t, NewQueryOp(0, "golang.org.", 1, 1, f))
}
<commit_msg>Fix printf verb in TestRegTxt<commit_after>package dnssd

import (
	"fmt"
	"strings"
	"testing"
)

type genericop interface {
	Start() error
	Active() bool
	Stop()
}

func StartStopHelper(t *testing.T, op genericop) {
	if err := op.Start(); err != nil {
		t.Fatalf("Couldn't start op: %v", err)
	}
	if !op.Active() {
		t.Fatal("Op didn't become active")
	}
	op.Stop()
	if op.Active() {
		t.Fatal("Op didn't become inactive")
	}
}

func TestRegisterStartStop(t *testing.T) {
	f := func(op *RegisterOp, e error, add bool, name, serviceType, domain string) {
	}
	StartStopHelper(t, NewRegisterOp("go", "_go-dnssd._tcp", 9, f))
}

func TestRegTxt(t *testing.T) {
	op := &RegisterOp{}
	key, value := "a", "a"
	if err := op.SetTXTPair(key, value); err != nil {
		t.Fatalf(`Unexpected error setting key "%s", value "%s": %v`, key, value, err)
	}
	if l := 2 + len(key) + len(value); op.txt.l != l {
		t.Fatalf(`Expected length %d after setting key "%s", value "%s", got: %d`, l, key, value, op.txt.l)
	}
	if err := op.DeleteTXTPair(key); err != nil {
		t.Fatalf(`Unexpected error deleting key "%s": %v`, key, err)
	}
	if op.txt.l != 0 {
		t.Fatalf(`Expected length 0 after deleting key "%s, got: %v`, key, op.txt.l)
	}
	key = strings.Repeat("a", 128)
	value = key
	if err := op.SetTXTPair(key, value); err != ErrTXTStringLen {
		t.Fatalf("Expected ErrTXTStringLen, got: %v", err)
	}
	key = strings.Repeat("a", 126)
	value = key
	ssize := 2 + len(key) + len(value)
	for i := 0; i < (65535 / ssize); i++ {
		key := fmt.Sprintf("%0126d", i)
		if err := op.SetTXTPair(key, value); err != nil {
			t.Fatalf("Unexpected error setting up for ErrTXTLen: %v", err)
		}
	}
	if err := op.SetTXTPair(key, value); err != ErrTXTLen {
		t.Fatalf("Expected ErrTXTLen, got: %v", err)
	}
	for i := 0; i < (65535 / ssize); i++ {
		key := fmt.Sprintf("%0126d", i)
		if err := op.DeleteTXTPair(key); err != nil {
			t.Fatalf("Unexpected error tearing down from ErrTXTLen test: %v", err)
		}
	}
	if op.txt.l != 0 {
		t.Fatalf("Expected length 0 after tearing down from ErrTXTLen test, got: %v", op.txt.l)
	}
}

func TestBrowseStartStop(t *testing.T) {
	f := func(op *BrowseOp, e error, add bool, interfaceIndex int, name string, serviceType string, domain string) {
	}
	StartStopHelper(t, NewBrowseOp("_go-dnssd._tcp", f))
}

func TestResolveStartStop(t *testing.T) {
	f := func(op *ResolveOp, e error, host string, port int, txt map[string]string) {
	}
	StartStopHelper(t, NewResolveOp(0, "go", "_go-dnssd._tcp", "local", f))
}

func TestDecodeTxtBadLength(t *testing.T) {
	b := []byte{255, 'b', '=', 'b'}
	m := decodeTxt(b)
	if v, p := m["b"]; p != false {
		t.Fatalf(`Expected pair "b" to be missing, instead it's present with value %v`, v)
	}
}

func TestDecodeTxtKeyNoValue(t *testing.T) {
	b := []byte{1, 'a', 2, 'b', '=', 1, '=', 2, '=', 'a'}
	m := decodeTxt(b)
	keys := []string{"a", "b", "=", "=a"}
	for _, k := range keys {
		if v, p := m[k]; v != "" {
			t.Fatalf(`Expected "%s" to return empty string, got %v instead (present: %v)`, k, v, p)
		}
	}
}

func TestDecodeTxtKeyValue(t *testing.T) {
	b := []byte{3, 'a', '=', 'a', 3, 'b', '=', 'b', 5, 'a', 'b', '=', 'a', 'b'}
	m := decodeTxt(b)
	for _, kv := range []string{"a", "b", "ab"} {
		if v, p := m[kv]; v != kv {
			t.Fatalf(`Expected "%s" to return "%s", got %v instead (present: %v)`, kv, kv, v, p)
		}
	}
}

func TestQueryStartStop(t *testing.T) {
	f := func(op *QueryOp, err error, add bool, interfaceIndex int, fullname string, rrtype, rrclass uint16, rdata []byte, ttl uint32) {
	}
	StartStopHelper(t, NewQueryOp(0, "golang.org.", 1, 1, f))
}
<|endoftext|>


<commit_before>package garagepi

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"
	"github.com/robdimsdale/garage-pi/logger"
	"strings"
)

type Executor struct {
	l                   logger.Logger
	webcamHost          string
	webcamPort          string
	rtr                 *mux.Router
	osHelper            OsHelper
	staticFilesystem    http.FileSystem
	templatesFilesystem http.FileSystem
}

type OsHelper interface {
	Exec(executable string, arg ...string) (string, error)
}

func NewExecutor(
	l logger.Logger,
	helper OsHelper,
	staticFilesystem http.FileSystem,
	templatesFilesystem http.FileSystem,
	webcamHost string,
	webcamPort string) *Executor {

	return &Executor{
		l:                   l,
		webcamHost:          webcamHost,
		webcamPort:          webcamPort,
		rtr:                 mux.NewRouter(),
		osHelper:            helper,
		staticFilesystem:    staticFilesystem,
		templatesFilesystem: templatesFilesystem,
	}
}

func (e *Executor) homepageHandler(w http.ResponseWriter, r *http.Request) {
	e.l.Log("homepage")
	buf := bytes.NewBuffer(nil)
	f, err := e.templatesFilesystem.Open("homepage.html")
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(buf, f)
	if err != nil {
		panic(err)
	}
	f.Close()
	w.Write(buf.Bytes())
}

func (e *Executor) webcamHandler(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get("http://" + e.webcamHost + ":" + e.webcamPort + "/?action=snapshot&n=" + r.Form.Get("n"))
	if err != nil {
		e.l.Log("Error getting image: " + err.Error())
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		e.l.Log("Error closing image request: " + err.Error())
	}
	w.Write(body)
}

func (e *Executor) toggleDoorHandler(w http.ResponseWriter, r *http.Request) {
	e.executeCommand("gpio", "write", "0", "1")
	sleepDuration, _ := time.ParseDuration("500ms")
	e.l.Log("sleeping for " + sleepDuration.String())
	time.Sleep(sleepDuration)
	e.executeCommand("gpio", "write", "0", "0")

	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (e *Executor) executeCommand(executable string, arg ...string) string {
	logStatement := append([]string{executable}, arg...)
	e.l.Log("executing: '" + strings.Join(logStatement, " ") + "'")
	out, err := e.osHelper.Exec(executable, arg...)
	if err != nil {
		e.l.Log("ERROR: " + err.Error())
	}
	return out
}

func (e *Executor) startCameraHandler(w http.ResponseWriter, r *http.Request) {
	e.executeCommand("/etc/init.d/garagestreamer", "start")
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (e *Executor) stopCameraHandler(w http.ResponseWriter, r *http.Request) {
	e.executeCommand("/etc/init.d/garagestreamer", "stop")
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (e *Executor) ServeForever(port string) {
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(e.staticFilesystem)))

	e.rtr.HandleFunc("/", e.homepageHandler).Methods("GET")
	e.rtr.HandleFunc("/webcam", e.webcamHandler).Methods("GET")
	e.rtr.HandleFunc("/toggle", e.toggleDoorHandler).Methods("POST")
	e.rtr.HandleFunc("/start-camera", e.startCameraHandler).Methods("POST")
	e.rtr.HandleFunc("/stop-camera", e.stopCameraHandler).Methods("POST")

	http.Handle("/", e.rtr)
	e.l.Log("Listening on port " + port + "...")
	http.ListenAndServe(":"+port, nil)
}

func (e *Executor) Kill() {
	os.Exit(0)
}
<commit_msg>Gracefully handle no response from webcam url.<commit_after>package garagepi

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"
	"github.com/robdimsdale/garage-pi/logger"
	"strings"
)

type Executor struct {
	l                   logger.Logger
	webcamHost          string
	webcamPort          string
	rtr                 *mux.Router
	osHelper            OsHelper
	staticFilesystem    http.FileSystem
	templatesFilesystem http.FileSystem
}

type OsHelper interface {
	Exec(executable string, arg ...string) (string, error)
}

func NewExecutor(
	l logger.Logger,
	helper OsHelper,
	staticFilesystem http.FileSystem,
	templatesFilesystem http.FileSystem,
	webcamHost string,
	webcamPort string) *Executor {

	return &Executor{
		l:                   l,
		webcamHost:          webcamHost,
		webcamPort:          webcamPort,
		rtr:                 mux.NewRouter(),
		osHelper:            helper,
		staticFilesystem:    staticFilesystem,
		templatesFilesystem: templatesFilesystem,
	}
}

func (e *Executor) homepageHandler(w http.ResponseWriter, r *http.Request) {
	e.l.Log("homepage")
	buf := bytes.NewBuffer(nil)
	f, err := e.templatesFilesystem.Open("homepage.html")
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(buf, f)
	if err != nil {
		panic(err)
	}
	f.Close()
	w.Write(buf.Bytes())
}

func (e *Executor) webcamHandler(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get("http://" + e.webcamHost + ":" + e.webcamPort + "/?action=snapshot&n=" + r.Form.Get("n"))
	if err != nil {
		e.l.Log("Error getting image: " + err.Error())
		if resp == nil {
			return
		}
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		e.l.Log("Error closing image request: " + err.Error())
	}
	w.Write(body)
}

func (e *Executor) toggleDoorHandler(w http.ResponseWriter, r *http.Request) {
	e.executeCommand("gpio", "write", "0", "1")
	sleepDuration, _ := time.ParseDuration("500ms")
	e.l.Log("sleeping for " + sleepDuration.String())
	time.Sleep(sleepDuration)
	e.executeCommand("gpio", "write", "0", "0")

	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (e *Executor) executeCommand(executable string, arg ...string) string {
	logStatement := append([]string{executable}, arg...)
	e.l.Log("executing: '" + strings.Join(logStatement, " ") + "'")
	out, err := e.osHelper.Exec(executable, arg...)
	if err != nil {
		e.l.Log("ERROR: " + err.Error())
	}
	return out
}

func (e *Executor) startCameraHandler(w http.ResponseWriter, r *http.Request) {
	e.executeCommand("/etc/init.d/garagestreamer", "start")
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (e *Executor) stopCameraHandler(w http.ResponseWriter, r *http.Request) {
	e.executeCommand("/etc/init.d/garagestreamer", "stop")
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (e *Executor) ServeForever(port string) {
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(e.staticFilesystem)))

	e.rtr.HandleFunc("/", e.homepageHandler).Methods("GET")
	e.rtr.HandleFunc("/webcam", e.webcamHandler).Methods("GET")
	e.rtr.HandleFunc("/toggle", e.toggleDoorHandler).Methods("POST")
	e.rtr.HandleFunc("/start-camera", e.startCameraHandler).Methods("POST")
	e.rtr.HandleFunc("/stop-camera", e.stopCameraHandler).Methods("POST")

	http.Handle("/", e.rtr)
	e.l.Log("Listening on port " + port + "...")
	http.ListenAndServe(":"+port, nil)
}

func (e *Executor) Kill() {
	os.Exit(0)
}
<|endoftext|>


<commit_before>package pool

import "net"
import "errors"
import "sync"
import "time"

//blockingPool implements the Pool interface.
//Connestions from blockingPool offer a kind of blocking mechanism that is derived from buffered channel.
type blockingPool struct {
	//mutex is to make closing the pool and recycling the connection an atomic operation
	mutex sync.Mutex

	//timeout to Get, default to 3
	timeout int

	//storage for net.Conn connections
	conns chan net.Conn

	//net.Conn generator
	factory Factory
}

//Factory is a function to create new connections
//which is provided by the user
type Factory func() (net.Conn, error)

//Create a new blocking pool.
//As no new connections would be made when the pool is busy, maxCap does not make sense yet.
func NewBlockingPool(initCap, maxCap int, factory Factory) (Pool, error) {
	if initCap < 0 || maxCap < 1 || initCap > maxCap {
		return nil, errors.New("invalid capacity settings")
	}

	newPool := &blockingPool{
		timeout: 3,
		conns: make(chan net.Conn, maxCap),
		factory: factory
	}

	for i := 0; i < initCap; i++ {
		conn, err := factory()
		if err != nil {
			newPool.Close()
			return nil, fmt.Errorf("error counted when calling factory: %s", err)
		}
		newPool.conns <- conn
	}
	return newPool, nil
}

func (p *blockingPool) Get() (net.Conn, error) {
	//in case that pool is closed and pool.conns is set to nil
	conns := p.conns
	if conns == nil {
		return nil, ErrClosed
	}

	select {
	case conn := <-conns:
		return conn, nil/*not wrapped yet*/
	case <-time.After(time.Second*p.timeout) {
		return nil, errors.New("timeout")
	}
}

//put puts the connection back to the pool. If the pool is closed, put simply close
//any connections received and return immediately. A nil net.Conn is illegal and will be rejected.
func (p *blockingPool) put(conn net.Conn) error {
	if conn == nil {
		return errors.New("connection is nil.")
	}

	//in case that pool is closed and pool.conns is set to nil
	conns := p.conns
	if conns == nil {
		return conn.Close()
	}

	//It is impossible to block as number of connections is never more than length of channel
	conns <-conn
	return nil
}

func (p *blockingPool) Close() {}

func (p *blockingPool) Len() {}
<commit_msg>nothing<commit_after>package pool

import "net"
import "errors"
import "sync"
import "time"

//blockingPool implements the Pool interface.
//Connestions from blockingPool offer a kind of blocking mechanism that is derived from buffered channel.
type blockingPool struct {
	//mutex is to make closing the pool and recycling the connection an atomic operation
	mutex sync.Mutex

	//timeout to Get, default to 3
	timeout int

	//storage for net.Conn connections
	conns chan net.Conn

	//net.Conn generator
	factory Factory
}

//Factory is a function to create new connections
//which is provided by the user
type Factory func() (net.Conn, error)

//Create a new blocking pool.
//As no new connections would be made when the pool is busy, maxCap does not make sense yet.
func NewBlockingPool(initCap, maxCap int, factory Factory) (Pool, error) {
	if initCap < 0 || maxCap < 1 || initCap > maxCap {
		return nil, errors.New("invalid capacity settings")
	}

	newPool := &blockingPool{
		timeout: 3,
		conns: make(chan net.Conn, maxCap),
		factory: factory
	}

	for i := 0; i < initCap; i++ {
		conn, err := factory()
		if err != nil {
			newPool.Close()
			return nil, fmt.Errorf("error counted when calling factory: %s", err)
		}
		newPool.conns <- conn
	}
	return newPool, nil
}

func (p *blockingPool) Get() (net.Conn, error) {
	//in case that pool is closed and pool.conns is set to nil
	conns := p.conns
	if conns == nil {
		return nil, ErrClosed
	}

	select {
	case conn := <-conns:
		return conn, nil/*not wrapped yet*/
	case <-time.After(time.Second*p.timeout) {
		return nil, errors.New("timeout")
	}
}

//put puts the connection back to the pool. If the pool is closed, put simply close
//any connections received and return immediately. A nil net.Conn is illegal and will be rejected.
func (p *blockingPool) put(conn net.Conn) error {
	if conn == nil {
		return errors.New("connection is nil.")
	}

	//in case that pool is closed and pool.conns is set to nil
	conns := p.conns
	if conns == nil {
		return conn.Close()
	}

	//It is impossible to block as number of connections is never more than length of channel
	conns <-conn
	return nil
}

//
func (p *blockingPool) Close() {}

func (p *blockingPool) Len() {}
<|endoftext|>


<commit_before>// Package krberror provides error type and functions for gokrb5.
package krberror

import (
	"fmt"
	"strings"
)

// Error type descriptions.
const (
	separator       = " < "
	EncodingError   = "Encoding_Error"
	NetworkingError = "Networking_Error"
	DecryptingError = "Decrypting_Error"
	EncryptingError = "Encrypting_Error"
	ChksumError     = "Checksum_Error"
	KRBMsgError     = "KRBMessage_Handling_Error"
	ConfigError     = "Configuration_Error"
)

// Krberror is an error type for gokrb5
type Krberror struct {
	RootCause string
	EText     []string
}

// Error function to implement the error interface.
func (e Krberror) Error() string {
	return fmt.Sprintf("[Root cause: %s] ", e.RootCause) + strings.Join(e.EText, separator)
}

// Add another error statement to the error.
func (e *Krberror) Add(et string, s string) {
	e.EText = append([]string{fmt.Sprintf("%s: %s", et, s)}, e.EText...)
}

// NewKrberror creates a new instance of Krberror.
func NewKrberror(et, s string) Krberror {
	return Krberror{
		RootCause: et,
		EText:     []string{s},
	}
}

// Errorf appends to or creates a new Krberror.
func Errorf(err error, et, format string, a ...interface{}) Krberror {
	if e, ok := err.(Krberror); ok {
		if a == nil {
			e.EText = append([]string{fmt.Sprintf("%s: "+format, et)}, e.EText...)
			return e
		}
		e.EText = append([]string{fmt.Sprintf("%s: "+format, et, a)}, e.EText...)
		return e
	}
	return NewErrorf(et, format+": %s", a, err)
}

// NewErrorf creates a new Krberror from a formatted string.
func NewErrorf(et, format string, a ...interface{}) Krberror {
	var s string
	if a == nil {
		s = fmt.Sprintf("%s: %s", et, format)
	} else {
		s = fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a))
	}
	return Krberror{
		RootCause: et,
		EText:     []string{s},
	}
}
<commit_msg>fix error text<commit_after>// Package krberror provides error type and functions for gokrb5.
package krberror

import (
	"fmt"
	"strings"
)

// Error type descriptions.
const (
	separator       = " < "
	EncodingError   = "Encoding_Error"
	NetworkingError = "Networking_Error"
	DecryptingError = "Decrypting_Error"
	EncryptingError = "Encrypting_Error"
	ChksumError     = "Checksum_Error"
	KRBMsgError     = "KRBMessage_Handling_Error"
	ConfigError     = "Configuration_Error"
)

// Krberror is an error type for gokrb5
type Krberror struct {
	RootCause string
	EText     []string
}

// Error function to implement the error interface.
func (e Krberror) Error() string {
	return fmt.Sprintf("[Root cause: %s] ", e.RootCause) + strings.Join(e.EText, separator)
}

// Add another error statement to the error.
func (e *Krberror) Add(et string, s string) {
	e.EText = append([]string{fmt.Sprintf("%s: %s", et, s)}, e.EText...)
}

// NewKrberror creates a new instance of Krberror.
func NewKrberror(et, s string) Krberror {
	return Krberror{
		RootCause: et,
		EText:     []string{s},
	}
}

// Errorf appends to or creates a new Krberror.
func Errorf(err error, et, format string, a ...interface{}) Krberror {
	if e, ok := err.(Krberror); ok {
		if len(a) > 0 {
			e.EText = append([]string{fmt.Sprintf("%s: "+format, et, a)}, e.EText...)
			return e
		}
		e.EText = append([]string{fmt.Sprintf("%s: "+format, et)}, e.EText...)
		return e
	}
	if len(a) > 0 {
		return NewErrorf(et, format+": %s", a, err)
	}
	return NewErrorf(et, format+": %s", err)
}

// NewErrorf creates a new Krberror from a formatted string.
func NewErrorf(et, format string, a ...interface{}) Krberror {
	var s string
	if len(a) > 0 {
		s = fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a))
	} else {
		s = fmt.Sprintf("%s: %s", et, format)
	}
	return Krberror{
		RootCause: et,
		EText:     []string{s},
	}
}
<|endoftext|>


<commit_before>package serial

import (
	"errors"
	"io"
	"os"
	"syscall"
	"unsafe"
)

//
// Grab the constants with the following little program, to avoid using cgo:
//
// #include <stdio.h>
// #include <stdlib.h>
// #include <linux/termios.h>
//
// int main(int argc, const char **argv) {
//   printf("TCGETS2 = 0x%08X\n", TCGETS2);
//   printf("TCSETS2 = 0x%08X\n", TCSETS2);
//   printf("BOTHER  = 0x%08X\n", BOTHER);
//   printf("NCCS    = %d\n",     NCCS);
//   return 0;
// }
//
const (
	TCGETS2 = 0x802C542A
	TCSETS2 = 0x402C542B
	BOTHER  = 0x1000
	NCCS    = 19
)

//
// Types from asm-generic/termbits.h
//

type cc_t byte
type speed_t uint32
type tcflag_t uint32
type termios2 struct {
	c_iflag  tcflag_t   // input mode flags
	c_oflag  tcflag_t   // output mode flags
	c_cflag  tcflag_t   // control mode flags
	c_lflag  tcflag_t   // local mode flags
	c_line   cc_t       // line discipline
	c_cc     [NCCS]cc_t // control characters
	c_ispeed speed_t    // input speed
	c_ospeed speed_t    // output speed
}

//
// Returns a pointer to an instantiates termios2 struct, based on the given
// OpenOptions. Termios2 is a Linux extension which allows arbitrary baud rates
// to be specified.
//
func makeTermios2(options OpenOptions) (*termios2, error) {

	// Sanity check inter-character timeout and minimum read size options.

	vtime := uint(round(float64(options.InterCharacterTimeout)/100.0) * 100)
	vmin := options.MinimumReadSize

	if vmin == 0 && vtime < 100 {
		return nil, errors.New("invalid values for InterCharacterTimeout and MinimumReadSize")
	}

	if vtime > 25500 {
		return nil, errors.New("invalid value for InterCharacterTimeout")
	}

	ccOpts := [NCCS]cc_t{}
	ccOpts[syscall.VTIME] = cc_t(vtime / 100)
	ccOpts[syscall.VMIN] = cc_t(vmin)

	t2 := &termios2{
		c_cflag:  syscall.CLOCAL | syscall.CREAD | BOTHER,
		c_ispeed: speed_t(options.BaudRate),
		c_ospeed: speed_t(options.BaudRate),
		c_cc:     ccOpts,
	}

	switch options.StopBits {
	case 1:
	case 2:
		t2.c_cflag |= syscall.CSTOPB

	default:
		return nil, errors.New("invalid setting for StopBits")
	}

	switch options.ParityMode {
	case PARITY_NONE:
	case PARITY_ODD:
		t2.c_cflag |= syscall.PARENB
		t2.c_cflag |= syscall.PARODD

	case PARITY_EVEN:
		t2.c_cflag |= syscall.PARENB

	default:
		return nil, errors.New("invalid setting for ParityMode")
	}

	return t2, nil
}

func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {

	file, openErr :=
		os.OpenFile(
			options.PortName,
			syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK,
			0600)
	if openErr != nil {
		return nil, openErr
	}

	// Clear the non-blocking flag set above.
	nonblockErr := syscall.SetNonblock(int(file.Fd()), false)
	if nonblockErr != nil {
		return nil, nonblockErr
	}

	t2, optErr := makeTermios2(options)
	if optErr != nil {
		return nil, optErr
	}

	r, _, errno := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(file.Fd()),
		uintptr(TCSETS2),
		uintptr(unsafe.Pointer(t2)))

	if errno != 0 {
		return nil, os.NewSyscallError("SYS_IOCTL", errno)
	}

	if r != 0 {
		return nil, errors.New("unknown error from SYS_IOCTL")
	}

	return file, nil
}
<commit_msg>Don't export termios consts<commit_after>package serial

import (
	"errors"
	"io"
	"os"
	"syscall"
	"unsafe"
)

//
// Grab the constants with the following little program, to avoid using cgo:
//
// #include <stdio.h>
// #include <stdlib.h>
// #include <linux/termios.h>
//
// int main(int argc, const char **argv) {
//   printf("TCSETS2 = 0x%08X\n", TCSETS2);
//   printf("BOTHER  = 0x%08X\n", BOTHER);
//   printf("NCCS    = %d\n",     NCCS);
//   return 0;
// }
//
const (
	kTCSETS2 = 0x402C542B
	kBOTHER  = 0x1000
	kNCCS    = 19
)

//
// Types from asm-generic/termbits.h
//

type cc_t byte
type speed_t uint32
type tcflag_t uint32
type termios2 struct {
	c_iflag  tcflag_t    // input mode flags
	c_oflag  tcflag_t    // output mode flags
	c_cflag  tcflag_t    // control mode flags
	c_lflag  tcflag_t    // local mode flags
	c_line   cc_t        // line discipline
	c_cc     [kNCCS]cc_t // control characters
	c_ispeed speed_t     // input speed
	c_ospeed speed_t     // output speed
}

//
// Returns a pointer to an instantiates termios2 struct, based on the given
// OpenOptions. Termios2 is a Linux extension which allows arbitrary baud rates
// to be specified.
//
func makeTermios2(options OpenOptions) (*termios2, error) {

	// Sanity check inter-character timeout and minimum read size options.

	vtime := uint(round(float64(options.InterCharacterTimeout)/100.0) * 100)
	vmin := options.MinimumReadSize

	if vmin == 0 && vtime < 100 {
		return nil, errors.New("invalid values for InterCharacterTimeout and MinimumReadSize")
	}

	if vtime > 25500 {
		return nil, errors.New("invalid value for InterCharacterTimeout")
	}

	ccOpts := [kNCCS]cc_t{}
	ccOpts[syscall.VTIME] = cc_t(vtime / 100)
	ccOpts[syscall.VMIN] = cc_t(vmin)

	t2 := &termios2{
		c_cflag:  syscall.CLOCAL | syscall.CREAD | kBOTHER,
		c_ispeed: speed_t(options.BaudRate),
		c_ospeed: speed_t(options.BaudRate),
		c_cc:     ccOpts,
	}

	switch options.StopBits {
	case 1:
	case 2:
		t2.c_cflag |= syscall.CSTOPB

	default:
		return nil, errors.New("invalid setting for StopBits")
	}

	switch options.ParityMode {
	case PARITY_NONE:
	case PARITY_ODD:
		t2.c_cflag |= syscall.PARENB
		t2.c_cflag |= syscall.PARODD

	case PARITY_EVEN:
		t2.c_cflag |= syscall.PARENB

	default:
		return nil, errors.New("invalid setting for ParityMode")
	}

	return t2, nil
}

func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {

	file, openErr :=
		os.OpenFile(
			options.PortName,
			syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK,
			0600)
	if openErr != nil {
		return nil, openErr
	}

	// Clear the non-blocking flag set above.
	nonblockErr := syscall.SetNonblock(int(file.Fd()), false)
	if nonblockErr != nil {
		return nil, nonblockErr
	}

	t2, optErr := makeTermios2(options)
	if optErr != nil {
		return nil, optErr
	}

	r, _, errno := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(file.Fd()),
		uintptr(kTCSETS2),
		uintptr(unsafe.Pointer(t2)))

	if errno != 0 {
		return nil, os.NewSyscallError("SYS_IOCTL", errno)
	}

	if r != 0 {
		return nil, errors.New("unknown error from SYS_IOCTL")
	}

	return file, nil
}
<|endoftext|>


<commit_before>package gweb

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/conf"
	"github.com/eynstudio/gobreak/di"
)

type App struct {
	Root INode
	di.Container
	Name string
	*Cfg
	Server *http.Server
	*Router
	*Tmpl
	*Sessions
}

func NewApp(name string) *App {
	var cfg Cfg
	conf.MustLoadJsonCfg(&cfg, "conf/"+name+".json")
	return NewAppWithCfg(&cfg)
}

func NewAppWithCfg(c *Cfg) *App {
	app := &App{
		Root:      NewNode("", false),
		Container: di.Root,
		Name:      "",
		Cfg:       c,
		Router:    &Router{},
		Tmpl:      &Tmpl{},
		Sessions:  &Sessions{},
	}

	if c.UseTmpl {
		app.Tmpl.Load()
	}
	app.Server = &http.Server{
		Addr:         fmt.Sprintf(":%d", c.Port),
		Handler:      http.HandlerFunc(app.handler),
		ReadTimeout:  time.Minute,
		WriteTimeout: time.Minute,
	}
	app.Map(c)
	return app
}

func (p *App) Start() {
	p.injectNodes(p.Root)

	if p.Cfg.Tls {
		err := p.Server.ListenAndServeTLS(p.Cfg.CertFile, p.Cfg.KeyFile)
		if err != nil {
			panic(err)
		}
	} else {
		err := p.Server.ListenAndServe()
		if err != nil {
			panic(err)
		}
	}
}

func (p *App) injectNodes(n INode) {
	p.Apply(n)
	log.Printf("xxxxxxxxxxxxxxxxxxx,%#v", n)

	nodes := n.GetNodes()
	for i, l := 0, len(nodes); i < l; i++ {
		//		p.Apply(&nodes[i])
		p.injectNodes(nodes[i])
	}
}

func (p *App) NewCtx(r *http.Request, rw http.ResponseWriter) *Ctx {
	req := NewReq(r)
	resp := &Resp{rw}
	c := &Ctx{App: p, Container: di.New(), Req: req, Resp: resp, Scope: M{}}
	c.Map(c) //需要吗？
	c.Map(resp)
	c.Map(req)
	c.SetParent(p)
	return c
}

func (p *App) handler(w http.ResponseWriter, r *http.Request) {
	ctx := p.NewCtx(r, w)
	if !ctx.ServeFile() {
		p.Route(p.Root, ctx)
		if !ctx.Handled {
			ctx.NotFound()
		}
	}

	if w, ok := ctx.Resp.ResponseWriter.(io.Closer); ok {
		w.Close()
	}
}
<commit_msg>rm log info<commit_after>package gweb

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/conf"
	"github.com/eynstudio/gobreak/di"
)

type App struct {
	Root INode
	di.Container
	Name string
	*Cfg
	Server *http.Server
	*Router
	*Tmpl
	*Sessions
}

func NewApp(name string) *App {
	var cfg Cfg
	conf.MustLoadJsonCfg(&cfg, "conf/"+name+".json")
	return NewAppWithCfg(&cfg)
}

func NewAppWithCfg(c *Cfg) *App {
	app := &App{
		Root:      NewNode("", false),
		Container: di.Root,
		Name:      "",
		Cfg:       c,
		Router:    &Router{},
		Tmpl:      &Tmpl{},
		Sessions:  &Sessions{},
	}

	if c.UseTmpl {
		app.Tmpl.Load()
	}
	app.Server = &http.Server{
		Addr:         fmt.Sprintf(":%d", c.Port),
		Handler:      http.HandlerFunc(app.handler),
		ReadTimeout:  time.Minute,
		WriteTimeout: time.Minute,
	}
	app.Map(c)
	return app
}

func (p *App) Start() {
	p.injectNodes(p.Root)

	if p.Cfg.Tls {
		err := p.Server.ListenAndServeTLS(p.Cfg.CertFile, p.Cfg.KeyFile)
		if err != nil {
			panic(err)
		}
	} else {
		err := p.Server.ListenAndServe()
		if err != nil {
			panic(err)
		}
	}
}

func (p *App) injectNodes(n INode) {
	p.Apply(n)
	nodes := n.GetNodes()
	for i, l := 0, len(nodes); i < l; i++ {
		p.injectNodes(nodes[i])
	}
}

func (p *App) NewCtx(r *http.Request, rw http.ResponseWriter) *Ctx {
	req := NewReq(r)
	resp := &Resp{rw}
	c := &Ctx{App: p, Container: di.New(), Req: req, Resp: resp, Scope: M{}}
	c.Map(c) //需要吗？
	c.Map(resp)
	c.Map(req)
	c.SetParent(p)
	return c
}

func (p *App) handler(w http.ResponseWriter, r *http.Request) {
	ctx := p.NewCtx(r, w)
	if !ctx.ServeFile() {
		p.Route(p.Root, ctx)
		if !ctx.Handled {
			ctx.NotFound()
		}
	}

	if w, ok := ctx.Resp.ResponseWriter.(io.Closer); ok {
		w.Close()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strings"
)

type Sources struct {
	Sources []string
}

type Output struct {
	Source          string
	Paragraphs      []string
	ShowsParagraphs bool
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func getSources() Sources {
	sourceDir, err := ioutil.ReadDir("lorem")
	check(err)

	sources := Sources{}
	for _, entry := range sourceDir {
		sources.Sources = append(sources.Sources, entry.Name())
	}

	return sources
}

func getRandomContent(stripParagraphs bool) (string, []string) {
	sources := getSources()
	source := sources.Sources[rand.Intn(len(sources.Sources))]

	content, err := ioutil.ReadFile("lorem/" + source)
	check(err)

	parsedContent := string(content)

	if stripParagraphs {
		parsedContent = strings.ReplaceAll(parsedContent, "
", "")
		parsedContent = strings.ReplaceAll(parsedContent, "
", "")
	}

	parsedLines := strings.Split(parsedContent, "\n")

	return source, parsedLines
}

func main() {

	tmpl := template.Must(template.ParseFiles("template.html"))

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		source, content := getRandomContent(true)

		output := Output{
			Source:          strings.TrimSuffix(source, ".txt"),
			Paragraphs:      content,
			ShowsParagraphs: false,
		}

		tmpl.Execute(w, output)
	})

	http.HandleFunc("/p", func(w http.ResponseWriter, r *http.Request) {
		source, content := getRandomContent(false)

		output := Output{
			Source:          strings.TrimSuffix(source, ".txt"),
			Paragraphs:      content,
			ShowsParagraphs: true,
		}

		tmpl.Execute(w, output)
	})

	http.ListenAndServe(":80", nil)
}
<commit_msg>Shuffle paragraphs before the output<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strings"
	"time"
)

type Sources struct {
	Sources []string
}

type Output struct {
	Source          string
	Paragraphs      []string
	ShowsParagraphs bool
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func getSources() Sources {
	sourceDir, err := ioutil.ReadDir("lorem")
	check(err)

	sources := Sources{}
	for _, entry := range sourceDir {
		sources.Sources = append(sources.Sources, entry.Name())
	}

	return sources
}

func getRandomContent(stripParagraphs bool) (string, []string) {
	sources := getSources()
	source := sources.Sources[rand.Intn(len(sources.Sources))]

	content, err := ioutil.ReadFile("lorem/" + source)
	check(err)

	parsedContent := string(content)

	if stripParagraphs {
		parsedContent = strings.ReplaceAll(parsedContent, "
", "")
		parsedContent = strings.ReplaceAll(parsedContent, "
", "")
	}

	parsedLines := strings.Split(parsedContent, "\n")

	rand.Seed(time.Now().UnixNano())
	rand.Shuffle(len(parsedLines), func(i, j int) {
		parsedLines[i], parsedLines[j] = parsedLines[j], parsedLines[i]
	})

	return source, parsedLines
}

func main() {

	tmpl := template.Must(template.ParseFiles("template.html"))

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		source, content := getRandomContent(true)

		output := Output{
			Source:          strings.TrimSuffix(source, ".txt"),
			Paragraphs:      content,
			ShowsParagraphs: false,
		}

		tmpl.Execute(w, output)
	})

	http.HandleFunc("/p", func(w http.ResponseWriter, r *http.Request) {
		source, content := getRandomContent(false)

		output := Output{
			Source:          strings.TrimSuffix(source, ".txt"),
			Paragraphs:      content,
			ShowsParagraphs: true,
		}

		tmpl.Execute(w, output)
	})

	http.ListenAndServe(":80", nil)
}
<|endoftext|>


<commit_before>package logger

import (
	"io"
	"io/ioutil"
	"log"
	"os"

	"github.com/fatih/color"
)

var (
	traceLogger   *log.Logger // Just about anything
	commandLogger *log.Logger // Important information
	infoLogger    *log.Logger // Important information
	errorLogger   *log.Logger // Critical problem

	// Defining color functions for loggers
	red    = color.New(color.FgRed).SprintFunc()
	cyan   = color.New(color.FgCyan).SprintFunc()
	yellow = color.New(color.FgYellow).SprintFunc()
)

func init() {

	// Initialising loggers
	traceLogger = log.New(ioutil.Discard,
		"TRACE: ",
		log.Ldate|log.Lmicroseconds)

	commandLogger = log.New(os.Stdout,
		yellow("> "),
		0)

	infoLogger = log.New(os.Stdout,
		cyan("INFO: "),
		0)

	errorLogger = log.New(os.Stderr,
		red("ERROR: "),
		0)
}

// Log commands
func Command(message string) {
	commandLogger.Printf("%s\n", yellow(message))
}

// Log important information
func Info(message string) {
	infoLogger.Printf("%s\n", message)
}

// Log error information and exit
func Error(err error) {
	errorLogger.Fatalf("%s\n", err)
}

// Log anything to trace
func Trace(message interface{}) {
	traceLogger.Printf("%+v\n", message)
}

// Change Trace ouput writer, default is ioutil.Discard.
func SetTraceOutput(w io.Writer) {
	traceLogger.SetOutput(w)
}
<commit_msg>Use color.Output to display colors in windows<commit_after>package logger

import (
	"io"
	"io/ioutil"
	"log"

	"github.com/fatih/color"
)

var (
	traceLogger   *log.Logger // Just about anything
	commandLogger *log.Logger // Important information
	infoLogger    *log.Logger // Important information
	errorLogger   *log.Logger // Critical problem

	// Defining color functions for loggers
	red    = color.New(color.FgRed).SprintFunc()
	cyan   = color.New(color.FgCyan).SprintFunc()
	yellow = color.New(color.FgYellow).SprintFunc()
)

func init() {

	// Initialising loggers
	traceLogger = log.New(ioutil.Discard,
		"TRACE: ",
		log.Ldate|log.Lmicroseconds)

	commandLogger = log.New(color.Output,
		yellow("> "),
		0)

	infoLogger = log.New(color.Output,
		cyan("INFO: "),
		0)

	errorLogger = log.New(color.Output,
		red("ERROR: "),
		0)
}

// Log commands
func Command(message string) {
	commandLogger.Printf("%s\n", yellow(message))
}

// Log important information
func Info(message string) {
	infoLogger.Printf("%s\n", message)
}

// Log error information and exit
func Error(err error) {
	errorLogger.Fatalf("%s\n", err)
}

// Log anything to trace
func Trace(message interface{}) {
	traceLogger.Printf("%+v\n", message)
}

// Change Trace ouput writer, default is ioutil.Discard.
func SetTraceOutput(w io.Writer) {
	traceLogger.SetOutput(w)
}
<|endoftext|>


<commit_before>package logging

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type SyslogLine struct {
	Raw      string    `json:"raw,omitempty"`
	Time     time.Time `json:"time,omitempty"`
	Host     string    `json:"host,omitempty"`
	Tag      string    `json:"tag,omitempty"`
	Severity string    `json:"severity,omitempty"`
	Pid      int       `json:"pid,omitempty"`
	Port     string    `json:"port,omitempty"`
	Message  string    `json:"message,omitempty"`

	fields     []string
	parsed     bool
	tags       map[string]interface{}
	tagsParsed bool
}

func (line *SyslogLine) TagString(key string) string {
	return fmt.Sprint(line.Tags()[key])
}

var validKeyRegexp = regexp.MustCompile("(?i)^[a-z]+$")
var callsRegexp = regexp.MustCompile("^([0-9.]+)\\/([0-9]+)$")

func removeQuotes(raw string) string {
	if strings.HasPrefix(raw, `"`) && strings.HasSuffix(raw, `"`) {
		return raw[1 : len(raw)-1]
	}
	return raw
}

func Fields(line string) []string {
	fields := strings.Fields(line)
	inQuotes := false
	sep := `"`
	current := ""
	out := []string{}
	for _, f := range fields {
		if strings.Contains(f, sep) {
			cnt := strings.Count(f, sep)
			replaced := strings.Replace(f, sep, "", -1)
			if cnt == 2 {
				out = append(out, replaced)
				continue
			} else if cnt != 1 {
				panic("cnt != 1 or 2 not supported (yet)")
			}
			switch inQuotes {
			case true:
				out = append(out, current+" "+replaced)
				inQuotes = false
			case false:
				current = replaced
				inQuotes = true
			}
		} else if inQuotes {
			current = current + " " + f
		} else {
			out = append(out, f)
		}
	}
	return out
}

func parseTags(raw string) map[string]interface{} {
	t := map[string]interface{}{}
	for _, field := range Fields(raw) {
		kv := strings.SplitN(field, "=", 2)
		if len(kv) == 2 && validKeyRegexp.MatchString(kv[0]) {
			currentKey := kv[0]
			value := kv[1]
			m := callsRegexp.FindStringSubmatch(value)
			if len(m) == 3 {
				totalTime, e := strconv.ParseFloat(m[1], 64)
				if e == nil {
					calls, e := strconv.ParseInt(m[2], 10, 64)
					if e == nil {
						t[currentKey+"_time"] = totalTime
						t[currentKey+"_calls"] = calls
					}
				}
			} else {
				t[currentKey] = parseTagValue(value)
				currentKey = ""
			}
		}
	}
	return t
}

func (line *SyslogLine) Tags() (t map[string]interface{}) {
	if !line.tagsParsed {
		line.tags = parseTags(line.Raw)
	}
	return line.tags
}

func parseTagValue(raw string) interface{} {
	if i, e := strconv.ParseInt(raw, 10, 64); e == nil {
		return i
	} else if f, e := strconv.ParseFloat(raw, 64); e == nil {
		return f
	}
	return removeQuotes(raw)
}

const (
	timeLayout             = "2006-01-02T15:04:05.000000-07:00"
	timeLayoutWithoutMicro = "2006-01-02T15:04:05-07:00"
)

var TagRegexp = regexp.MustCompile("(.*?)\\[(\\d*)\\]")

func (line *SyslogLine) Parse(raw string) (e error) {
	if line.parsed {
		return nil
	}
	line.Raw = raw
	line.fields = strings.Fields(raw)
	if len(line.fields) >= 3 {
		line.Time, e = time.Parse(timeLayout, line.fields[0])
		if e != nil {
			line.Time, e = time.Parse(timeLayoutWithoutMicro, line.fields[0])
			if e != nil {
				return e
			}
		}
		line.Host = line.fields[1]
		line.Tag, line.Port, line.Severity, line.Pid = parseTag(line.fields[2])
		if len(line.fields) > 3 {
			line.Message = strings.Join(line.fields[3:], " ")
		}
	}
	line.parsed = true
	return nil
}

func parseTag(raw string) (tag, port, severity string, pid int) {
	tagAndSeverity, pid := splitTagAndPid(raw)
	tag, port, severity = splitTagAndSeverity(tagAndSeverity)
	return tag, port, severity, pid
}

func splitTagAndSeverity(raw string) (tag, port, severity string) {
	tag = raw
	parts := strings.Split(raw, ".")
	if len(parts) == 3 {
		// seems to be mongodb
		tag, port, severity = parts[0], parts[1], parts[2]

	} else if len(parts) == 2 {
		tag, severity = parts[0], parts[1]
	} else {
		tag = raw
	}
	return tag, port, severity
}

func splitTagAndPid(raw string) (tag string, pid int) {
	tag = raw
	chunks := TagRegexp.FindStringSubmatch(raw)
	if len(chunks) > 2 {
		tag = chunks[1]
		pid, _ = strconv.Atoi(chunks[2])
	} else {
		if tag[len(tag)-1] == ':' {
			tag = tag[0 : len(tag)-1]
		}
	}
	return tag, pid
}

var UUIDRegexp = regexp.MustCompile("([a-z0-9\\-]{36})")

type UnicornLine struct {
	UUID string
	SyslogLine
}

func (line *UnicornLine) Parse(raw string) error {
	e := line.SyslogLine.Parse(raw)
	if e != nil {
		return e
	}
	if line.Tag != "unicorn" {
		return fmt.Errorf("tag %q not supported", line.Tag)
	}
	if len(line.fields) >= 4 {
		parts := UUIDRegexp.FindStringSubmatch(raw)
		if len(parts) > 1 {
			line.UUID = parts[1]
		}
	}
	return nil
}

type NginxLine struct {
	*SyslogLine
	XForwardedFor []string `json:"x_forwarded_for,omitempty"`
	Method        string   `json:"method,omitempty"`
	Status        string   `json:"status,omitempty"`
	Length        int      `json:"length,omitempty"`
	TotalTime     float64  `json:"total_time,omitempty"`
	UnicornTime   float64  `json:"unicorn_time,omitempty"`
	HttpHost      string   `json:"http_host,omitempty"`
	UserAgentName string   `json:"user_agent_name,omitempty"`
	Uri           string   `json:"uri,omitempty"`
	Referer       string   `json:"referer,omitempty"`
	Action        string   `json:"action,omitempty"`
	Revision      string   `json:"revision,omitempty"`
	UUID          string   `json:"uuid,omitempty"`
	Etag          string   `json:"etag,omitempty"`
}

var quotesRegexp = regexp.MustCompile(`(ua|uri|ref)="(.*?)"`)

func (line *NginxLine) Parse(raw string) error {
	if line.SyslogLine == nil {
		line.SyslogLine = &SyslogLine{}
	}
	e := line.SyslogLine.Parse(raw)
	if e != nil {
		return e
	}
	if line.Tag != "ssl_endpoint" && line.Tag != "nginx" {
		return fmt.Errorf("tag %q not supported", line.Tag)
	}
	forwarded := false
	for i, field := range line.fields {
		if forwarded && strings.Contains(field, "=") {
			forwarded = false
		}
		parts := strings.SplitN(field, "=", 2)
		if len(parts) == 2 {
			key := parts[0]
			value := parts[1]
			switch key {
			case "rev":
				line.Revision = value
			case "action":
				line.Action = value
			case "nginx":
				if !strings.Contains(line.Raw, "forwarded=") {
					forwarded = true
				}
			case "forwarded":
				forwarded = false
				line.XForwardedFor = []string{parts[1]}
			case "method":
				line.Method = value
			case "uuid":
				line.UUID = filterDash(value)
			case "etag":
				line.Etag = filterDash(value)
			case "status":
				line.Status = value
			case "host":
				line.HttpHost = value
			case "length":
				line.Length, _ = strconv.Atoi(value)
			case "total":
				line.TotalTime, _ = strconv.ParseFloat(value, 64)
			case "unicorn_time":
				line.UnicornTime, _ = strconv.ParseFloat(value, 64)
			}
		} else if i == 2 && strings.HasPrefix(field, "nginx") {
			if !strings.Contains(line.Raw, "forwarded=") {
				forwarded = true
			}
		} else if forwarded {
			if strings.HasPrefix(field, "host") || field == "-" {
				forwarded = false
			} else {
				line.XForwardedFor = append(line.XForwardedFor, strings.TrimSuffix(field, ","))
			}

		}
	}
	quotes := quotesRegexp.FindAllStringSubmatch(raw, -1)
	for _, quote := range quotes {
		switch quote[1] {
		case "ua":
			line.UserAgentName = quote[2]
		case "uri":
			line.Uri = quote[2]
		case "ref":
			line.Referer = filterDash(quote[2])
		default:
		}
	}
	return nil
}

func filterDash(raw string) string {
	if raw == "-" {
		return ""
	}
	return raw
}

type HAProxyLine struct {
	SyslogLine
	Frontend            string
	Backend             string
	BackendHost         string
	BackendImageId      string
	BackendContainerId  string
	Status              string
	Length              int
	ClientRequestTime   int
	ConnectionQueueTime int
	TcpConnectTime      int
	ServerResponseTime  int
	SessionDurationTime int
	ActiveConnections   int
	FrontendConnections int
	BackendConnectons   int
	ServerConnections   int
	Retries             int
	ServerQueue         int
	BackendQueue        int
	Method              string
	Uri                 string
}

func (line *HAProxyLine) Parse(raw string) error {
	e := line.SyslogLine.Parse(raw)
	if e != nil {
		return e
	}
	if line.Tag != "haproxy" {
		return fmt.Errorf("tag was %s", line.Tag)
	}
	if len(line.fields) > 16 {
		line.Frontend = line.fields[5]
		backend := line.fields[6]
		parts := strings.SplitN(backend, "/", 2)
		if len(parts) == 2 {
			line.Backend = parts[0]
			backendContainer := parts[1]
			parts := strings.Split(backendContainer, ":")
			if len(parts) == 3 {
				line.BackendHost = parts[0]
				line.BackendImageId = parts[1]
				line.BackendContainerId = parts[2]
			}
		}
		times := line.fields[7]
		parts = strings.Split(times, "/")
		if len(parts) == 5 {
			line.ClientRequestTime, _ = strconv.Atoi(parts[0])
			line.ConnectionQueueTime, _ = strconv.Atoi(parts[1])
			line.TcpConnectTime, _ = strconv.Atoi(parts[2])
			line.ServerResponseTime, _ = strconv.Atoi(parts[3])
			line.SessionDurationTime, _ = strconv.Atoi(parts[4])
		}
		line.Status = line.fields[8]
		line.Length, _ = strconv.Atoi(line.fields[9])

		connections := line.fields[13]
		parts = strings.Split(connections, "/")
		if len(parts) == 5 {
			line.ActiveConnections, _ = strconv.Atoi(parts[0])
			line.FrontendConnections, _ = strconv.Atoi(parts[1])
			line.BackendConnectons, _ = strconv.Atoi(parts[2])
			line.ServerConnections, _ = strconv.Atoi(parts[3])
			line.Retries, _ = strconv.Atoi(parts[4])
		}

		queues := line.fields[14]
		parts = strings.Split(queues, "/")
		if len(parts) == 2 {
			line.ServerQueue, _ = strconv.Atoi(parts[0])
			line.BackendQueue, _ = strconv.Atoi(parts[1])
		}
		line.Method = line.fields[15][1:]
		line.Uri = line.fields[16]
	}
	return nil
}
<commit_msg>use shellwords for line parsing<commit_after>package logging

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	shellwords "github.com/mattn/go-shellwords"
)

type SyslogLine struct {
	Raw      string    `json:"raw,omitempty"`
	Time     time.Time `json:"time,omitempty"`
	Host     string    `json:"host,omitempty"`
	Tag      string    `json:"tag,omitempty"`
	Severity string    `json:"severity,omitempty"`
	Pid      int       `json:"pid,omitempty"`
	Port     string    `json:"port,omitempty"`
	Message  string    `json:"message,omitempty"`

	fields     []string
	parsed     bool
	tags       map[string]interface{}
	tagsParsed bool
}

func (line *SyslogLine) TagString(key string) string {
	return fmt.Sprint(line.Tags()[key])
}

var validKeyRegexp = regexp.MustCompile("(?i)^[a-z]+$")
var callsRegexp = regexp.MustCompile("^([0-9.]+)\\/([0-9]+)$")

func removeQuotes(raw string) string {
	if strings.HasPrefix(raw, `"`) && strings.HasSuffix(raw, `"`) {
		return raw[1 : len(raw)-1]
	}
	return raw
}

func Fields(line string) []string {
	list, err := shellwords.Parse(line)
	if err != nil {
		panic(err.Error())
	}
	return list
}

func parseTags(raw string) map[string]interface{} {
	t := map[string]interface{}{}
	for _, field := range Fields(raw) {
		kv := strings.SplitN(field, "=", 2)
		if len(kv) == 2 && validKeyRegexp.MatchString(kv[0]) {
			currentKey := kv[0]
			value := kv[1]
			m := callsRegexp.FindStringSubmatch(value)
			if len(m) == 3 {
				totalTime, e := strconv.ParseFloat(m[1], 64)
				if e == nil {
					calls, e := strconv.ParseInt(m[2], 10, 64)
					if e == nil {
						t[currentKey+"_time"] = totalTime
						t[currentKey+"_calls"] = calls
					}
				}
			} else {
				t[currentKey] = parseTagValue(value)
				currentKey = ""
			}
		}
	}
	return t
}

func (line *SyslogLine) Tags() (t map[string]interface{}) {
	if !line.tagsParsed {
		line.tags = parseTags(line.Raw)
	}
	return line.tags
}

func parseTagValue(raw string) interface{} {
	if i, e := strconv.ParseInt(raw, 10, 64); e == nil {
		return i
	} else if f, e := strconv.ParseFloat(raw, 64); e == nil {
		return f
	}
	return removeQuotes(raw)
}

const (
	timeLayout             = "2006-01-02T15:04:05.000000-07:00"
	timeLayoutWithoutMicro = "2006-01-02T15:04:05-07:00"
)

var TagRegexp = regexp.MustCompile("(.*?)\\[(\\d*)\\]")

func (line *SyslogLine) Parse(raw string) (e error) {
	if line.parsed {
		return nil
	}
	line.Raw = raw
	line.fields = strings.Fields(raw)
	if len(line.fields) >= 3 {
		line.Time, e = time.Parse(timeLayout, line.fields[0])
		if e != nil {
			line.Time, e = time.Parse(timeLayoutWithoutMicro, line.fields[0])
			if e != nil {
				return e
			}
		}
		line.Host = line.fields[1]
		line.Tag, line.Port, line.Severity, line.Pid = parseTag(line.fields[2])
		if len(line.fields) > 3 {
			line.Message = strings.Join(line.fields[3:], " ")
		}
	}
	line.parsed = true
	return nil
}

func parseTag(raw string) (tag, port, severity string, pid int) {
	tagAndSeverity, pid := splitTagAndPid(raw)
	tag, port, severity = splitTagAndSeverity(tagAndSeverity)
	return tag, port, severity, pid
}

func splitTagAndSeverity(raw string) (tag, port, severity string) {
	tag = raw
	parts := strings.Split(raw, ".")
	if len(parts) == 3 {
		// seems to be mongodb
		tag, port, severity = parts[0], parts[1], parts[2]

	} else if len(parts) == 2 {
		tag, severity = parts[0], parts[1]
	} else {
		tag = raw
	}
	return tag, port, severity
}

func splitTagAndPid(raw string) (tag string, pid int) {
	tag = raw
	chunks := TagRegexp.FindStringSubmatch(raw)
	if len(chunks) > 2 {
		tag = chunks[1]
		pid, _ = strconv.Atoi(chunks[2])
	} else {
		if tag[len(tag)-1] == ':' {
			tag = tag[0 : len(tag)-1]
		}
	}
	return tag, pid
}

var UUIDRegexp = regexp.MustCompile("([a-z0-9\\-]{36})")

type UnicornLine struct {
	UUID string
	SyslogLine
}

func (line *UnicornLine) Parse(raw string) error {
	e := line.SyslogLine.Parse(raw)
	if e != nil {
		return e
	}
	if line.Tag != "unicorn" {
		return fmt.Errorf("tag %q not supported", line.Tag)
	}
	if len(line.fields) >= 4 {
		parts := UUIDRegexp.FindStringSubmatch(raw)
		if len(parts) > 1 {
			line.UUID = parts[1]
		}
	}
	return nil
}

type NginxLine struct {
	*SyslogLine
	XForwardedFor []string `json:"x_forwarded_for,omitempty"`
	Method        string   `json:"method,omitempty"`
	Status        string   `json:"status,omitempty"`
	Length        int      `json:"length,omitempty"`
	TotalTime     float64  `json:"total_time,omitempty"`
	UnicornTime   float64  `json:"unicorn_time,omitempty"`
	HttpHost      string   `json:"http_host,omitempty"`
	UserAgentName string   `json:"user_agent_name,omitempty"`
	Uri           string   `json:"uri,omitempty"`
	Referer       string   `json:"referer,omitempty"`
	Action        string   `json:"action,omitempty"`
	Revision      string   `json:"revision,omitempty"`
	UUID          string   `json:"uuid,omitempty"`
	Etag          string   `json:"etag,omitempty"`
}

var quotesRegexp = regexp.MustCompile(`(ua|uri|ref)="(.*?)"`)

func (line *NginxLine) Parse(raw string) error {
	if line.SyslogLine == nil {
		line.SyslogLine = &SyslogLine{}
	}
	e := line.SyslogLine.Parse(raw)
	if e != nil {
		return e
	}
	if line.Tag != "ssl_endpoint" && line.Tag != "nginx" {
		return fmt.Errorf("tag %q not supported", line.Tag)
	}
	forwarded := false
	for i, field := range line.fields {
		if forwarded && strings.Contains(field, "=") {
			forwarded = false
		}
		parts := strings.SplitN(field, "=", 2)
		if len(parts) == 2 {
			key := parts[0]
			value := parts[1]
			switch key {
			case "rev":
				line.Revision = value
			case "action":
				line.Action = value
			case "nginx":
				if !strings.Contains(line.Raw, "forwarded=") {
					forwarded = true
				}
			case "forwarded":
				forwarded = false
				line.XForwardedFor = []string{parts[1]}
			case "method":
				line.Method = value
			case "uuid":
				line.UUID = filterDash(value)
			case "etag":
				line.Etag = filterDash(value)
			case "status":
				line.Status = value
			case "host":
				line.HttpHost = value
			case "length":
				line.Length, _ = strconv.Atoi(value)
			case "total":
				line.TotalTime, _ = strconv.ParseFloat(value, 64)
			case "unicorn_time":
				line.UnicornTime, _ = strconv.ParseFloat(value, 64)
			}
		} else if i == 2 && strings.HasPrefix(field, "nginx") {
			if !strings.Contains(line.Raw, "forwarded=") {
				forwarded = true
			}
		} else if forwarded {
			if strings.HasPrefix(field, "host") || field == "-" {
				forwarded = false
			} else {
				line.XForwardedFor = append(line.XForwardedFor, strings.TrimSuffix(field, ","))
			}

		}
	}
	quotes := quotesRegexp.FindAllStringSubmatch(raw, -1)
	for _, quote := range quotes {
		switch quote[1] {
		case "ua":
			line.UserAgentName = quote[2]
		case "uri":
			line.Uri = quote[2]
		case "ref":
			line.Referer = filterDash(quote[2])
		default:
		}
	}
	return nil
}

func filterDash(raw string) string {
	if raw == "-" {
		return ""
	}
	return raw
}

type HAProxyLine struct {
	SyslogLine
	Frontend            string
	Backend             string
	BackendHost         string
	BackendImageId      string
	BackendContainerId  string
	Status              string
	Length              int
	ClientRequestTime   int
	ConnectionQueueTime int
	TcpConnectTime      int
	ServerResponseTime  int
	SessionDurationTime int
	ActiveConnections   int
	FrontendConnections int
	BackendConnectons   int
	ServerConnections   int
	Retries             int
	ServerQueue         int
	BackendQueue        int
	Method              string
	Uri                 string
}

func (line *HAProxyLine) Parse(raw string) error {
	e := line.SyslogLine.Parse(raw)
	if e != nil {
		return e
	}
	if line.Tag != "haproxy" {
		return fmt.Errorf("tag was %s", line.Tag)
	}
	if len(line.fields) > 16 {
		line.Frontend = line.fields[5]
		backend := line.fields[6]
		parts := strings.SplitN(backend, "/", 2)
		if len(parts) == 2 {
			line.Backend = parts[0]
			backendContainer := parts[1]
			parts := strings.Split(backendContainer, ":")
			if len(parts) == 3 {
				line.BackendHost = parts[0]
				line.BackendImageId = parts[1]
				line.BackendContainerId = parts[2]
			}
		}
		times := line.fields[7]
		parts = strings.Split(times, "/")
		if len(parts) == 5 {
			line.ClientRequestTime, _ = strconv.Atoi(parts[0])
			line.ConnectionQueueTime, _ = strconv.Atoi(parts[1])
			line.TcpConnectTime, _ = strconv.Atoi(parts[2])
			line.ServerResponseTime, _ = strconv.Atoi(parts[3])
			line.SessionDurationTime, _ = strconv.Atoi(parts[4])
		}
		line.Status = line.fields[8]
		line.Length, _ = strconv.Atoi(line.fields[9])

		connections := line.fields[13]
		parts = strings.Split(connections, "/")
		if len(parts) == 5 {
			line.ActiveConnections, _ = strconv.Atoi(parts[0])
			line.FrontendConnections, _ = strconv.Atoi(parts[1])
			line.BackendConnectons, _ = strconv.Atoi(parts[2])
			line.ServerConnections, _ = strconv.Atoi(parts[3])
			line.Retries, _ = strconv.Atoi(parts[4])
		}

		queues := line.fields[14]
		parts = strings.Split(queues, "/")
		if len(parts) == 2 {
			line.ServerQueue, _ = strconv.Atoi(parts[0])
			line.BackendQueue, _ = strconv.Atoi(parts[1])
		}
		line.Method = line.fields[15][1:]
		line.Uri = line.fields[16]
	}
	return nil
}
<|endoftext|>


<commit_before>// Package stack implements utilities to capture, manipulate, and format call
// stacks. It provides a simpler API than package runtime.
//
// The implementation takes care of the minutia and special cases of
// interpreting the program counter (pc) values returned by runtime.Callers.
//
// Package stack's types implement fmt.Formatter, which provides a simple and
// flexible way to declaratively configure formatting when used with logging
// or error tracking packages.
package stack

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

// Call records a single function invocation from a goroutine stack.
type Call struct {
	fn *runtime.Func
	pc uintptr
}

// Caller returns a Call from the stack of the current goroutine. The argument
// skip is the number of stack frames to ascend, with 0 identifying the
// calling function.
func Caller(skip int) Call {
	var pcs [2]uintptr
	n := runtime.Callers(skip+1, pcs[:])

	var c Call

	if n < 2 {
		return c
	}

	c.pc = pcs[1]
	if runtime.FuncForPC(pcs[0]) != sigpanic {
		c.pc--
	}
	c.fn = runtime.FuncForPC(c.pc)
	return c
}

// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
func (c Call) String() string {
	return fmt.Sprint(c)
}

// MarshalText implements encoding.TextMarshaler. It formats the Call the same
// as fmt.Sprintf("%v", c).
func (c Call) MarshalText() ([]byte, error) {
	if c.fn == nil {
		return nil, ErrNoFunc
	}
	buf := bytes.Buffer{}
	fmt.Fprint(&buf, c)
	return buf.Bytes(), nil
}

// ErrNoFunc means that the Call has a nil *runtime.Func. The most likely
// cause is a Call with the zero value.
var ErrNoFunc = errors.New("no call stack information")

// Format implements fmt.Formatter with support for the following verbs.
//
//    %s    source file
//    %d    line number
//    %n    function name
//    %v    equivalent to %s:%d
//
// It accepts the '+' and '#' flags for most of the verbs as follows.
//
//    %+s   path of source file relative to the compile time GOPATH
//    %#s   full path of source file
//    %+n   import path qualified function name
//    %+v   equivalent to %+s:%d
//    %#v   equivalent to %#s:%d
func (c Call) Format(s fmt.State, verb rune) {
	if c.fn == nil {
		fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
		return
	}

	switch verb {
	case 's', 'v':
		file, line := c.fn.FileLine(c.pc)
		switch {
		case s.Flag('#'):
			// done
		case s.Flag('+'):
			// Here we want to get the source file path relative to the
			// compile time GOPATH. As of Go 1.4.x there is no direct way to
			// know the compiled GOPATH at runtime, but we can infer the
			// number of path segments in the GOPATH. We note that fn.Name()
			// returns the function name qualified by the import path, which
			// does not include the GOPATH. Thus we can trim segments from the
			// beginning of the file path until the number of path separators
			// remaining is one more than the number of path separators in the
			// function name. For example, given:
			//
			//    GOPATH     /home/user
			//    file       /home/user/src/pkg/sub/file.go
			//    fn.Name()  pkg/sub.Type.Method
			//
			// We want to produce:
			//
			//    pkg/sub/file.go
			//
			// From this we can easily see that fn.Name() has one less path
			// separator than our desired output. We count separators from the
			// end of the file path until it finds two more than in the
			// function name and then move one character forward to preserve
			// the initial path segment without a leading separator.
			const sep = "/"
			goal := strings.Count(c.fn.Name(), sep) + 2
			pathCnt := 0
			i := len(file)
			for pathCnt < goal {
				i = strings.LastIndex(file[:i], sep)
				if i == -1 {
					i = -len(sep)
					break
				}
				pathCnt++
			}
			// get back to 0 or trim the leading seperator
			file = file[i+len(sep):]
		default:
			const sep = "/"
			if i := strings.LastIndex(file, sep); i != -1 {
				file = file[i+len(sep):]
			}
		}
		io.WriteString(s, file)
		if verb == 'v' {
			buf := [7]byte{':'}
			s.Write(strconv.AppendInt(buf[:1], int64(line), 10))
		}

	case 'd':
		_, line := c.fn.FileLine(c.pc)
		buf := [6]byte{}
		s.Write(strconv.AppendInt(buf[:0], int64(line), 10))

	case 'n':
		name := c.fn.Name()
		if !s.Flag('+') {
			const pathSep = "/"
			if i := strings.LastIndex(name, pathSep); i != -1 {
				name = name[i+len(pathSep):]
			}
			const pkgSep = "."
			if i := strings.Index(name, pkgSep); i != -1 {
				name = name[i+len(pkgSep):]
			}
		}
		io.WriteString(s, name)
	}
}

// PC returns the program counter for this call frame; multiple frames may
// have the same PC value.
func (c Call) PC() uintptr {
	return c.pc
}

// name returns the import path qualified name of the function containing the
// call.
func (c Call) name() string {
	if c.fn == nil {
		return "???"
	}
	return c.fn.Name()
}

func (c Call) file() string {
	if c.fn == nil {
		return "???"
	}
	file, _ := c.fn.FileLine(c.pc)
	return file
}

func (c Call) line() int {
	if c.fn == nil {
		return 0
	}
	_, line := c.fn.FileLine(c.pc)
	return line
}

// CallStack records a sequence of function invocations from a goroutine
// stack.
type CallStack []Call

// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs).
func (cs CallStack) String() string {
	return fmt.Sprint(cs)
}

var (
	openBracketBytes  = []byte("[")
	closeBracketBytes = []byte("]")
	spaceBytes        = []byte(" ")
)

// MarshalText implements encoding.TextMarshaler. It formats the CallStack the
// same as fmt.Sprintf("%v", cs).
func (cs CallStack) MarshalText() ([]byte, error) {
	buf := bytes.Buffer{}
	buf.Write(openBracketBytes)
	for i, pc := range cs {
		if pc.fn == nil {
			return nil, ErrNoFunc
		}
		if i > 0 {
			buf.Write(spaceBytes)
		}
		fmt.Fprint(&buf, pc)
	}
	buf.Write(closeBracketBytes)
	return buf.Bytes(), nil
}

// Format implements fmt.Formatter by printing the CallStack as square brackes
// ([, ]) surrounding a space separated list of Calls each formatted with the
// supplied verb and options.
func (cs CallStack) Format(s fmt.State, verb rune) {
	s.Write(openBracketBytes)
	for i, pc := range cs {
		if i > 0 {
			s.Write(spaceBytes)
		}
		pc.Format(s, verb)
	}
	s.Write(closeBracketBytes)
}

// findSigpanic intentionally executes faulting code to generate a stack trace
// containing an entry for runtime.sigpanic.
func findSigpanic() *runtime.Func {
	var fn *runtime.Func
	var p *int
	func() int {
		defer func() {
			if p := recover(); p != nil {
				var pcs [512]uintptr
				n := runtime.Callers(2, pcs[:])
				for _, pc := range pcs[:n] {
					f := runtime.FuncForPC(pc)
					if f.Name() == "runtime.sigpanic" {
						fn = f
						break
					}
				}
			}
		}()
		// intentional nil pointer dereference to trigger sigpanic
		return *p
	}()
	return fn
}

var sigpanic = findSigpanic()

// Trace returns a CallStack for the current goroutine with element 0
// identifying the calling function.
func Trace() CallStack {
	var pcs [512]uintptr
	n := runtime.Callers(2, pcs[:])
	cs := make([]Call, n)

	for i, pc := range pcs[:n] {
		pcFix := pc
		if i > 0 && cs[i-1].fn != sigpanic {
			pcFix--
		}
		cs[i] = Call{
			fn: runtime.FuncForPC(pcFix),
			pc: pcFix,
		}
	}

	return cs
}

// TrimBelow returns a slice of the CallStack with all entries below c
// removed.
func (cs CallStack) TrimBelow(c Call) CallStack {
	for len(cs) > 0 && cs[0].pc != c.pc {
		cs = cs[1:]
	}
	return cs
}

// TrimAbove returns a slice of the CallStack with all entries above c
// removed.
func (cs CallStack) TrimAbove(c Call) CallStack {
	for len(cs) > 0 && cs[len(cs)-1].pc != c.pc {
		cs = cs[:len(cs)-1]
	}
	return cs
}

var goroot string

func init() {
	goroot = filepath.ToSlash(runtime.GOROOT())
	if runtime.GOOS == "windows" {
		goroot = strings.ToLower(goroot)
	}
}

func inGoroot(c Call) bool {
	file := c.file()
	if len(file) == 0 || file[0] == '?' {
		return true
	}
	if runtime.GOOS == "windows" {
		file = strings.ToLower(file)
	}
	return strings.HasPrefix(file, goroot) || strings.HasSuffix(file, "/_testmain.go")
}

// TrimRuntime returns a slice of the CallStack with the topmost entries from
// the go runtime removed. It considers any calls originating from unknown
// files, files under GOROOT, or _testmain.go as part of the runtime.
func (cs CallStack) TrimRuntime() CallStack {
	for len(cs) > 0 && inGoroot(cs[len(cs)-1]) {
		cs = cs[:len(cs)-1]
	}
	return cs
}
<commit_msg>Use the compile-time GOROOT instead of the run-time GOROOT.<commit_after>// Package stack implements utilities to capture, manipulate, and format call
// stacks. It provides a simpler API than package runtime.
//
// The implementation takes care of the minutia and special cases of
// interpreting the program counter (pc) values returned by runtime.Callers.
//
// Package stack's types implement fmt.Formatter, which provides a simple and
// flexible way to declaratively configure formatting when used with logging
// or error tracking packages.
package stack

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"runtime"
	"strconv"
	"strings"
)

// Call records a single function invocation from a goroutine stack.
type Call struct {
	fn *runtime.Func
	pc uintptr
}

// Caller returns a Call from the stack of the current goroutine. The argument
// skip is the number of stack frames to ascend, with 0 identifying the
// calling function.
func Caller(skip int) Call {
	var pcs [2]uintptr
	n := runtime.Callers(skip+1, pcs[:])

	var c Call

	if n < 2 {
		return c
	}

	c.pc = pcs[1]
	if runtime.FuncForPC(pcs[0]) != sigpanic {
		c.pc--
	}
	c.fn = runtime.FuncForPC(c.pc)
	return c
}

// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
func (c Call) String() string {
	return fmt.Sprint(c)
}

// MarshalText implements encoding.TextMarshaler. It formats the Call the same
// as fmt.Sprintf("%v", c).
func (c Call) MarshalText() ([]byte, error) {
	if c.fn == nil {
		return nil, ErrNoFunc
	}
	buf := bytes.Buffer{}
	fmt.Fprint(&buf, c)
	return buf.Bytes(), nil
}

// ErrNoFunc means that the Call has a nil *runtime.Func. The most likely
// cause is a Call with the zero value.
var ErrNoFunc = errors.New("no call stack information")

// Format implements fmt.Formatter with support for the following verbs.
//
//    %s    source file
//    %d    line number
//    %n    function name
//    %v    equivalent to %s:%d
//
// It accepts the '+' and '#' flags for most of the verbs as follows.
//
//    %+s   path of source file relative to the compile time GOPATH
//    %#s   full path of source file
//    %+n   import path qualified function name
//    %+v   equivalent to %+s:%d
//    %#v   equivalent to %#s:%d
func (c Call) Format(s fmt.State, verb rune) {
	if c.fn == nil {
		fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
		return
	}

	switch verb {
	case 's', 'v':
		file, line := c.fn.FileLine(c.pc)
		switch {
		case s.Flag('#'):
			// done
		case s.Flag('+'):
			file = file[pkgIndex(file, c.fn.Name()):]
		default:
			const sep = "/"
			if i := strings.LastIndex(file, sep); i != -1 {
				file = file[i+len(sep):]
			}
		}
		io.WriteString(s, file)
		if verb == 'v' {
			buf := [7]byte{':'}
			s.Write(strconv.AppendInt(buf[:1], int64(line), 10))
		}

	case 'd':
		_, line := c.fn.FileLine(c.pc)
		buf := [6]byte{}
		s.Write(strconv.AppendInt(buf[:0], int64(line), 10))

	case 'n':
		name := c.fn.Name()
		if !s.Flag('+') {
			const pathSep = "/"
			if i := strings.LastIndex(name, pathSep); i != -1 {
				name = name[i+len(pathSep):]
			}
			const pkgSep = "."
			if i := strings.Index(name, pkgSep); i != -1 {
				name = name[i+len(pkgSep):]
			}
		}
		io.WriteString(s, name)
	}
}

// PC returns the program counter for this call frame; multiple frames may
// have the same PC value.
func (c Call) PC() uintptr {
	return c.pc
}

// name returns the import path qualified name of the function containing the
// call.
func (c Call) name() string {
	if c.fn == nil {
		return "???"
	}
	return c.fn.Name()
}

func (c Call) file() string {
	if c.fn == nil {
		return "???"
	}
	file, _ := c.fn.FileLine(c.pc)
	return file
}

func (c Call) line() int {
	if c.fn == nil {
		return 0
	}
	_, line := c.fn.FileLine(c.pc)
	return line
}

// CallStack records a sequence of function invocations from a goroutine
// stack.
type CallStack []Call

// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs).
func (cs CallStack) String() string {
	return fmt.Sprint(cs)
}

var (
	openBracketBytes  = []byte("[")
	closeBracketBytes = []byte("]")
	spaceBytes        = []byte(" ")
)

// MarshalText implements encoding.TextMarshaler. It formats the CallStack the
// same as fmt.Sprintf("%v", cs).
func (cs CallStack) MarshalText() ([]byte, error) {
	buf := bytes.Buffer{}
	buf.Write(openBracketBytes)
	for i, pc := range cs {
		if pc.fn == nil {
			return nil, ErrNoFunc
		}
		if i > 0 {
			buf.Write(spaceBytes)
		}
		fmt.Fprint(&buf, pc)
	}
	buf.Write(closeBracketBytes)
	return buf.Bytes(), nil
}

// Format implements fmt.Formatter by printing the CallStack as square brackes
// ([, ]) surrounding a space separated list of Calls each formatted with the
// supplied verb and options.
func (cs CallStack) Format(s fmt.State, verb rune) {
	s.Write(openBracketBytes)
	for i, pc := range cs {
		if i > 0 {
			s.Write(spaceBytes)
		}
		pc.Format(s, verb)
	}
	s.Write(closeBracketBytes)
}

// findSigpanic intentionally executes faulting code to generate a stack trace
// containing an entry for runtime.sigpanic.
func findSigpanic() *runtime.Func {
	var fn *runtime.Func
	var p *int
	func() int {
		defer func() {
			if p := recover(); p != nil {
				var pcs [512]uintptr
				n := runtime.Callers(2, pcs[:])
				for _, pc := range pcs[:n] {
					f := runtime.FuncForPC(pc)
					if f.Name() == "runtime.sigpanic" {
						fn = f
						break
					}
				}
			}
		}()
		// intentional nil pointer dereference to trigger sigpanic
		return *p
	}()
	return fn
}

var sigpanic = findSigpanic()

// Trace returns a CallStack for the current goroutine with element 0
// identifying the calling function.
func Trace() CallStack {
	var pcs [512]uintptr
	n := runtime.Callers(2, pcs[:])
	cs := make([]Call, n)

	for i, pc := range pcs[:n] {
		pcFix := pc
		if i > 0 && cs[i-1].fn != sigpanic {
			pcFix--
		}
		cs[i] = Call{
			fn: runtime.FuncForPC(pcFix),
			pc: pcFix,
		}
	}

	return cs
}

// TrimBelow returns a slice of the CallStack with all entries below c
// removed.
func (cs CallStack) TrimBelow(c Call) CallStack {
	for len(cs) > 0 && cs[0].pc != c.pc {
		cs = cs[1:]
	}
	return cs
}

// TrimAbove returns a slice of the CallStack with all entries above c
// removed.
func (cs CallStack) TrimAbove(c Call) CallStack {
	for len(cs) > 0 && cs[len(cs)-1].pc != c.pc {
		cs = cs[:len(cs)-1]
	}
	return cs
}

// pkgIndex returns the index that results in file[index:] being the path of
// file relative to the compile time GOPATH, and file[:index] being the
// $GOPATH/src/ portion of file. funcName must be the name of a function in
// file as returned by runtime.Func.Name.
func pkgIndex(file, funcName string) int {
	// As of Go 1.6.2 there is no direct way to know the compile time GOPATH
	// at runtime, but we can infer the number of path segments in the GOPATH.
	// We note that runtime.Func.Name() returns the function name qualified by
	// the import path, which does not include the GOPATH. Thus we can trim
	// segments from the beginning of the file path until the number of path
	// separators remaining is one more than the number of path separators in
	// the function name. For example, given:
	//
	//    GOPATH     /home/user
	//    file       /home/user/src/pkg/sub/file.go
	//    fn.Name()  pkg/sub.Type.Method
	//
	// We want to produce:
	//
	//    file[:idx] == /home/user/src/
	//    file[idx:] == pkg/sub/file.go
	//
	// From this we can easily see that fn.Name() has one less path separator
	// than our desired result for file[idx:]. We count separators from the
	// end of the file path until it finds two more than in the function name
	// and then move one character forward to preserve the initial path
	// segment without a leading separator.
	const sep = "/"
	i := len(file)
	for n := strings.Count(funcName, sep) + 2; n > 0; n-- {
		i = strings.LastIndex(file[:i], sep)
		if i == -1 {
			i = -len(sep)
			break
		}
	}
	// get back to 0 or trim the leading seperator
	return i + len(sep)
}

var runtimePath string

func init() {
	var pcs [1]uintptr
	runtime.Callers(0, pcs[:])
	fn := runtime.FuncForPC(pcs[0])
	file, _ := fn.FileLine(pcs[0])

	idx := pkgIndex(file, fn.Name())

	runtimePath = file[:idx]
	if runtime.GOOS == "windows" {
		runtimePath = strings.ToLower(runtimePath)
	}
}

func inGoroot(c Call) bool {
	file := c.file()
	if len(file) == 0 || file[0] == '?' {
		return true
	}
	if runtime.GOOS == "windows" {
		file = strings.ToLower(file)
	}
	return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go")
}

// TrimRuntime returns a slice of the CallStack with the topmost entries from
// the go runtime removed. It considers any calls originating from unknown
// files, files under GOROOT, or _testmain.go as part of the runtime.
func (cs CallStack) TrimRuntime() CallStack {
	for len(cs) > 0 && inGoroot(cs[len(cs)-1]) {
		cs = cs[:len(cs)-1]
	}
	return cs
}
<|endoftext|>