<commit_before>package main

import (
	"crypto/sha1"
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/calmh/ini"
	"github.com/calmh/syncthing/discover"
	flags "github.com/calmh/syncthing/github.com/jessevdk/go-flags"
	"github.com/calmh/syncthing/protocol"
)

type Options struct {
	ConfDir    string           `short:"c" long:"cfg" description:"Configuration directory" default:"~/.syncthing" value-name:"DIR"`
	Listen     string           `short:"l" long:"listen" description:"Listen address" default:":22000" value-name:"ADDR"`
	ReadOnly   bool             `short:"r" long:"ro" description:"Repository is read only"`
	Delete     bool             `short:"d" long:"delete" description:"Delete files deleted from cluster"`
	NoSymlinks bool             `long:"no-symlinks" description:"Don't follow first level symlinks in the repo"`
	Discovery  DiscoveryOptions `group:"Discovery Options"`
	Advanced   AdvancedOptions  `group:"Advanced Options"`
	Debug      DebugOptions     `group:"Debugging Options"`
}

type DebugOptions struct {
	LogSource bool   `long:"log-source"`
	TraceFile bool   `long:"trace-file"`
	TraceNet  bool   `long:"trace-net"`
	TraceIdx  bool   `long:"trace-idx"`
	Profiler  string `long:"profiler" value-name:"ADDR"`
}

type DiscoveryOptions struct {
	ExternalServer      string `long:"ext-server" description:"External discovery server" value-name:"NAME" default:"syncthing.nym.se"`
	ExternalPort        int    `short:"e" long:"ext-port" description:"External listen port" value-name:"PORT" default:"22000"`
	NoExternalDiscovery bool   `short:"n" long:"no-ext-announce" description:"Do not announce presence externally"`
	NoLocalDiscovery    bool   `short:"N" long:"no-local-announce" description:"Do not announce presence locally"`
}

type AdvancedOptions struct {
	RequestsInFlight int           `long:"reqs-in-flight" description:"Parallell in flight requests per file" default:"8" value-name:"REQS"`
	FilesInFlight    int           `long:"files-in-flight" description:"Parallell in flight file pulls" default:"4" value-name:"FILES"`
	ScanInterval     time.Duration `long:"scan-intv" description:"Repository scan interval" default:"60s" value-name:"INTV"`
	ConnInterval     time.Duration `long:"conn-intv" description:"Node reconnect interval" default:"60s" value-name:"INTV"`
}

var opts Options
var Version string = "unknown-dev"

const (
	confDirName  = ".syncthing"
	confFileName = "syncthing.ini"
)

var (
	config    ini.Config
	nodeAddrs = make(map[string][]string)
)

// Options
var (
	ConfDir = path.Join(getHomeDir(), confDirName)
)

func main() {
	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(0)
	}
	if opts.Debug.TraceFile || opts.Debug.TraceIdx || opts.Debug.TraceNet || opts.Debug.LogSource {
		logger = log.New(os.Stderr, "", log.Lshortfile|log.Ldate|log.Ltime|log.Lmicroseconds)
	}
	if strings.HasPrefix(opts.ConfDir, "~/") {
		opts.ConfDir = strings.Replace(opts.ConfDir, "~", getHomeDir(), 1)
	}

	infoln("Version", Version)

	// Ensure that our home directory exists and that we have a certificate and key.

	ensureDir(ConfDir, 0700)
	cert, err := loadCert(ConfDir)
	if err != nil {
		newCertificate(ConfDir)
		cert, err = loadCert(ConfDir)
		fatalErr(err)
	}

	myID := string(certId(cert.Certificate[0]))
	infoln("My ID:", myID)

	if opts.Debug.Profiler != "" {
		go func() {
			err := http.ListenAndServe(opts.Debug.Profiler, nil)
			if err != nil {
				warnln(err)
			}
		}()
	}

	// The TLS configuration is used for both the listening socket and outgoing
	// connections.

	cfg := &tls.Config{
		ClientAuth:         tls.RequestClientCert,
		ServerName:         "syncthing",
		NextProtos:         []string{"bep/1.0"},
		InsecureSkipVerify: true,
		Certificates:       []tls.Certificate{cert},
	}

	// Load the configuration file, if it exists.

	cf, err := os.Open(path.Join(ConfDir, confFileName))
	if err != nil {
		fatalln("No config file")
		config = ini.Config{}
	}
	config = ini.Parse(cf)
	cf.Close()

	var dir = config.Get("repository", "dir")

	// Create a map of desired node connections based on the configuration file
	// directives.

	for nodeID, addrs := range config.OptionMap("nodes") {
		addrs := strings.Fields(addrs)
		nodeAddrs[nodeID] = addrs
	}

	ensureDir(dir, -1)
	m := NewModel(dir)

	// Walk the repository and update the local model before establishing any
	// connections to other nodes.

	infoln("Initial repository scan in progress")
	loadIndex(m)
	updateLocalModel(m)

	// Routine to listen for incoming connections
	infoln("Listening for incoming connections")
	go listen(myID, opts.Listen, m, cfg)

	// Routine to connect out to configured nodes
	infoln("Attempting to connect to other nodes")
	go connect(myID, opts.Listen, nodeAddrs, m, cfg)

	// Routine to pull blocks from other nodes to synchronize the local
	// repository. Does not run when we are in read only (publish only) mode.
	if !opts.ReadOnly {
		infoln("Cleaning out incomplete synchronizations")
		CleanTempFiles(dir)
		okln("Ready to synchronize")
		m.Start()
	}

	// Periodically scan the repository and update the local model.
	// XXX: Should use some fsnotify mechanism.
	go func() {
		for {
			time.Sleep(opts.Advanced.ScanInterval)
			updateLocalModel(m)
		}
	}()

	select {}
}

func listen(myID string, addr string, m *Model, cfg *tls.Config) {
	l, err := tls.Listen("tcp", addr, cfg)
	fatalErr(err)

listen:
	for {
		conn, err := l.Accept()
		if err != nil {
			warnln(err)
			continue
		}

		if opts.Debug.TraceNet {
			debugln("NET: Connect from", conn.RemoteAddr())
		}

		tc := conn.(*tls.Conn)
		err = tc.Handshake()
		if err != nil {
			warnln(err)
			tc.Close()
			continue
		}

		remoteID := certId(tc.ConnectionState().PeerCertificates[0].Raw)

		if remoteID == myID {
			warnf("Connect from myself (%s) - should not happen", remoteID)
			conn.Close()
			continue
		}

		if m.ConnectedTo(remoteID) {
			warnf("Connect from connected node (%s)", remoteID)
		}

		for nodeID := range nodeAddrs {
			if nodeID == remoteID {
				nc := protocol.NewConnection(remoteID, conn, conn, m)
				m.AddNode(nc)
				infoln("Connected to node", remoteID, "(in)")
				continue listen
			}
		}
		conn.Close()
	}
}

func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model, cfg *tls.Config) {
	_, portstr, err := net.SplitHostPort(addr)
	fatalErr(err)
	port, _ := strconv.Atoi(portstr)

	if opts.Discovery.NoLocalDiscovery {
		port = -1
	} else {
		infoln("Sending local discovery announcements")
	}

	if opts.Discovery.NoExternalDiscovery {
		opts.Discovery.ExternalPort = -1
	} else {
		infoln("Sending external discovery announcements")
	}

	disc, err := discover.NewDiscoverer(myID, port, opts.Discovery.ExternalPort, opts.Discovery.ExternalServer)

	if err != nil {
		warnf("No discovery possible (%v)", err)
	}

	for {
	nextNode:
		for nodeID, addrs := range nodeAddrs {
			if nodeID == myID {
				continue
			}
			if m.ConnectedTo(nodeID) {
				continue
			}
			for _, addr := range addrs {
				if addr == "dynamic" {
					var ok bool
					if disc != nil {
						addr, ok = disc.Lookup(nodeID)
					}
					if !ok {
						continue
					}
				}

				if opts.Debug.TraceNet {
					debugln("NET: Dial", nodeID, addr)
				}
				conn, err := tls.Dial("tcp", addr, cfg)
				if err != nil {
					if opts.Debug.TraceNet {
						debugln("NET:", err)
					}
					continue
				}

				remoteID := certId(conn.ConnectionState().PeerCertificates[0].Raw)
				if remoteID != nodeID {
					warnln("Unexpected nodeID", remoteID, "!=", nodeID)
					conn.Close()
					continue
				}

				nc := protocol.NewConnection(nodeID, conn, conn, m)
				m.AddNode(nc)
				infoln("Connected to node", remoteID, "(out)")
				continue nextNode
			}
		}

		time.Sleep(opts.Advanced.ConnInterval)
	}
}

func updateLocalModel(m *Model) {
	files := Walk(m.Dir(), m, !opts.NoSymlinks)
	m.ReplaceLocal(files)
	saveIndex(m)
}

func saveIndex(m *Model) {
	fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
	idxf, err := os.Create(path.Join(ConfDir, fname))
	if err != nil {
		return
	}
	protocol.WriteIndex(idxf, m.ProtocolIndex())
	idxf.Close()
}

func loadIndex(m *Model) {
	fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
	idxf, err := os.Open(path.Join(ConfDir, fname))
	if err != nil {
		return
	}
	defer idxf.Close()

	idx, err := protocol.ReadIndex(idxf)
	if err != nil {
		return
	}
	m.SeedIndex(idx)
}

func ensureDir(dir string, mode int) {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0700)
		fatalErr(err)
	} else if mode >= 0 && err == nil && int(fi.Mode()&0777) != mode {
		err := os.Chmod(dir, os.FileMode(mode))
		fatalErr(err)
	}
}

func getHomeDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		fatalln("No home directory?")
	}
	return home
}
<commit_msg>Change default queue parameters to optimize better for small files<commit_after>package main

import (
	"crypto/sha1"
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/calmh/ini"
	"github.com/calmh/syncthing/discover"
	flags "github.com/calmh/syncthing/github.com/jessevdk/go-flags"
	"github.com/calmh/syncthing/protocol"
)

type Options struct {
	ConfDir    string           `short:"c" long:"cfg" description:"Configuration directory" default:"~/.syncthing" value-name:"DIR"`
	Listen     string           `short:"l" long:"listen" description:"Listen address" default:":22000" value-name:"ADDR"`
	ReadOnly   bool             `short:"r" long:"ro" description:"Repository is read only"`
	Delete     bool             `short:"d" long:"delete" description:"Delete files deleted from cluster"`
	NoSymlinks bool             `long:"no-symlinks" description:"Don't follow first level symlinks in the repo"`
	Discovery  DiscoveryOptions `group:"Discovery Options"`
	Advanced   AdvancedOptions  `group:"Advanced Options"`
	Debug      DebugOptions     `group:"Debugging Options"`
}

type DebugOptions struct {
	LogSource bool   `long:"log-source"`
	TraceFile bool   `long:"trace-file"`
	TraceNet  bool   `long:"trace-net"`
	TraceIdx  bool   `long:"trace-idx"`
	Profiler  string `long:"profiler" value-name:"ADDR"`
}

type DiscoveryOptions struct {
	ExternalServer      string `long:"ext-server" description:"External discovery server" value-name:"NAME" default:"syncthing.nym.se"`
	ExternalPort        int    `short:"e" long:"ext-port" description:"External listen port" value-name:"PORT" default:"22000"`
	NoExternalDiscovery bool   `short:"n" long:"no-ext-announce" description:"Do not announce presence externally"`
	NoLocalDiscovery    bool   `short:"N" long:"no-local-announce" description:"Do not announce presence locally"`
}

type AdvancedOptions struct {
	RequestsInFlight int           `long:"reqs-in-flight" description:"Parallell in flight requests per file" default:"4" value-name:"REQS"`
	FilesInFlight    int           `long:"files-in-flight" description:"Parallell in flight file pulls" default:"8" value-name:"FILES"`
	ScanInterval     time.Duration `long:"scan-intv" description:"Repository scan interval" default:"60s" value-name:"INTV"`
	ConnInterval     time.Duration `long:"conn-intv" description:"Node reconnect interval" default:"60s" value-name:"INTV"`
}

var opts Options
var Version string = "unknown-dev"

const (
	confDirName  = ".syncthing"
	confFileName = "syncthing.ini"
)

var (
	config    ini.Config
	nodeAddrs = make(map[string][]string)
)

// Options
var (
	ConfDir = path.Join(getHomeDir(), confDirName)
)

func main() {
	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(0)
	}
	if opts.Debug.TraceFile || opts.Debug.TraceIdx || opts.Debug.TraceNet || opts.Debug.LogSource {
		logger = log.New(os.Stderr, "", log.Lshortfile|log.Ldate|log.Ltime|log.Lmicroseconds)
	}
	if strings.HasPrefix(opts.ConfDir, "~/") {
		opts.ConfDir = strings.Replace(opts.ConfDir, "~", getHomeDir(), 1)
	}

	infoln("Version", Version)

	// Ensure that our home directory exists and that we have a certificate and key.

	ensureDir(ConfDir, 0700)
	cert, err := loadCert(ConfDir)
	if err != nil {
		newCertificate(ConfDir)
		cert, err = loadCert(ConfDir)
		fatalErr(err)
	}

	myID := string(certId(cert.Certificate[0]))
	infoln("My ID:", myID)

	if opts.Debug.Profiler != "" {
		go func() {
			err := http.ListenAndServe(opts.Debug.Profiler, nil)
			if err != nil {
				warnln(err)
			}
		}()
	}

	// The TLS configuration is used for both the listening socket and outgoing
	// connections.

	cfg := &tls.Config{
		ClientAuth:         tls.RequestClientCert,
		ServerName:         "syncthing",
		NextProtos:         []string{"bep/1.0"},
		InsecureSkipVerify: true,
		Certificates:       []tls.Certificate{cert},
	}

	// Load the configuration file, if it exists.

	cf, err := os.Open(path.Join(ConfDir, confFileName))
	if err != nil {
		fatalln("No config file")
		config = ini.Config{}
	}
	config = ini.Parse(cf)
	cf.Close()

	var dir = config.Get("repository", "dir")

	// Create a map of desired node connections based on the configuration file
	// directives.

	for nodeID, addrs := range config.OptionMap("nodes") {
		addrs := strings.Fields(addrs)
		nodeAddrs[nodeID] = addrs
	}

	ensureDir(dir, -1)
	m := NewModel(dir)

	// Walk the repository and update the local model before establishing any
	// connections to other nodes.

	infoln("Initial repository scan in progress")
	loadIndex(m)
	updateLocalModel(m)

	// Routine to listen for incoming connections
	infoln("Listening for incoming connections")
	go listen(myID, opts.Listen, m, cfg)

	// Routine to connect out to configured nodes
	infoln("Attempting to connect to other nodes")
	go connect(myID, opts.Listen, nodeAddrs, m, cfg)

	// Routine to pull blocks from other nodes to synchronize the local
	// repository. Does not run when we are in read only (publish only) mode.
	if !opts.ReadOnly {
		infoln("Cleaning out incomplete synchronizations")
		CleanTempFiles(dir)
		okln("Ready to synchronize")
		m.Start()
	}

	// Periodically scan the repository and update the local model.
	// XXX: Should use some fsnotify mechanism.
	go func() {
		for {
			time.Sleep(opts.Advanced.ScanInterval)
			updateLocalModel(m)
		}
	}()

	select {}
}

func listen(myID string, addr string, m *Model, cfg *tls.Config) {
	l, err := tls.Listen("tcp", addr, cfg)
	fatalErr(err)

listen:
	for {
		conn, err := l.Accept()
		if err != nil {
			warnln(err)
			continue
		}

		if opts.Debug.TraceNet {
			debugln("NET: Connect from", conn.RemoteAddr())
		}

		tc := conn.(*tls.Conn)
		err = tc.Handshake()
		if err != nil {
			warnln(err)
			tc.Close()
			continue
		}

		remoteID := certId(tc.ConnectionState().PeerCertificates[0].Raw)

		if remoteID == myID {
			warnf("Connect from myself (%s) - should not happen", remoteID)
			conn.Close()
			continue
		}

		if m.ConnectedTo(remoteID) {
			warnf("Connect from connected node (%s)", remoteID)
		}

		for nodeID := range nodeAddrs {
			if nodeID == remoteID {
				nc := protocol.NewConnection(remoteID, conn, conn, m)
				m.AddNode(nc)
				infoln("Connected to node", remoteID, "(in)")
				continue listen
			}
		}
		conn.Close()
	}
}

func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model, cfg *tls.Config) {
	_, portstr, err := net.SplitHostPort(addr)
	fatalErr(err)
	port, _ := strconv.Atoi(portstr)

	if opts.Discovery.NoLocalDiscovery {
		port = -1
	} else {
		infoln("Sending local discovery announcements")
	}

	if opts.Discovery.NoExternalDiscovery {
		opts.Discovery.ExternalPort = -1
	} else {
		infoln("Sending external discovery announcements")
	}

	disc, err := discover.NewDiscoverer(myID, port, opts.Discovery.ExternalPort, opts.Discovery.ExternalServer)

	if err != nil {
		warnf("No discovery possible (%v)", err)
	}

	for {
	nextNode:
		for nodeID, addrs := range nodeAddrs {
			if nodeID == myID {
				continue
			}
			if m.ConnectedTo(nodeID) {
				continue
			}
			for _, addr := range addrs {
				if addr == "dynamic" {
					var ok bool
					if disc != nil {
						addr, ok = disc.Lookup(nodeID)
					}
					if !ok {
						continue
					}
				}

				if opts.Debug.TraceNet {
					debugln("NET: Dial", nodeID, addr)
				}
				conn, err := tls.Dial("tcp", addr, cfg)
				if err != nil {
					if opts.Debug.TraceNet {
						debugln("NET:", err)
					}
					continue
				}

				remoteID := certId(conn.ConnectionState().PeerCertificates[0].Raw)
				if remoteID != nodeID {
					warnln("Unexpected nodeID", remoteID, "!=", nodeID)
					conn.Close()
					continue
				}

				nc := protocol.NewConnection(nodeID, conn, conn, m)
				m.AddNode(nc)
				infoln("Connected to node", remoteID, "(out)")
				continue nextNode
			}
		}

		time.Sleep(opts.Advanced.ConnInterval)
	}
}

func updateLocalModel(m *Model) {
	files := Walk(m.Dir(), m, !opts.NoSymlinks)
	m.ReplaceLocal(files)
	saveIndex(m)
}

func saveIndex(m *Model) {
	fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
	idxf, err := os.Create(path.Join(ConfDir, fname))
	if err != nil {
		return
	}
	protocol.WriteIndex(idxf, m.ProtocolIndex())
	idxf.Close()
}

func loadIndex(m *Model) {
	fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
	idxf, err := os.Open(path.Join(ConfDir, fname))
	if err != nil {
		return
	}
	defer idxf.Close()

	idx, err := protocol.ReadIndex(idxf)
	if err != nil {
		return
	}
	m.SeedIndex(idx)
}

func ensureDir(dir string, mode int) {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0700)
		fatalErr(err)
	} else if mode >= 0 && err == nil && int(fi.Mode()&0777) != mode {
		err := os.Chmod(dir, os.FileMode(mode))
		fatalErr(err)
	}
}

func getHomeDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		fatalln("No home directory?")
	}
	return home
}
<|endoftext|>


<commit_before>package render_test

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/test"
)

func TestOriginTable(t *testing.T) {
	if _, ok := render.OriginTable(test.Report, "not-found"); ok {
		t.Errorf("unknown origin ID gave unexpected success")
	}
	for originID, want := range map[string]render.Table{
		test.ClientAddressNodeID: {
			Title:   "Origin Address",
			Numeric: false,
			Rows: []render.Row{
				{"Address", test.ClientIP, ""},
			},
		},
		test.ServerProcessNodeID: {
			Title:   "Origin Process",
			Numeric: false,
			Rank:    2,
			Rows: []render.Row{
				{"Name (comm)", "apache", ""},
				{"PID", test.ServerPID, ""},
			},
		},
		test.ServerHostNodeID: {
			Title:   "Origin Host",
			Numeric: false,
			Rank:    1,
			Rows: []render.Row{
				{"Host name", test.ServerHostName, ""},
				{"Load", "0.01 0.01 0.01", ""},
				{"Operating system", "Linux", ""},
			},
		},
	} {
		have, ok := render.OriginTable(test.Report, originID)
		if !ok {
			t.Errorf("%q: not OK", originID)
			continue
		}
		if !reflect.DeepEqual(want, have) {
			t.Errorf("%q: %s", originID, test.Diff(want, have))
		}
	}
}

func TestMakeDetailedNode(t *testing.T) {
	renderableNode := render.ContainerRenderer.Render(test.Report)[test.ServerContainerID]
	have := render.MakeDetailedNode(test.Report, renderableNode)
	want := render.DetailedNode{
		ID:         test.ServerContainerID,
		LabelMajor: "server",
		LabelMinor: test.ServerHostName,
		Pseudo:     false,
		Tables: []render.Table{
			{
				Title:   "Connections",
				Numeric: true,
				Rank:    100,
				Rows: []render.Row{
					{"Egress packet rate", "75", "packets/sec"},
					{"Egress byte rate", "750", "Bps"},
				},
			},
			{
				Title:   "Origin Container",
				Numeric: false,
				Rank:    3,
				Rows: []render.Row{
					{"ID", test.ServerContainerID, ""},
					{"Name", "server", ""},
					{"Image ID", test.ServerContainerImageID, ""},
				},
			},
			{
				Title:   "Origin Process",
				Numeric: false,
				Rank:    2,
				Rows: []render.Row{
					{"Name (comm)", "apache", ""},
					{"PID", test.ServerPID, ""},
				},
			},
			{
				Title:   "Origin Host",
				Numeric: false,
				Rank:    1,
				Rows: []render.Row{
					{"Host name", test.ServerHostName, ""},
					{"Load", "0.01 0.01 0.01", ""},
					{"Operating system", "Linux", ""},
				},
			},
			{
				Title:   "Connection Details",
				Numeric: false,
				Rows: []render.Row{
					{"Local", "Remote", ""},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345),
						"",
					},
				},
			},
		},
	}
	if !reflect.DeepEqual(want, have) {
		t.Errorf("%s", test.Diff(want, have))
	}
}
<commit_msg>Add test.<commit_after>package render_test

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/test"
)

func TestOriginTable(t *testing.T) {
	if _, ok := render.OriginTable(test.Report, "not-found"); ok {
		t.Errorf("unknown origin ID gave unexpected success")
	}
	for originID, want := range map[string]render.Table{
		test.ServerProcessNodeID: {
			Title:   "Origin Process",
			Numeric: false,
			Rank:    2,
			Rows: []render.Row{
				{"Name (comm)", "apache", ""},
				{"PID", test.ServerPID, ""},
			},
		},
		test.ServerHostNodeID: {
			Title:   "Origin Host",
			Numeric: false,
			Rank:    1,
			Rows: []render.Row{
				{"Host name", test.ServerHostName, ""},
				{"Load", "0.01 0.01 0.01", ""},
				{"Operating system", "Linux", ""},
			},
		},
	} {
		have, ok := render.OriginTable(test.Report, originID)
		if !ok {
			t.Errorf("%q: not OK", originID)
			continue
		}
		if !reflect.DeepEqual(want, have) {
			t.Errorf("%q: %s", originID, test.Diff(want, have))
		}
	}
}

func TestMakeDetailedHostNode(t *testing.T) {
	renderableNode := render.HostRenderer.Render(test.Report)[render.MakeHostID(test.ClientHostID)]
	have := render.MakeDetailedNode(test.Report, renderableNode)
	want := render.DetailedNode{
		ID:         render.MakeHostID(test.ClientHostID),
		LabelMajor: "client",
		LabelMinor: "hostname.com",
		Pseudo:     false,
		Tables: []render.Table{
			{
				Title:   "Connections",
				Numeric: true,
				Rank:    100,
				Rows: []render.Row{
					{
						Key:        "TCP connections",
						ValueMajor: "3",
						ValueMinor: "",
					},
				},
			},
			{
				Title:   "Origin Host",
				Numeric: false,
				Rank:    1,
				Rows: []render.Row{
					{
						Key:        "Host name",
						ValueMajor: "client.hostname.com",
						ValueMinor: "",
					},
					{
						Key:        "Load",
						ValueMajor: "0.01 0.01 0.01",
						ValueMinor: "",
					},
					{
						Key:        "Operating system",
						ValueMajor: "Linux",
						ValueMinor: "",
					},
				},
			},
			{
				Title:   "Connection Details",
				Numeric: false,
				Rank:    0,
				Rows: []render.Row{
					{
						Key:        "Local",
						ValueMajor: "Remote",
						ValueMinor: "",
					},
					{
						Key:        "10.10.10.20",
						ValueMajor: "192.168.1.1",
						ValueMinor: "",
					},
				},
			},
		},
	}
	if !reflect.DeepEqual(want, have) {
		t.Errorf("%s", test.Diff(want, have))
	}
}

func TestMakeDetailedContainerNode(t *testing.T) {
	renderableNode := render.ContainerRenderer.Render(test.Report)[test.ServerContainerID]
	have := render.MakeDetailedNode(test.Report, renderableNode)
	want := render.DetailedNode{
		ID:         test.ServerContainerID,
		LabelMajor: "server",
		LabelMinor: test.ServerHostName,
		Pseudo:     false,
		Tables: []render.Table{
			{
				Title:   "Connections",
				Numeric: true,
				Rank:    100,
				Rows: []render.Row{
					{"Egress packet rate", "75", "packets/sec"},
					{"Egress byte rate", "750", "Bps"},
				},
			},
			{
				Title:   "Origin Container",
				Numeric: false,
				Rank:    3,
				Rows: []render.Row{
					{"ID", test.ServerContainerID, ""},
					{"Name", "server", ""},
					{"Image ID", test.ServerContainerImageID, ""},
				},
			},
			{
				Title:   "Origin Process",
				Numeric: false,
				Rank:    2,
				Rows: []render.Row{
					{"Name (comm)", "apache", ""},
					{"PID", test.ServerPID, ""},
				},
			},
			{
				Title:   "Origin Host",
				Numeric: false,
				Rank:    1,
				Rows: []render.Row{
					{"Host name", test.ServerHostName, ""},
					{"Load", "0.01 0.01 0.01", ""},
					{"Operating system", "Linux", ""},
				},
			},
			{
				Title:   "Connection Details",
				Numeric: false,
				Rows: []render.Row{
					{"Local", "Remote", ""},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002),
						"",
					},
					{
						fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
						fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345),
						"",
					},
				},
			},
		},
	}
	if !reflect.DeepEqual(want, have) {
		t.Errorf("%s", test.Diff(want, have))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"os"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"

	"github.com/droptheplot/rand_chat/env"
	"github.com/droptheplot/rand_chat/models"
	"github.com/droptheplot/rand_chat/telegram"
	"github.com/droptheplot/rand_chat/vk"
)

var templates = template.Must(template.ParseGlob(env.Config.Templates))
var db = env.Init()

func main() {
	r := mux.NewRouter()

	r.HandleFunc("/", IndexHandler).Methods("GET")
	r.HandleFunc("/api/chart", ChartHandler).Methods("GET")
	r.HandleFunc("/api/telegram", TelegramHandler).Methods("POST")
	r.HandleFunc("/api/vk", VKHandler).Methods("POST")

	http.ListenAndServeTLS(
		":443",
		env.Config.TLS.Cert,
		env.Config.TLS.Key,
		handlers.LoggingHandler(os.Stdout, r),
	)
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "index.html", map[string]string{"Title": "Рандомный чат"})

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func TelegramHandler(w http.ResponseWriter, r *http.Request) {
	var update telegram.Update
	json.NewDecoder(r.Body).Decode(&update)

	models.Message{
		Text: update.Message.Text,
		User: models.User{
			ID:  update.Message.User.ID,
			App: "telegram",
		},
	}.Handle(db)

	w.WriteHeader(http.StatusOK)
}

func VKHandler(w http.ResponseWriter, r *http.Request) {
	var event vk.Event
	json.NewDecoder(r.Body).Decode(&event)

	if event.Type == "confirmation" {
		w.Write([]byte(env.Config.VK.Confirmation))
		return
	}

	if event.Message.Out == 1 {
		w.Write([]byte("ok"))
		return
	}

	models.Message{
		Text: event.Message.Body,
		User: models.User{
			ID:  event.Message.UserID,
			App: "vk",
		},
	}.Handle(db)

	w.Write([]byte("ok"))
}

func ChartHandler(w http.ResponseWriter, r *http.Request) {
	var charts = make(map[string][]interface{})

	for _, chart := range models.GetCharts(db) {
		charts["dates"] = append(charts["dates"], chart.Date.Format("2 Jan"))
		charts["counts"] = append(charts["counts"], chart.Count)
	}

	result, _ := json.Marshal(charts)

	w.Header().Set("Content-Type", "application/json")
	w.Write(result)
}
<commit_msg>Set Strict-Transport-Security for index handler<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"os"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"

	"github.com/droptheplot/rand_chat/env"
	"github.com/droptheplot/rand_chat/models"
	"github.com/droptheplot/rand_chat/telegram"
	"github.com/droptheplot/rand_chat/vk"
)

var templates = template.Must(template.ParseGlob(env.Config.Templates))
var db = env.Init()

func main() {
	r := mux.NewRouter()

	r.HandleFunc("/", IndexHandler).Methods("GET")
	r.HandleFunc("/api/chart", ChartHandler).Methods("GET")
	r.HandleFunc("/api/telegram", TelegramHandler).Methods("POST")
	r.HandleFunc("/api/vk", VKHandler).Methods("POST")

	http.ListenAndServeTLS(
		":443",
		env.Config.TLS.Cert,
		env.Config.TLS.Key,
		handlers.LoggingHandler(os.Stdout, r),
	)
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")

	err := templates.ExecuteTemplate(w, "index.html", map[string]string{"Title": "Рандомный чат"})

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func TelegramHandler(w http.ResponseWriter, r *http.Request) {
	var update telegram.Update
	json.NewDecoder(r.Body).Decode(&update)

	models.Message{
		Text: update.Message.Text,
		User: models.User{
			ID:  update.Message.User.ID,
			App: "telegram",
		},
	}.Handle(db)

	w.WriteHeader(http.StatusOK)
}

func VKHandler(w http.ResponseWriter, r *http.Request) {
	var event vk.Event
	json.NewDecoder(r.Body).Decode(&event)

	if event.Type == "confirmation" {
		w.Write([]byte(env.Config.VK.Confirmation))
		return
	}

	if event.Message.Out == 1 {
		w.Write([]byte("ok"))
		return
	}

	models.Message{
		Text: event.Message.Body,
		User: models.User{
			ID:  event.Message.UserID,
			App: "vk",
		},
	}.Handle(db)

	w.Write([]byte("ok"))
}

func ChartHandler(w http.ResponseWriter, r *http.Request) {
	var charts = make(map[string][]interface{})

	for _, chart := range models.GetCharts(db) {
		charts["dates"] = append(charts["dates"], chart.Date.Format("2 Jan"))
		charts["counts"] = append(charts["counts"], chart.Count)
	}

	result, _ := json.Marshal(charts)

	w.Header().Set("Content-Type", "application/json")
	w.Write(result)
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "time"
  "os"
  "bufio"
  "log"
  "regexp"
  "errors"
  "math/rand"
)

type Posicao struct {
  linha  int
  coluna int
}

type PacGo struct {
  posicao Posicao
  figura  string // emoji
  pilula  bool
  pontos int
}

type Fantasma struct {
  posicao Posicao
  figura  string // emoji
}

type Labirinto struct {
  largura int
  altura  int
  mapa    []string
  figura string
}

type Movimento int

const (
        Cima = iota
        Baixo
        Esquerda
        Direita
        Nenhum
        Sai
)

var labirinto *Labirinto
var pacgo     *PacGo
var lista_de_fantasmas []*Fantasma
var mapaSinais map[int]string

func construirLabirinto(nomeArquivo string) (*Labirinto, *PacGo, []*Fantasma, error) {

  var ErrMapNotFound = errors.New("Não conseguiu ler o arquivo do mapa")

  var arquivo string
  if nomeArquivo == "" {
    arquivo = "./data/mapa.txt"
  } else {
    arquivo = nomeArquivo
  }

  if file, err := os.Open(arquivo); err == nil {

    // fecha depois de ler o arquivo
    defer file.Close()

    // inicializa o mapa vazio
    var pacgo *PacGo
    fantasmas := []*Fantasma{}
    mapa := []string{}

    r, _ := regexp.Compile("[^ #.]")

    // cria um leitor para ler linha a linha o arquivo
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
      linha := scanner.Text()

      for indice , caracter := range linha {
        switch caracter {
          case 'F': {
            fantasma := &Fantasma{ posicao: Posicao{len(mapa), indice}, figura: "\xF0\x9F\x91\xBB"}
            fantasmas = append(fantasmas, fantasma)
          }
          case 'G': pacgo = &PacGo{ posicao: Posicao{len(mapa), indice}, figura: "\xF0\x9F\x98\x83", pontos : 0 }
        }
      }

      linha = r.ReplaceAllString(linha, " ")
      mapa = append(mapa, linha)
    }

    // verifica se teve erro o leitor
    if err = scanner.Err(); err != nil {
      log.Fatal(err)
      return nil, nil, nil, ErrMapNotFound
    }

    l := &Labirinto{largura: len(mapa[0]), altura: len(mapa), mapa : mapa, figura: "\x1b[44m \x1b[0m"}
    return l, pacgo, fantasmas, nil

  } else {
    log.Fatal(err)
    return nil, nil, nil, ErrMapNotFound
  }
}

func atualizarLabirinto() {
  limpaTela()

  // Imprime os pontos
  moveCursor(Posicao{0,0})
  fmt.Printf("%sPontos: %d%s\n", "\x1b[31;1m", pacgo.pontos, "\x1b[0m")

  posicaoInicial := Posicao{2,0}
  moveCursor(posicaoInicial)
  for _, linha := range labirinto.mapa {
    for _, char := range linha {
      switch char {
        case '#': fmt.Print(labirinto.figura)
        case '.': fmt.Print("·")
        default:  fmt.Print(" ")
      }
    }
    fmt.Println("")
  }

  // Imprime PacGo
  moveCursor(posicaoInicial.adiciona(&pacgo.posicao))
  fmt.Printf("%s", pacgo.figura)

  // Imprime fantasmas
  for _, fantasma := range lista_de_fantasmas {
    moveCursor(posicaoInicial.adiciona(&fantasma.posicao))
    fmt.Printf("%s", fantasma.figura)
  }

  // Move o cursor para fora do labirinto
  moveCursor(posicaoInicial.adiciona(&Posicao{labirinto.altura + 2, 0}))
}

func detectarColisao() bool {
  for _, fantasma := range lista_de_fantasmas {
    if fantasma.posicao == pacgo.posicao {
      return true
    }
  }
  return false
}

func moverPacGo(m Movimento) {
  var novaLinha = pacgo.posicao.linha
  var novaColuna = pacgo.posicao.coluna

  switch m {
    case Cima:
      novaLinha--
      if novaLinha < 0 {
        novaLinha = labirinto.altura - 1
      }
    case Baixo:
      novaLinha++
      if novaLinha >= labirinto.altura {
        novaLinha = 0
      }
    case Direita:
      novaColuna++
      if novaColuna >= labirinto.largura {
        novaColuna = 0
      }
    case Esquerda:
      novaColuna--
      if novaColuna < 0 {
        novaColuna = labirinto.largura - 1
      }
  }

  conteudoDoMapa := labirinto.mapa[novaLinha][novaColuna]
  if conteudoDoMapa != '#' {
    pacgo.posicao.linha = novaLinha
    pacgo.posicao.coluna = novaColuna

    if conteudoDoMapa == '.' {
      pacgo.pontos += 10
      linha := labirinto.mapa[novaLinha]
      linha = linha[:novaColuna] + " " + linha[novaColuna+1:]
      labirinto.mapa[novaLinha] = linha
    }
  }
}

func random(min, max int) int {
    return rand.Intn(max - min) + min
}

func move(fantasma *Fantasma, valorDaPosicaoAtualDoFantasma byte, linhaAtualDoFantasma int, colunaAtualDoFantasma int){

  var direcao = random(0, 4)
  var sinal = mapaSinais[direcao]
  //fmt.Println(sinal)
  switch sinal {
  case "Cima":
              if linhaAtualDoFantasma == 0{
                if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = labirinto.altura - 1
                 }
             }else{
               var posicaoAcimaDoFantasma = labirinto.mapa[fantasma.posicao.linha - 1][fantasma.posicao.coluna]
               if posicaoAcimaDoFantasma != '#'{
                 fantasma.posicao.linha = fantasma.posicao.linha - 1
               }
             }
  case "Baixo":
              if linhaAtualDoFantasma == labirinto.altura - 1{
                 if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = 0
                 }
              }else{
                var posicaoAbaixoDoFantasma = labirinto.mapa[fantasma.posicao.linha + 1][fantasma.posicao.coluna]
                if posicaoAbaixoDoFantasma != '#'{
                  fantasma.posicao.linha = fantasma.posicao.linha + 1
                }
              }
  case "Direita":
                if colunaAtualDoFantasma == labirinto.largura-1{
                  if valorDaPosicaoAtualDoFantasma == ' '{
                    fantasma.posicao.coluna = 0
                  }
                }else{
                  var posicaoDireitaDofantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna + 1]
                  if posicaoDireitaDofantasma != '#'{
                    fantasma.posicao.coluna = fantasma.posicao.coluna + 1
                  }
                }
  case "Esquerda":
                 if colunaAtualDoFantasma == 0{
                   if valorDaPosicaoAtualDoFantasma == ' '{
                     fantasma.posicao.coluna = labirinto.largura - 1
                   }
                 }else{
                   var posicaoEsquerdaDoFantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna - 1]
                   if posicaoEsquerdaDoFantasma != '#'{
                     fantasma.posicao.coluna = fantasma.posicao.coluna - 1
                   }
                 }
  }
}

func moverFantasmas() {

  for {
    for i := 0; i < len(lista_de_fantasmas); i++{
        var valorDaPosicaoAtualDoFantasma = labirinto.mapa[lista_de_fantasmas[i].posicao.linha][lista_de_fantasmas[i].posicao.coluna]
        var linhaAtualDoFantasma = lista_de_fantasmas[i].posicao.linha
        var colunaAtualDoFantasma = lista_de_fantasmas[i].posicao.coluna
        //fmt.Println(valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
        move(lista_de_fantasmas[i], valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
    }
    dorme(200)
  }
}

func dorme(milisegundos time.Duration) {
  time.Sleep(time.Millisecond * milisegundos)
}

func entradaDoUsuario(canal chan<- Movimento) {
  array := make([]byte, 10)

  for {
    lido, _ := os.Stdin.Read(array)

    if lido == 1 && array[0] == 0x1b {
      canal <- Sai;
    } else if lido == 3 {
      if array[0] == 0x1b && array[1] == '[' {
        switch array[2] {
        case 'A': canal <- Cima
        case 'B': canal <- Baixo
        case 'C': canal <- Direita
        case 'D': canal <- Esquerda
        }
      }
    }
  }
}

func ativarPilula() {
  pacgo.pilula = true
  go desativarPilula(3000)
}

func desativarPilula(milisegundos time.Duration) {
  dorme(milisegundos)
  pacgo.pilula = false
}

func terminarJogo() {
  // pacgo morreu :(
  moveCursor( Posicao{labirinto.altura + 2, 0} )
  fmt.Println("Fim de jogo! Os fantasmas venceram... \xF0\x9F\x98\xAD")
}

func main() {
  inicializa()
  defer finaliza()

  mapaSinais = make(map[int]string)
  mapaSinais[0] = "Cima"
  mapaSinais[1] = "Baixo"
  mapaSinais[2] = "Direita"
  mapaSinais[3] = "Esquerda"

  args    := os.Args[1:]
  var arquivo string
  if len(args) >= 1 {
    arquivo = args[0]
  } else {
    arquivo = ""
  }

  labirinto, pacgo, lista_de_fantasmas, _ = construirLabirinto(arquivo)

  canal := make(chan Movimento, 10)

  // Processos assincronos
  go entradaDoUsuario(canal)
  go moverFantasmas()

  var tecla Movimento
  for  {
    atualizarLabirinto()

    // canal não-bloqueador
    select {
    case tecla = <-canal:
        moverPacGo(tecla)
    default:
    }
    if tecla == Sai { break }

    if detectarColisao() {
      terminarJogo()
      break;
    }

    dorme(100)
  }
}
<commit_msg>adiona super pastilhas<commit_after>package main

import (
  "fmt"
  "time"
  "os"
  "bufio"
  "log"
  "regexp"
  "errors"
  "math/rand"
)

type Posicao struct {
  linha  int
  coluna int
}

type PacGo struct {
  posicao Posicao
  figura  string // emoji
  pilula  bool
  pontos int
}

type Fantasma struct {
  posicao Posicao
  figura  string // emoji
}

type Labirinto struct {
  largura int
  altura  int
  mapa    []string
  figMuro string
  figSP   string
}

type Movimento int

const (
        Cima = iota
        Baixo
        Esquerda
        Direita
        Nenhum
        Sai
)

var labirinto *Labirinto
var pacgo     *PacGo
var lista_de_fantasmas []*Fantasma
var mapaSinais map[int]string

func construirLabirinto(nomeArquivo string) (*Labirinto, *PacGo, []*Fantasma, error) {

  var ErrMapNotFound = errors.New("Não conseguiu ler o arquivo do mapa")

  var arquivo string
  if nomeArquivo == "" {
    arquivo = "./data/mapa.txt"
  } else {
    arquivo = nomeArquivo
  }

  if file, err := os.Open(arquivo); err == nil {

    // fecha depois de ler o arquivo
    defer file.Close()

    // inicializa o mapa vazio
    var pacgo *PacGo
    fantasmas := []*Fantasma{}
    mapa := []string{}

    r, _ := regexp.Compile("[^ #.P]")

    // cria um leitor para ler linha a linha o arquivo
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
      linha := scanner.Text()

      for indice , caracter := range linha {
        switch caracter {
          case 'F': {
            fantasma := &Fantasma{ posicao: Posicao{len(mapa), indice}, figura: "\xF0\x9F\x91\xBB"}
            fantasmas = append(fantasmas, fantasma)
          }
          case 'G': pacgo = &PacGo{ posicao: Posicao{len(mapa), indice}, figura: "\xF0\x9F\x98\x83", pontos : 0 }
        }
      }

      linha = r.ReplaceAllString(linha, " ")
      mapa = append(mapa, linha)
    }

    // verifica se teve erro o leitor
    if err = scanner.Err(); err != nil {
      log.Fatal(err)
      return nil, nil, nil, ErrMapNotFound
    }

    l := &Labirinto{largura: len(mapa[0]), altura: len(mapa), mapa : mapa, figMuro: "\x1b[44m \x1b[0m", figSP: "\xF0\x9F\x8D\x84"}
    return l, pacgo, fantasmas, nil

  } else {
    log.Fatal(err)
    return nil, nil, nil, ErrMapNotFound
  }
}

func atualizarLabirinto() {
  limpaTela()

  // Imprime os pontos
  moveCursor(Posicao{0,0})
  fmt.Printf("%sPontos: %d%s\n", "\x1b[31;1m", pacgo.pontos, "\x1b[0m")

  posicaoInicial := Posicao{2,0}
  moveCursor(posicaoInicial)
  for _, linha := range labirinto.mapa {
    for _, char := range linha {
      switch char {
        case '#': fmt.Print(labirinto.figMuro)
        case '.': fmt.Print(".")
        case 'P': fmt.Print(labirinto.figSP)
        default:  fmt.Print(" ")
      }
    }
    fmt.Println("")
  }

  // Imprime PacGo
  moveCursor(posicaoInicial.adiciona(&pacgo.posicao))
  fmt.Printf("%s", pacgo.figura)

  // Imprime fantasmas
  for _, fantasma := range lista_de_fantasmas {
    moveCursor(posicaoInicial.adiciona(&fantasma.posicao))
    fmt.Printf("%s", fantasma.figura)
  }

  // Move o cursor para fora do labirinto
  moveCursor(posicaoInicial.adiciona(&Posicao{labirinto.altura + 2, 0}))
}

func detectarColisao() bool {
  for _, fantasma := range lista_de_fantasmas {
    if fantasma.posicao == pacgo.posicao {
      return true
    }
  }
  return false
}

func moverPacGo(m Movimento) {
  var novaLinha = pacgo.posicao.linha
  var novaColuna = pacgo.posicao.coluna

  switch m {
    case Cima:
      novaLinha--
      if novaLinha < 0 {
        novaLinha = labirinto.altura - 1
      }
    case Baixo:
      novaLinha++
      if novaLinha >= labirinto.altura {
        novaLinha = 0
      }
    case Direita:
      novaColuna++
      if novaColuna >= labirinto.largura {
        novaColuna = 0
      }
    case Esquerda:
      novaColuna--
      if novaColuna < 0 {
        novaColuna = labirinto.largura - 1
      }
  }

  conteudoDoMapa := labirinto.mapa[novaLinha][novaColuna]
  if conteudoDoMapa != '#' {
    pacgo.posicao.linha = novaLinha
    pacgo.posicao.coluna = novaColuna

    if (conteudoDoMapa == '.') || (conteudoDoMapa == 'P') {
      if (conteudoDoMapa == '.') {
        pacgo.pontos += 10
      } else {
        pacgo.pontos += 100
      }

      linha := labirinto.mapa[novaLinha]
      linha = linha[:novaColuna] + " " + linha[novaColuna+1:]
      labirinto.mapa[novaLinha] = linha
    }
  }
}

func random(min, max int) int {
    return rand.Intn(max - min) + min
}

func move(fantasma *Fantasma, valorDaPosicaoAtualDoFantasma byte, linhaAtualDoFantasma int, colunaAtualDoFantasma int){

  var direcao = random(0, 4)
  var sinal = mapaSinais[direcao]
  //fmt.Println(sinal)
  switch sinal {
  case "Cima":
              if linhaAtualDoFantasma == 0{
                if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = labirinto.altura - 1
                 }
             }else{
               var posicaoAcimaDoFantasma = labirinto.mapa[fantasma.posicao.linha - 1][fantasma.posicao.coluna]
               if posicaoAcimaDoFantasma != '#'{
                 fantasma.posicao.linha = fantasma.posicao.linha - 1
               }
             }
  case "Baixo":
              if linhaAtualDoFantasma == labirinto.altura - 1{
                 if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = 0
                 }
              }else{
                var posicaoAbaixoDoFantasma = labirinto.mapa[fantasma.posicao.linha + 1][fantasma.posicao.coluna]
                if posicaoAbaixoDoFantasma != '#'{
                  fantasma.posicao.linha = fantasma.posicao.linha + 1
                }
              }
  case "Direita":
                if colunaAtualDoFantasma == labirinto.largura-1{
                  if valorDaPosicaoAtualDoFantasma == ' '{
                    fantasma.posicao.coluna = 0
                  }
                }else{
                  var posicaoDireitaDofantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna + 1]
                  if posicaoDireitaDofantasma != '#'{
                    fantasma.posicao.coluna = fantasma.posicao.coluna + 1
                  }
                }
  case "Esquerda":
                 if colunaAtualDoFantasma == 0{
                   if valorDaPosicaoAtualDoFantasma == ' '{
                     fantasma.posicao.coluna = labirinto.largura - 1
                   }
                 }else{
                   var posicaoEsquerdaDoFantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna - 1]
                   if posicaoEsquerdaDoFantasma != '#'{
                     fantasma.posicao.coluna = fantasma.posicao.coluna - 1
                   }
                 }
  }
}

func moverFantasmas() {

  for {
    for i := 0; i < len(lista_de_fantasmas); i++{
        var valorDaPosicaoAtualDoFantasma = labirinto.mapa[lista_de_fantasmas[i].posicao.linha][lista_de_fantasmas[i].posicao.coluna]
        var linhaAtualDoFantasma = lista_de_fantasmas[i].posicao.linha
        var colunaAtualDoFantasma = lista_de_fantasmas[i].posicao.coluna
        //fmt.Println(valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
        move(lista_de_fantasmas[i], valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
    }
    dorme(200)
  }
}

func dorme(milisegundos time.Duration) {
  time.Sleep(time.Millisecond * milisegundos)
}

func entradaDoUsuario(canal chan<- Movimento) {
  array := make([]byte, 10)

  for {
    lido, _ := os.Stdin.Read(array)

    if lido == 1 && array[0] == 0x1b {
      canal <- Sai;
    } else if lido == 3 {
      if array[0] == 0x1b && array[1] == '[' {
        switch array[2] {
        case 'A': canal <- Cima
        case 'B': canal <- Baixo
        case 'C': canal <- Direita
        case 'D': canal <- Esquerda
        }
      }
    }
  }
}

func ativarPilula() {
  pacgo.pilula = true
  go desativarPilula(3000)
}

func desativarPilula(milisegundos time.Duration) {
  dorme(milisegundos)
  pacgo.pilula = false
}

func terminarJogo() {
  // pacgo morreu :(
  moveCursor( Posicao{labirinto.altura + 2, 0} )
  fmt.Println("Fim de jogo! Os fantasmas venceram... \xF0\x9F\x98\xAD")
}

func main() {
  inicializa()
  defer finaliza()

  mapaSinais = make(map[int]string)
  mapaSinais[0] = "Cima"
  mapaSinais[1] = "Baixo"
  mapaSinais[2] = "Direita"
  mapaSinais[3] = "Esquerda"

  args    := os.Args[1:]
  var arquivo string
  if len(args) >= 1 {
    arquivo = args[0]
  } else {
    arquivo = ""
  }

  labirinto, pacgo, lista_de_fantasmas, _ = construirLabirinto(arquivo)

  canal := make(chan Movimento, 10)

  // Processos assincronos
  go entradaDoUsuario(canal)
  go moverFantasmas()

  var tecla Movimento
  for  {
    atualizarLabirinto()

    // canal não-bloqueador
    select {
    case tecla = <-canal:
        moverPacGo(tecla)
    default:
    }
    if tecla == Sai { break }

    if detectarColisao() {
      terminarJogo()
      break;
    }

    dorme(100)
  }
}
<|endoftext|>


<commit_before>package irc

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/sorcix/irc.v2"
)

type ClientConfiguration struct {
	Name                 string `json:"name"`
	Host                 string `json:"host"`
	Port                 int    `json:"port"`
	IsTLS                bool   `json:"is_tls"`
	SkipCertificateCheck bool   `json:"skip_certificate_check,omitempty"`

	Nick           string   `json:"nick"`
	RealName       string   `json:"real_name"`
	ServerPass     string   `json:"server_pass,omitempty"`
	OnConnect      []string `json:"on_connect"`
	RejoinExisting bool     `json:"rejoin_existing"`
}

type Client struct {
	ClientConfiguration

	conn *irc.Conn
	mux  sync.Mutex

	buffers map[string]buffer

	directory string
}

type buffer struct {
	ch     chan *irc.Message
	client *Client
	path   string

	name  string
	topic string
	users map[string]string
}

const serverBufferName = "$server"

func NewClient(baseDir string, cfg ClientConfiguration) *Client {
	client := &Client{
		ClientConfiguration: cfg,

		directory: filepath.Join(baseDir, cfg.Name),
		buffers:   make(map[string]buffer),
	}

	return client
}

func (c *Client) connect() (*net.Conn, error) {
	c.serverBuffer().writeInfoMessage("connecting ...")

	server := fmt.Sprintf("%s:%d", c.Host, c.Port)

	conn, err := net.Dial("tcp", server)
	if err != nil {
		return nil, err
	}

	tcpc := conn.(*net.TCPConn)
	if err = tcpc.SetKeepAlive(true); err != nil {
		return nil, err
	}
	if err = tcpc.SetKeepAlivePeriod(5 * time.Minute); err != nil {
		return nil, err
	}

	if c.IsTLS {
		conn = tls.Client(conn, &tls.Config{
			ServerName:         c.Host,
			InsecureSkipVerify: c.SkipCertificateCheck,
		})
	}

	return &conn, nil
}

func (c *Client) Initialize() error {
	conn, err := c.connect()
	if err != nil {
		return err
	}

	c.conn = irc.NewConn(*conn)

	if c.ServerPass != "" {
		c.send("PASS", c.ServerPass)
	}

	r := c.RealName
	if r == "" {
		r = c.Nick
	}

	c.send("NICK", c.Nick)
	c.send("USER", c.Nick, "*", "*", c.RealName)

	return nil
}

func (c *Client) send(cmd string, params ...string) {
	msg := &irc.Message{
		Command: cmd,
		Params:  params,
	}

	c.conn.Encode(msg)

	fmt.Printf("[%s] --> %+v\n", c.Name, msg)
}

func (c *Client) sendRaw(msg string) {
	c.conn.Write([]byte(msg))
	fmt.Printf("[%s] --> %+v\n", c.Name, msg)
}

func (c *Client) listExistingChannels() []string {
	files, err := ioutil.ReadDir(c.directory)
	if err != nil {
		log.Fatal(err)
	}

	channels := []string{}
	for _, file := range files {
		name := file.Name()
		if isChannel(name) {
			channels = append(channels, name)
		}
	}

	return channels
}

func (b *buffer) writeInfoMessage(msg string) {
	b.ch <- &irc.Message{
		Prefix:  &irc.Prefix{Name: "uex"},
		Command: "*",
		Params:  []string{msg},
	}

}

func (c *Client) handleMessage(msg *irc.Message) {
	buf := c.getBuffer(serverBufferName)

	switch msg.Command {
	case irc.RPL_WELCOME:
		for _, msg := range c.OnConnect {
			c.sendRaw(msg)
		}

		if c.RejoinExisting {
			for _, ch := range c.listExistingChannels() {
				c.send(irc.JOIN, ch)
			}
		}

	case irc.PING:
		c.send(irc.PONG, msg.Params...)

	case irc.PONG:
		if len(msg.Params) != 2 {
			break
		}

		s := strings.SplitN(msg.Params[1], " ", 2)
		if len(s) != 2 {
			break
		}

		if ts, err := strconv.ParseInt(s[1], 10, 64); err == nil {
			delta := time.Duration(time.Now().UnixNano()-ts) / time.Millisecond
			text := fmt.Sprintf("PONG from %s: %d ms", msg.Params[0], delta)

			buf.writeInfoMessage(text)
		}

	case irc.NICK:
		from := msg.Prefix.Name
		to := msg.Params[0]

		if from == c.Nick {
			c.Nick = to

			text := fmt.Sprintf("changed nick from %s to %s", from, to)
			buf.writeInfoMessage(text)
		} else {
			// TODO: broadcast renames to all bufs having that user?
			// requires more tracking
			buf = nil
		}

	case irc.JOIN:
		buf = c.getBuffer(msg.Params[0])

	case irc.PRIVMSG, irc.NOTICE:
		target := msg.Params[0]

		// Group all messages sent by the server together,
		// regardless of server name.
		//
		// For direct messages, we want to look at the sender.
		if msg.Prefix.IsServer() {
			target = serverBufferName
		} else if !isChannel(target) {
			target = msg.Prefix.Name
		}

		buf = c.getBuffer(target)

	case irc.RPL_TOPIC:
		target := msg.Params[1]
		topic := msg.Params[2]

		c.getBuffer(target).topic = topic

	case irc.ERR_NICKNAMEINUSE:
		c.Nick = c.Nick + "`"
		fmt.Printf("Nick in use, trying '%s'\n", c.Nick)
		c.send("NICK", c.Nick)
	}

	if buf != nil {
		buf.ch <- msg
	}
}

func (c *Client) serverBuffer() *buffer {
	return c.getBuffer(serverBufferName)
}

func (c *Client) getBuffer(name string) *buffer {
	c.mux.Lock()
	defer c.mux.Unlock()

	// Sent early on, at least by freenode.
	if name == "*" {
		name = serverBufferName
	}

	if buf, exists := c.buffers[name]; exists {
		return &buf
	}

	path := c.directory

	// We want to write __in, __out top level for the server, and
	// as a child for every other buffer.
	if name != serverBufferName {
		path = filepath.Join(path, name)
	}

	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		log.Fatal(err)
	}

	c.buffers[name] = buffer{
		ch:     make(chan *irc.Message),
		client: c,
		path:   path,

		name:  name,
		topic: "",
		users: make(map[string]string),
	}

	b := c.buffers[name]

	go b.inputHandler()
	go b.outputHandler()

	return &b
}

func (b *buffer) outputHandler() {
	name := filepath.Join(b.path, "__out")
	mode := os.O_APPEND | os.O_RDWR | os.O_CREATE
	file, err := os.OpenFile(name, mode, 0644)
	if err != nil {
		log.Fatalf("failed to create output file: %+v\n", err)
	}

	defer file.Close()

	// TODO: better serialization?? colors?? etc.
	for msg := range b.ch {
		text := formatMessage(msg)
		if text == "" {
			continue
		}

		if _, err := file.WriteString(text + "\n"); err != nil {
			log.Fatal(err)
		}
		if err := file.Sync(); err != nil {
			log.Fatal(err)
		}
	}
}

func (b *buffer) inputHandler() {
	name := filepath.Join(b.path, "__in")
	err := syscall.Mkfifo(name, 0777)

	// Doesn't matter if the FIFO already exists from a previous run.
	if err != nil && err != syscall.EEXIST {
		log.Fatal(err)
	}

	for {
		buf, err := ioutil.ReadFile(name)
		if err != nil {
			log.Fatal(err)
		}

		if len(buf) == 0 {
			time.Sleep(100 * time.Millisecond)
			continue
		}

		lines := strings.Split(string(buf), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}

			b.client.handleInputLine(b.name, line)
		}
	}
}

func isChannel(target string) bool {
	if target == "" {
		return false
	}

	return target[0] == '#' || target[0] == '&'
}

func splitInputCommand(bufName, line string) (string, string) {
	// Without a prefix, it's just a regular PRIVMSG
	if !strings.HasPrefix(line, "/") {
		return "/msg", bufName + " " + line
	}
	// Double slash at start means privmsg with leading slash
	if strings.HasPrefix(line, "//") {
		return "/msg", bufName + " " + line[1:]
	}

	s := strings.SplitN(line, " ", 2)
	cmd := strings.ToLower(s[0])

	if len(s) == 1 {
		return cmd, ""
	}

	return cmd, s[1]
}

func (c *Client) handleInputLine(bufName, line string) {
	fmt.Printf("[%s/%s] >> %s\n", c.Name, bufName, line)

	cmd, rest := splitInputCommand(bufName, line)

	switch cmd {
	case "/m", "/msg":
		s := strings.SplitN(rest, " ", 2)
		if len(s) != 2 {
			c.serverBuffer().writeInfoMessage("expected: /msg TARGET MESSAGE")
			return
		} else if s[0] == serverBufferName {
			c.serverBuffer().writeInfoMessage("can't PRIVMSG a server.")
			return
		}

		buf := c.getBuffer(s[0])

		// TODO: pull this out into simple `buf.AddMessage` or so
		buf.ch <- &irc.Message{
			Prefix:  &irc.Prefix{Name: c.Nick},
			Command: irc.PRIVMSG,
			Params:  []string{s[1]},
		}

		c.send("PRIVMSG", s[0], s[1])

	case "/j", "/join":
		if !isChannel(rest) {
			c.getBuffer(bufName).writeInfoMessage("expected: /join TARGET")
			return
		}
		c.send("JOIN", rest)

	case "/l", "/list":
		buf := c.getBuffer(bufName)

		buf.writeInfoMessage("~~ buffers ~~")
		for k, _ := range c.buffers {
			buf.writeInfoMessage(" " + k)
		}

	case "/ping":
		ts := time.Now().UnixNano()
		c.send("PING", fmt.Sprintf("%s %d", bufName, ts))

	case "/quote":
		params := strings.Split(rest, " ")
		if len(params) == 1 {
			c.send(params[0])
		} else {
			c.send(params[0], params[1:]...)
		}

	case "/r", "/reconnect":
		c.serverBuffer().writeInfoMessage("... disconnecting")
		if err := c.conn.Close(); err != nil {
			fmt.Printf("failed to close: %+v\n", err)
		}

	default:
		text := fmt.Sprintf("Unknown command: %s %s", cmd, rest)
		c.getBuffer(bufName).writeInfoMessage(text)
	}
}

func (c *Client) RunLoop() error {
	for {
		message, err := c.conn.Decode()
		if err != nil {
			return err
		}

		fmt.Printf("[%s] <-- %+v\n", c.Name, message)
		c.handleMessage(message)
	}
}
<commit_msg>Normalize buffer naming<commit_after>package irc

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/sorcix/irc.v2"
	"unicode"
)

type ClientConfiguration struct {
	Name                 string `json:"name"`
	Host                 string `json:"host"`
	Port                 int    `json:"port"`
	IsTLS                bool   `json:"is_tls"`
	SkipCertificateCheck bool   `json:"skip_certificate_check,omitempty"`

	Nick           string   `json:"nick"`
	RealName       string   `json:"real_name"`
	ServerPass     string   `json:"server_pass,omitempty"`
	OnConnect      []string `json:"on_connect"`
	RejoinExisting bool     `json:"rejoin_existing"`
}

type Client struct {
	ClientConfiguration

	conn *irc.Conn
	mux  sync.Mutex

	buffers map[string]buffer

	directory string
}

type buffer struct {
	ch     chan *irc.Message
	client *Client
	path   string

	name  string
	topic string
	users map[string]string
}

const serverBufferName = "$server"

func NewClient(baseDir string, cfg ClientConfiguration) *Client {
	client := &Client{
		ClientConfiguration: cfg,

		directory: filepath.Join(baseDir, cfg.Name),
		buffers:   make(map[string]buffer),
	}

	return client
}

func (c *Client) connect() (*net.Conn, error) {
	c.serverBuffer().writeInfoMessage("connecting ...")

	server := fmt.Sprintf("%s:%d", c.Host, c.Port)

	conn, err := net.Dial("tcp", server)
	if err != nil {
		return nil, err
	}

	tcpc := conn.(*net.TCPConn)
	if err = tcpc.SetKeepAlive(true); err != nil {
		return nil, err
	}
	if err = tcpc.SetKeepAlivePeriod(5 * time.Minute); err != nil {
		return nil, err
	}

	if c.IsTLS {
		conn = tls.Client(conn, &tls.Config{
			ServerName:         c.Host,
			InsecureSkipVerify: c.SkipCertificateCheck,
		})
	}

	return &conn, nil
}

func (c *Client) Initialize() error {
	conn, err := c.connect()
	if err != nil {
		return err
	}

	c.conn = irc.NewConn(*conn)

	if c.ServerPass != "" {
		c.send("PASS", c.ServerPass)
	}

	realName := c.RealName
	if realName == "" {
		realName = c.Nick
	}

	c.send("NICK", c.Nick)
	c.send("USER", c.Nick, "*", "*", realName)

	return nil
}

func (c *Client) send(cmd string, params ...string) {
	msg := &irc.Message{
		Command: cmd,
		Params:  params,
	}

	c.conn.Encode(msg)

	fmt.Printf("[%s] --> %+v\n", c.Name, msg)
}

func (c *Client) sendRaw(msg string) {
	c.conn.Write([]byte(msg))
	fmt.Printf("[%s] --> %+v\n", c.Name, msg)
}

func (c *Client) listExistingChannels() []string {
	files, err := ioutil.ReadDir(c.directory)
	if err != nil {
		log.Fatal(err)
	}

	channels := []string{}
	for _, file := range files {
		name := file.Name()
		if isChannel(name) {
			channels = append(channels, name)
		}
	}

	return channels
}

func (c *Client) handleMessage(msg *irc.Message) {
	buf := c.getBuffer(serverBufferName)

	switch msg.Command {
	case irc.RPL_WELCOME:
		for _, msg := range c.OnConnect {
			c.sendRaw(msg)
		}

		if c.RejoinExisting {
			for _, ch := range c.listExistingChannels() {
				c.send(irc.JOIN, ch)
			}
		}

	case irc.PING:
		c.send(irc.PONG, msg.Params...)

	case irc.PONG:
		if len(msg.Params) != 2 {
			break
		}

		s := strings.SplitN(msg.Params[1], " ", 2)
		if len(s) != 2 {
			break
		}

		if ts, err := strconv.ParseInt(s[1], 10, 64); err == nil {
			delta := time.Duration(time.Now().UnixNano()-ts) / time.Millisecond
			text := fmt.Sprintf("PONG from %s: %d ms", msg.Params[0], delta)

			buf.writeInfoMessage(text)
		}

	case irc.NICK:
		from := msg.Prefix.Name
		to := msg.Params[0]

		if from == c.Nick {
			c.Nick = to

			text := fmt.Sprintf("changed nick from %s to %s", from, to)
			buf.writeInfoMessage(text)
		} else {
			// TODO: broadcast renames to all bufs having that user?
			// requires more tracking
			buf = nil
		}

	case irc.JOIN:
		buf = c.getBuffer(msg.Params[0])

	case irc.PRIVMSG, irc.NOTICE:
		target := msg.Params[0]

		// Group all messages sent by the server together,
		// regardless of server name.
		//
		// For direct messages, we want to look at the sender.
		if msg.Prefix.IsServer() {
			target = serverBufferName
		} else if !isChannel(target) {
			target = msg.Prefix.Name
		}

		buf = c.getBuffer(target)

	case irc.RPL_TOPIC:
		target := msg.Params[1]
		topic := msg.Params[2]

		c.getBuffer(target).topic = topic

	case irc.ERR_NICKNAMEINUSE:
		c.Nick = c.Nick + "`"
		fmt.Printf("Nick in use, trying '%s'\n", c.Nick)
		c.send("NICK", c.Nick)
	}

	if buf != nil {
		buf.ch <- msg
	}
}

func (c *Client) serverBuffer() *buffer {
	return c.getBuffer(serverBufferName)
}

func (c *Client) getBuffer(name string) *buffer {
	c.mux.Lock()
	defer c.mux.Unlock()

	name = normalizeBufferName(name)

	// Sent early on, at least by freenode.
	if name == "*" {
		name = serverBufferName
	}

	if buf, exists := c.buffers[name]; exists {
		return &buf
	}

	path := c.directory

	// We want to write __in, __out top level for the server, and
	// as a child for every other buffer.
	if name != serverBufferName {
		path = filepath.Join(path, name)
	}

	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		log.Fatal(err)
	}

	c.buffers[name] = buffer{
		ch:     make(chan *irc.Message),
		client: c,
		path:   path,

		name:  name,
		topic: "",
		users: make(map[string]string),
	}

	b := c.buffers[name]

	go b.inputHandler()
	go b.outputHandler()

	return &b
}

func (c *Client) handleInputLine(bufName, line string) {
	fmt.Printf("[%s/%s] >> %s\n", c.Name, bufName, line)

	cmd, rest := splitInputCommand(bufName, line)

	switch cmd {
	case "/m", "/msg":
		s := strings.SplitN(rest, " ", 2)
		if len(s) != 2 {
			c.serverBuffer().writeInfoMessage("expected: /msg TARGET MESSAGE")
			return
		} else if s[0] == serverBufferName {
			c.serverBuffer().writeInfoMessage("can't PRIVMSG a server.")
			return
		}

		buf := c.getBuffer(s[0])

		// TODO: pull this out into simple `buf.AddMessage` or so
		buf.ch <- &irc.Message{
			Prefix:  &irc.Prefix{Name: c.Nick},
			Command: irc.PRIVMSG,
			Params:  []string{s[1]},
		}

		c.send("PRIVMSG", s[0], s[1])

	case "/j", "/join":
		if !isChannel(rest) {
			c.getBuffer(bufName).writeInfoMessage("expected: /join TARGET")
			return
		}
		c.send("JOIN", rest)

	case "/l", "/list":
		buf := c.getBuffer(bufName)

		buf.writeInfoMessage("~~ buffers ~~")
		for k, _ := range c.buffers {
			buf.writeInfoMessage(" " + k)
		}

	case "/ping":
		ts := time.Now().UnixNano()
		c.send("PING", fmt.Sprintf("%s %d", bufName, ts))

	case "/quote":
		params := strings.Split(rest, " ")
		if len(params) == 1 {
			c.send(params[0])
		} else {
			c.send(params[0], params[1:]...)
		}

	case "/r", "/reconnect":
		c.serverBuffer().writeInfoMessage("... disconnecting")
		if err := c.conn.Close(); err != nil {
			fmt.Printf("failed to close: %+v\n", err)
		}

	default:
		text := fmt.Sprintf("Unknown command: %s %s", cmd, rest)
		c.getBuffer(bufName).writeInfoMessage(text)
	}
}

func (c *Client) RunLoop() error {
	for {
		message, err := c.conn.Decode()
		if err != nil {
			return err
		}

		fmt.Printf("[%s] <-- %+v\n", c.Name, message)
		c.handleMessage(message)
	}
}

func (b *buffer) writeInfoMessage(msg string) {
	b.ch <- &irc.Message{
		Prefix:  &irc.Prefix{Name: "uex"},
		Command: "*",
		Params:  []string{msg},
	}
}

func (b *buffer) outputHandler() {
	name := filepath.Join(b.path, "__out")
	mode := os.O_APPEND | os.O_RDWR | os.O_CREATE
	file, err := os.OpenFile(name, mode, 0644)
	if err != nil {
		log.Fatalf("failed to create output file: %+v\n", err)
	}

	defer file.Close()

	// TODO: better serialization?? colors?? etc.
	for msg := range b.ch {
		text := formatMessage(msg)
		if text == "" {
			continue
		}

		if _, err := file.WriteString(text + "\n"); err != nil {
			log.Fatal(err)
		}
		if err := file.Sync(); err != nil {
			log.Fatal(err)
		}
	}
}

func (b *buffer) inputHandler() {
	name := filepath.Join(b.path, "__in")
	err := syscall.Mkfifo(name, 0777)

	// Doesn't matter if the FIFO already exists from a previous run.
	if err != nil && err != syscall.EEXIST {
		log.Fatal(err)
	}

	for {
		buf, err := ioutil.ReadFile(name)
		if err != nil {
			log.Fatal(err)
		}

		if len(buf) == 0 {
			time.Sleep(100 * time.Millisecond)
			continue
		}

		lines := strings.Split(string(buf), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}

			b.client.handleInputLine(b.name, line)
		}
	}
}

func isChannel(target string) bool {
	if target == "" {
		return false
	}

	return target[0] == '#' || target[0] == '&'
}

// normalizeBufferName strips out illegal characters and standardizes
// naming so that it's safe to write as a directory name to the file
// system.
func normalizeBufferName(buffer string) string {
	return strings.Map(func(ch rune) rune {
		if unicode.IsLetter(ch) || unicode.IsNumber(ch) {
			return unicode.ToLower(ch)
		} else if strings.ContainsRune(".#&+!-", ch) {
			return ch
		}

		return '_'
	}, buffer)
}

func splitInputCommand(bufName, line string) (string, string) {
	// Without a prefix, it's just a regular PRIVMSG
	if !strings.HasPrefix(line, "/") {
		return "/msg", bufName + " " + line
	}
	// Double slash at start means privmsg with leading slash
	if strings.HasPrefix(line, "//") {
		return "/msg", bufName + " " + line[1:]
	}

	s := strings.SplitN(line, " ", 2)
	cmd := strings.ToLower(s[0])

	if len(s) == 1 {
		return cmd, ""
	}

	return cmd, s[1]
}
<|endoftext|>


<commit_before>// Copyright 2016 bs authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/tsuru/bs/bslog"
	"github.com/tsuru/bs/config"
	"github.com/tsuru/bs/log"
	"github.com/tsuru/bs/metric"
	_ "github.com/tsuru/bs/metric/logstash"
	"github.com/tsuru/bs/status"
)

const (
	version = "v1.13-rc1"
)

var printVersion bool

type StopWaiter interface {
	Stop()
	Wait()
}

func init() {
	flag.BoolVar(&printVersion, "version", false, "Print version and exit")
}

func startSignalHandler(callback func(os.Signal), signals ...os.Signal) {
	sigChan := make(chan os.Signal, 4)
	go func() {
		if signal, ok := <-sigChan; ok {
			callback(signal)
		}
	}()
	signal.Notify(sigChan, signals...)
}

func main() {
	err := agent.Listen(&agent.Options{
		NoShutdownCleanup: true,
	})
	if err != nil {
		bslog.Fatalf("Unable to initialize gops agent: %s\n", err)
	}
	defer agent.Close()
	flag.Parse()
	if printVersion {
		fmt.Printf("bs version %s\n", version)
		return
	}
	lf := log.LogForwarder{
		BindAddress:     config.Config.SyslogListenAddress,
		DockerEndpoint:  config.Config.DockerEndpoint,
		EnabledBackends: config.Config.LogBackends,
	}
	err = lf.Start()
	if err != nil {
		bslog.Fatalf("Unable to initialize log forwarder: %s\n", err)
	}
	mRunner := metric.NewRunner(config.Config.DockerEndpoint, config.Config.MetricsInterval,
		config.Config.MetricsBackend)
	mRunner.EnableBasicMetrics = config.Config.MetricsEnableBasic
	mRunner.EnableConnMetrics = config.Config.MetricsEnableConn
	mRunner.EnableHostMetrics = config.Config.MetricsEnableHost
	err = mRunner.Start()
	if err != nil {
		bslog.Warnf("Unable to initialize metrics runner: %s\n", err)
	}
	reporter, err := status.NewReporter(&status.ReporterConfig{
		TsuruEndpoint:  config.Config.TsuruEndpoint,
		TsuruToken:     config.Config.TsuruToken,
		DockerEndpoint: config.Config.DockerEndpoint,
		Interval:       config.Config.StatusInterval,
	})
	if err != nil {
		bslog.Warnf("Unable to initialize status reporter: %s\n", err)
	}
	monitorEl := []StopWaiter{&lf, mRunner}
	if reporter != nil {
		monitorEl = append(monitorEl, reporter)
	}
	var signaled bool
	startSignalHandler(func(signal os.Signal) {
		signaled = true
		for _, m := range monitorEl {
			go m.Stop()
		}
	}, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	for _, m := range monitorEl {
		m.Wait()
	}
	if !signaled {
		bslog.Fatalf("Exiting bs because no service could be initialized.")
	}
}
<commit_msg>bump tag to 1.13-rc2<commit_after>// Copyright 2016 bs authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/google/gops/agent"
	"github.com/tsuru/bs/bslog"
	"github.com/tsuru/bs/config"
	"github.com/tsuru/bs/log"
	"github.com/tsuru/bs/metric"
	_ "github.com/tsuru/bs/metric/logstash"
	"github.com/tsuru/bs/status"
)

const (
	version = "v1.13-rc2"
)

var printVersion bool

type StopWaiter interface {
	Stop()
	Wait()
}

func init() {
	flag.BoolVar(&printVersion, "version", false, "Print version and exit")
}

func startSignalHandler(callback func(os.Signal), signals ...os.Signal) {
	sigChan := make(chan os.Signal, 4)
	go func() {
		if signal, ok := <-sigChan; ok {
			callback(signal)
		}
	}()
	signal.Notify(sigChan, signals...)
}

func main() {
	err := agent.Listen(&agent.Options{
		NoShutdownCleanup: true,
	})
	if err != nil {
		bslog.Fatalf("Unable to initialize gops agent: %s\n", err)
	}
	defer agent.Close()
	flag.Parse()
	if printVersion {
		fmt.Printf("bs version %s\n", version)
		return
	}
	lf := log.LogForwarder{
		BindAddress:     config.Config.SyslogListenAddress,
		DockerEndpoint:  config.Config.DockerEndpoint,
		EnabledBackends: config.Config.LogBackends,
	}
	err = lf.Start()
	if err != nil {
		bslog.Fatalf("Unable to initialize log forwarder: %s\n", err)
	}
	mRunner := metric.NewRunner(config.Config.DockerEndpoint, config.Config.MetricsInterval,
		config.Config.MetricsBackend)
	mRunner.EnableBasicMetrics = config.Config.MetricsEnableBasic
	mRunner.EnableConnMetrics = config.Config.MetricsEnableConn
	mRunner.EnableHostMetrics = config.Config.MetricsEnableHost
	err = mRunner.Start()
	if err != nil {
		bslog.Warnf("Unable to initialize metrics runner: %s\n", err)
	}
	reporter, err := status.NewReporter(&status.ReporterConfig{
		TsuruEndpoint:  config.Config.TsuruEndpoint,
		TsuruToken:     config.Config.TsuruToken,
		DockerEndpoint: config.Config.DockerEndpoint,
		Interval:       config.Config.StatusInterval,
	})
	if err != nil {
		bslog.Warnf("Unable to initialize status reporter: %s\n", err)
	}
	monitorEl := []StopWaiter{&lf, mRunner}
	if reporter != nil {
		monitorEl = append(monitorEl, reporter)
	}
	var signaled bool
	startSignalHandler(func(signal os.Signal) {
		signaled = true
		for _, m := range monitorEl {
			go m.Stop()
		}
	}, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	for _, m := range monitorEl {
		m.Wait()
	}
	if !signaled {
		bslog.Fatalf("Exiting bs because no service could be initialized.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"time"

	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/pkg/wsproxy"
	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/proxyprotocol"
)

// Version number
const Version = "0.13.1"

var message = `<!DOCTYPE html>

503 Backend Unavailable

Sorry, we‘re having a brief problem. You can retry.

If the problem persists, please get in touch.

`

type ConnectionErrorHandler struct{ http.RoundTripper }

func (c *ConnectionErrorHandler) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := c.RoundTripper.RoundTrip(req)
	if _, ok := err.(*net.OpError); ok {
		r := &http.Response{
			StatusCode: http.StatusServiceUnavailable,
			Body:       ioutil.NopCloser(bytes.NewBufferString(message)),
		}
		return r, nil
	}
	return resp, err
}

func main() {
	var (
		listen, cert, key, where           string
		useTLS, useLogging, behindTCPProxy bool
		flushInterval                      time.Duration
	)
	flag.StringVar(&listen, "listen", ":443", "Bind address to listen on")
	flag.StringVar(&key, "key", "/etc/ssl/private/key.pem", "Path to PEM key")
	flag.StringVar(&cert, "cert", "/etc/ssl/private/cert.pem", "Path to PEM certificate")
	flag.StringVar(&where, "where", "http://localhost:80", "Place to forward connections to")
	flag.BoolVar(&useTLS, "tls", true, "accept HTTPS connections")
	flag.BoolVar(&useLogging, "logging", true, "log requests")
	flag.BoolVar(&behindTCPProxy, "behind-tcp-proxy", false, "running behind TCP proxy (such as ELB or HAProxy)")
	flag.DurationVar(&flushInterval, "flush-interval", 0, "minimum duration between flushes to the client (default: off)")
	oldUsage := flag.Usage
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n%v version %v\n\n", os.Args[0], Version)
		oldUsage()
	}
	flag.Parse()

	url, err := url.Parse(where)
	if err != nil {
		log.Fatalln("Fatal parsing -where:", err)
	}

	httpProxy := httputil.NewSingleHostReverseProxy(url)
	httpProxy.Transport = &ConnectionErrorHandler{http.DefaultTransport}
	httpProxy.FlushInterval = flushInterval

	proxy := &wsproxy.ReverseProxy{httpProxy}

	var handler http.Handler

	handler = proxy

	originalHandler := handler
	handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/_version" {
			w.Header().Add("X-Tiny-SSL-Version", Version)
		}
		r.Header.Set("X-Forwarded-Proto", "https")
		originalHandler.ServeHTTP(w, r)
	})

	if useLogging {
		handler = &LoggingMiddleware{handler}
	}

	server := &http.Server{Addr: listen, Handler: handler}

	switch {
	case useTLS && behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServeTLS(server, cert, key)
	case behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServe(server)
	case useTLS:
		err = server.ListenAndServeTLS(cert, key)
	default:
		err = server.ListenAndServe()
	}

	log.Fatalln(err)
}
<commit_msg>Add missing title element to HTML<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"time"

	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/pkg/wsproxy"
	"github.com/sensiblecodeio/tiny-ssl-reverse-proxy/proxyprotocol"
)

// Version number
const Version = "0.13.1"

var message = `<!DOCTYPE html>

<title>
Backend Unavailable
</title>

503 Backend Unavailable

Sorry, we‘re having a brief problem. You can retry.

If the problem persists, please get in touch.

`

type ConnectionErrorHandler struct{ http.RoundTripper }

func (c *ConnectionErrorHandler) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := c.RoundTripper.RoundTrip(req)
	if _, ok := err.(*net.OpError); ok {
		r := &http.Response{
			StatusCode: http.StatusServiceUnavailable,
			Body:       ioutil.NopCloser(bytes.NewBufferString(message)),
		}
		return r, nil
	}
	return resp, err
}

func main() {
	var (
		listen, cert, key, where           string
		useTLS, useLogging, behindTCPProxy bool
		flushInterval                      time.Duration
	)
	flag.StringVar(&listen, "listen", ":443", "Bind address to listen on")
	flag.StringVar(&key, "key", "/etc/ssl/private/key.pem", "Path to PEM key")
	flag.StringVar(&cert, "cert", "/etc/ssl/private/cert.pem", "Path to PEM certificate")
	flag.StringVar(&where, "where", "http://localhost:80", "Place to forward connections to")
	flag.BoolVar(&useTLS, "tls", true, "accept HTTPS connections")
	flag.BoolVar(&useLogging, "logging", true, "log requests")
	flag.BoolVar(&behindTCPProxy, "behind-tcp-proxy", false, "running behind TCP proxy (such as ELB or HAProxy)")
	flag.DurationVar(&flushInterval, "flush-interval", 0, "minimum duration between flushes to the client (default: off)")
	oldUsage := flag.Usage
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n%v version %v\n\n", os.Args[0], Version)
		oldUsage()
	}
	flag.Parse()

	url, err := url.Parse(where)
	if err != nil {
		log.Fatalln("Fatal parsing -where:", err)
	}

	httpProxy := httputil.NewSingleHostReverseProxy(url)
	httpProxy.Transport = &ConnectionErrorHandler{http.DefaultTransport}
	httpProxy.FlushInterval = flushInterval

	proxy := &wsproxy.ReverseProxy{httpProxy}

	var handler http.Handler

	handler = proxy

	originalHandler := handler
	handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/_version" {
			w.Header().Add("X-Tiny-SSL-Version", Version)
		}
		r.Header.Set("X-Forwarded-Proto", "https")
		originalHandler.ServeHTTP(w, r)
	})

	if useLogging {
		handler = &LoggingMiddleware{handler}
	}

	server := &http.Server{Addr: listen, Handler: handler}

	switch {
	case useTLS && behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServeTLS(server, cert, key)
	case behindTCPProxy:
		err = proxyprotocol.BehindTCPProxyListenAndServe(server)
	case useTLS:
		err = server.ListenAndServeTLS(cert, key)
	default:
		err = server.ListenAndServe()
	}

	log.Fatalln(err)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	native "github.com/docker/docker/daemon/execdriver/native/template"
	"github.com/docker/docker/pkg/platform"
	"github.com/docker/engine-api/client"
	"github.com/jfrazelle/riddler/parse"
	specs "github.com/opencontainers/specs/specs-go"
)

const (
	// BANNER is what is printed for help/info output
	BANNER = `      _     _     _ _
 _ __(_) __| | __| | | ___ _ __
| '__| |/ _` + "`" + ` |/ _` + "`" + ` | |/ _ \ '__|
| |  | | (_| | (_| | |  __/ |
|_|  |_|\__,_|\__,_|_|\___|_|
 docker inspect to opencontainers runc spec generator.
 Version: %s

`
	// VERSION is the binary version.
	VERSION = "v0.1.0"

	specConfig = "config.json"
)

var (
	arg        string
	bundle     string
	dockerHost string
	hooks      specs.Hooks
	hookflags  stringSlice
	force      bool
	idroot     uint32
	idlen      uint32

	debug   bool
	version bool
)

// stringSlice is a slice of strings
type stringSlice []string

// implement the flag interface for stringSlice
func (s *stringSlice) String() string {
	return fmt.Sprintf("%s", *s)
}
func (s *stringSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}
func (s stringSlice) ParseHooks() (hooks specs.Hooks, err error) {
	for _, v := range s {
		parts := strings.SplitN(v, ":", 2)
		if len(parts) <= 1 {
			return hooks, fmt.Errorf("parsing %s as hook_name:exec failed", v)
		}
		cmd := strings.Split(parts[1], " ")
		exec, err := exec.LookPath(cmd[0])
		if err != nil {
			return hooks, fmt.Errorf("looking up exec path for %s failed: %v", cmd[0], err)
		}
		hook := specs.Hook{
			Path: exec,
		}
		if len(cmd) > 1 {
			hook.Args = cmd[:1]
		}
		switch parts[0] {
		case "prestart":
			hooks.Prestart = append(hooks.Prestart, hook)
		case "poststart":
			hooks.Poststart = append(hooks.Poststart, hook)
		case "poststop":
			hooks.Poststop = append(hooks.Poststop, hook)
		default:
			return hooks, fmt.Errorf("%s is not a valid hook, try 'prestart', 'poststart', or 'poststop'", parts[0])
		}
	}
	return hooks, nil
}

func init() {
	var idrootVar, idlenVar int
	// parse flags
	flag.StringVar(&dockerHost, "host", "unix:///var/run/docker.sock", "Docker Daemon socket(s) to connect to")
	flag.StringVar(&bundle, "bundle", "", "Path to the root of the bundle directory")
	flag.Var(&hookflags, "hook", "Hooks to prefill into spec file. (ex. --hook prestart:netns)")

	flag.IntVar(&idrootVar, "idroot", 0, "Root UID/GID for user namespaces")
	flag.IntVar(&idlenVar, "idlen", 0, "Length of UID/GID ID space ranges for user namespaces")

	flag.BoolVar(&force, "force", false, "force overwrite existing files")
	flag.BoolVar(&force, "f", false, "force overwrite existing files")

	flag.BoolVar(&version, "version", false, "print version and exit")
	flag.BoolVar(&version, "v", false, "print version and exit (shorthand)")
	flag.BoolVar(&debug, "d", false, "run in debug mode")

	flag.Usage = func() {
		fmt.Fprint(os.Stderr, fmt.Sprintf(BANNER, VERSION))
		flag.PrintDefaults()
	}

	flag.Parse()
	idroot = uint32(idrootVar)
	idlen = uint32(idlenVar)

	if version {
		fmt.Printf("%s\n", VERSION)
		os.Exit(0)
	}

	if flag.NArg() < 1 {
		usageAndExit("Pass the container name or ID.", 1)
	}

	// parse the arg
	arg = flag.Args()[0]
	if arg == "help" {
		usageAndExit("", 0)
	}

	if arg == "version" {
		fmt.Printf("%s", VERSION)
		os.Exit(0)
	}

	// set log level
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	var err error
	hooks, err = hookflags.ParseHooks()
	if err != nil {
		logrus.Fatal(err)
	}
}

func main() {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	cli, err := client.NewClient(dockerHost, "", nil, defaultHeaders)
	if err != nil {
		panic(err)
	}

	// get container info
	c, err := cli.ContainerInspect(context.Background(), arg)
	if err != nil {
		logrus.Fatalf("inspecting container (%s) failed: %v", arg, err)
	}

	t := native.New()
	spec, err := parse.Config(c, platform.OSType, platform.Architecture, t.Capabilities, idroot, idlen)
	if err != nil {
		logrus.Fatalf("Spec config conversion for %s failed: %v", arg, err)
	}

	// fill in hooks, if passed through command line
	spec.Hooks = hooks
	if err := writeConfig(spec); err != nil {
		logrus.Fatal(err)
	}

	fmt.Printf("%s has been saved.\n", specConfig)
}

func usageAndExit(message string, exitCode int) {
	if message != "" {
		fmt.Fprintf(os.Stderr, message)
		fmt.Fprintf(os.Stderr, "\n\n")
	}
	flag.Usage()
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(exitCode)
}

func checkNoFile(name string) error {
	_, err := os.Stat(name)
	if err == nil {
		return fmt.Errorf("File %s exists. Remove it first", name)
	}
	if !os.IsNotExist(err) {
		return err
	}
	return nil
}

func writeConfig(spec *specs.Spec) error {
	if bundle != "" {
		// change current working directory
		if err := os.Chdir(bundle); err != nil {
			return fmt.Errorf("change working directory to %s failed: %v", bundle, err)
		}
	}

	// make sure we don't already have files, we would not want to overwrite them
	if !force {
		if err := checkNoFile(specConfig); err != nil {
			return err
		}
	}

	data, err := json.MarshalIndent(&spec, "", "    ")
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
		return err
	}

	return nil
}
<commit_msg>Add all hook args to config.json<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	native "github.com/docker/docker/daemon/execdriver/native/template"
	"github.com/docker/docker/pkg/platform"
	"github.com/docker/engine-api/client"
	"github.com/jfrazelle/riddler/parse"
	specs "github.com/opencontainers/specs/specs-go"
)

const (
	// BANNER is what is printed for help/info output
	BANNER = `      _     _     _ _
 _ __(_) __| | __| | | ___ _ __
| '__| |/ _` + "`" + ` |/ _` + "`" + ` | |/ _ \ '__|
| |  | | (_| | (_| | |  __/ |
|_|  |_|\__,_|\__,_|_|\___|_|
 docker inspect to opencontainers runc spec generator.
 Version: %s

`
	// VERSION is the binary version.
	VERSION = "v0.1.0"

	specConfig = "config.json"
)

var (
	arg        string
	bundle     string
	dockerHost string
	hooks      specs.Hooks
	hookflags  stringSlice
	force      bool
	idroot     uint32
	idlen      uint32

	debug   bool
	version bool
)

// stringSlice is a slice of strings
type stringSlice []string

// implement the flag interface for stringSlice
func (s *stringSlice) String() string {
	return fmt.Sprintf("%s", *s)
}
func (s *stringSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}
func (s stringSlice) ParseHooks() (hooks specs.Hooks, err error) {
	for _, v := range s {
		parts := strings.SplitN(v, ":", 2)
		if len(parts) <= 1 {
			return hooks, fmt.Errorf("parsing %s as hook_name:exec failed", v)
		}
		cmd := strings.Split(parts[1], " ")
		exec, err := exec.LookPath(cmd[0])
		if err != nil {
			return hooks, fmt.Errorf("looking up exec path for %s failed: %v", cmd[0], err)
		}
		hook := specs.Hook{
			Path: exec,
		}
		if len(cmd) > 1 {
			hook.Args = append(hook.Args, cmd...)
		}
		switch parts[0] {
		case "prestart":
			hooks.Prestart = append(hooks.Prestart, hook)
		case "poststart":
			hooks.Poststart = append(hooks.Poststart, hook)
		case "poststop":
			hooks.Poststop = append(hooks.Poststop, hook)
		default:
			return hooks, fmt.Errorf("%s is not a valid hook, try 'prestart', 'poststart', or 'poststop'", parts[0])
		}
	}
	return hooks, nil
}

func init() {
	var idrootVar, idlenVar int
	// parse flags
	flag.StringVar(&dockerHost, "host", "unix:///var/run/docker.sock", "Docker Daemon socket(s) to connect to")
	flag.StringVar(&bundle, "bundle", "", "Path to the root of the bundle directory")
	flag.Var(&hookflags, "hook", "Hooks to prefill into spec file. (ex. --hook prestart:netns)")

	flag.IntVar(&idrootVar, "idroot", 0, "Root UID/GID for user namespaces")
	flag.IntVar(&idlenVar, "idlen", 0, "Length of UID/GID ID space ranges for user namespaces")

	flag.BoolVar(&force, "force", false, "force overwrite existing files")
	flag.BoolVar(&force, "f", false, "force overwrite existing files")

	flag.BoolVar(&version, "version", false, "print version and exit")
	flag.BoolVar(&version, "v", false, "print version and exit (shorthand)")
	flag.BoolVar(&debug, "d", false, "run in debug mode")

	flag.Usage = func() {
		fmt.Fprint(os.Stderr, fmt.Sprintf(BANNER, VERSION))
		flag.PrintDefaults()
	}

	flag.Parse()
	idroot = uint32(idrootVar)
	idlen = uint32(idlenVar)

	if version {
		fmt.Printf("%s\n", VERSION)
		os.Exit(0)
	}

	if flag.NArg() < 1 {
		usageAndExit("Pass the container name or ID.", 1)
	}

	// parse the arg
	arg = flag.Args()[0]
	if arg == "help" {
		usageAndExit("", 0)
	}

	if arg == "version" {
		fmt.Printf("%s", VERSION)
		os.Exit(0)
	}

	// set log level
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	var err error
	hooks, err = hookflags.ParseHooks()
	if err != nil {
		logrus.Fatal(err)
	}
}

func main() {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	cli, err := client.NewClient(dockerHost, "", nil, defaultHeaders)
	if err != nil {
		panic(err)
	}

	// get container info
	c, err := cli.ContainerInspect(context.Background(), arg)
	if err != nil {
		logrus.Fatalf("inspecting container (%s) failed: %v", arg, err)
	}

	t := native.New()
	spec, err := parse.Config(c, platform.OSType, platform.Architecture, t.Capabilities, idroot, idlen)
	if err != nil {
		logrus.Fatalf("Spec config conversion for %s failed: %v", arg, err)
	}

	// fill in hooks, if passed through command line
	spec.Hooks = hooks
	if err := writeConfig(spec); err != nil {
		logrus.Fatal(err)
	}

	fmt.Printf("%s has been saved.\n", specConfig)
}

func usageAndExit(message string, exitCode int) {
	if message != "" {
		fmt.Fprintf(os.Stderr, message)
		fmt.Fprintf(os.Stderr, "\n\n")
	}
	flag.Usage()
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(exitCode)
}

func checkNoFile(name string) error {
	_, err := os.Stat(name)
	if err == nil {
		return fmt.Errorf("File %s exists. Remove it first", name)
	}
	if !os.IsNotExist(err) {
		return err
	}
	return nil
}

func writeConfig(spec *specs.Spec) error {
	if bundle != "" {
		// change current working directory
		if err := os.Chdir(bundle); err != nil {
			return fmt.Errorf("change working directory to %s failed: %v", bundle, err)
		}
	}

	// make sure we don't already have files, we would not want to overwrite them
	if !force {
		if err := checkNoFile(specConfig); err != nil {
			return err
		}
	}

	data, err := json.MarshalIndent(&spec, "", "    ")
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"sync"

	"github.com/mholt/caddy/config"
	"github.com/mholt/caddy/server"
)

var (
	conf  string
	http2 bool // TODO: temporary flag until http2 is standard
	quiet bool
	cpu   string
)

func init() {
	flag.StringVar(&conf, "conf", "", "Configuration file to use (default="+config.DefaultConfigFile+")")
	flag.BoolVar(&http2, "http2", true, "Enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
	flag.BoolVar(&quiet, "quiet", false, "Quiet mode (no initialization output)")
	flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
	flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site")
	flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
	flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
	flag.Parse()

	config.AppName = "Caddy"
	config.AppVersion = "0.6.0"
}

func main() {
	var wg sync.WaitGroup

	// Set CPU cap
	err := setCPU(cpu)
	if err != nil {
		log.Fatal(err)
	}

	// Load config from file
	allConfigs, err := loadConfigs()
	if err != nil {
		log.Fatal(err)
	}

	// Group by address (virtual hosts)
	addresses, err := arrangeBindings(allConfigs)
	if err != nil {
		log.Fatal(err)
	}

	// Start each server with its one or more configurations
	for addr, configs := range addresses {
		s, err := server.New(addr, configs, configs[0].TLS.Enabled)
		if err != nil {
			log.Fatal(err)
		}
		s.HTTP2 = http2 // TODO: This setting is temporary
		wg.Add(1)
		go func(s *server.Server) {
			defer wg.Done()
			err := s.Serve()
			if err != nil {
				log.Fatal(err) // kill whole process to avoid a half-alive zombie server
			}
		}(s)

		if !quiet {
			for _, config := range configs {
				fmt.Println(config.Address())
			}
		}
	}

	wg.Wait()
}

// loadConfigs loads configuration from a file or stdin (piped).
// Configuration is obtained from one of three sources, tried
// in this order: 1. -conf flag, 2. stdin, 3. Caddyfile.
// If none of those are available, a default configuration is
// loaded.
func loadConfigs() ([]server.Config, error) {
	// -conf flag
	if conf != "" {
		file, err := os.Open(conf)
		if err != nil {
			return []server.Config{}, err
		}
		defer file.Close()
		return config.Load(path.Base(conf), file)
	}

	// stdin
	// Load piped configuration data, if any
	fi, err := os.Stdin.Stat()
	if err != nil {
		log.Fatal(err)
	}
	if err == nil && fi.Mode()&os.ModeCharDevice == 0 {
		confBody, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			log.Fatal(err)
		}
		if len(confBody) > 0 {
			return config.Load("stdin", bytes.NewReader(confBody))
		}
	}

	// Caddyfile
	file, err := os.Open(config.DefaultConfigFile)
	if err != nil {
		if os.IsNotExist(err) {
			return []server.Config{config.Default()}, nil
		}
		return []server.Config{}, err
	}
	defer file.Close()
	return config.Load(config.DefaultConfigFile, file)
}

// arrangeBindings groups configurations by their bind address. For example,
// a server that should listen on localhost and another on 127.0.0.1 will
// be grouped into the same address: 127.0.0.1. It will return an error
// if the address lookup fails or if a TLS listener is configured on the
// same address as a plaintext HTTP listener.
func arrangeBindings(allConfigs []server.Config) (map[string][]server.Config, error) {
	addresses := make(map[string][]server.Config)

	// Group configs by bind address
	for _, conf := range allConfigs {
		addr, err := net.ResolveTCPAddr("tcp", conf.Address())
		if err != nil {
			return addresses, err
		}
		addresses[addr.String()] = append(addresses[addr.String()], conf)
	}

	// Don't allow HTTP and HTTPS to be served on the same address
	for _, configs := range addresses {
		isTLS := configs[0].TLS.Enabled
		for _, config := range configs {
			if config.TLS.Enabled != isTLS {
				thisConfigProto, otherConfigProto := "HTTP", "HTTP"
				if config.TLS.Enabled {
					thisConfigProto = "HTTPS"
				}
				if configs[0].TLS.Enabled {
					otherConfigProto = "HTTPS"
				}
				return addresses, fmt.Errorf("Configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
					configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
			}
		}
	}

	return addresses, nil
}

// setCPU parses string cpu and sets GOMAXPROCS
// according to its value. It accepts either
// a number (e.g. 3) or a percent (e.g. 50%).
func setCPU(cpu string) error {
	var numCPU int

	availCPU := runtime.NumCPU()

	if strings.HasSuffix(cpu, "%") {
		// Percent
		var percent float32
		pctStr := cpu[:len(cpu)-1]
		pctInt, err := strconv.Atoi(pctStr)
		if err != nil || pctInt < 1 || pctInt > 100 {
			return errors.New("Invalid CPU value: percentage must be between 1-100")
		}
		percent = float32(pctInt) / 100
		numCPU = int(float32(availCPU) * percent)
	} else {
		// Number
		num, err := strconv.Atoi(cpu)
		if err != nil || num < 1 {
			return errors.New("Invalid CPU value: provide a number or percent greater than 0")
		}
		numCPU = num
	}

	if numCPU > availCPU {
		numCPU = availCPU
	}

	runtime.GOMAXPROCS(numCPU)
	return nil
}
<commit_msg>Fix for stdin on Windows<commit_after>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"sync"

	"github.com/mholt/caddy/config"
	"github.com/mholt/caddy/server"
)

var (
	conf  string
	http2 bool // TODO: temporary flag until http2 is standard
	quiet bool
	cpu   string
)

func init() {
	flag.StringVar(&conf, "conf", "", "Configuration file to use (default="+config.DefaultConfigFile+")")
	flag.BoolVar(&http2, "http2", true, "Enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
	flag.BoolVar(&quiet, "quiet", false, "Quiet mode (no initialization output)")
	flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
	flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site")
	flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
	flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
	flag.Parse()

	config.AppName = "Caddy"
	config.AppVersion = "0.6.0"
}

func main() {
	var wg sync.WaitGroup

	// Set CPU cap
	err := setCPU(cpu)
	if err != nil {
		log.Fatal(err)
	}

	// Load config from file
	allConfigs, err := loadConfigs()
	if err != nil {
		log.Fatal(err)
	}

	// Group by address (virtual hosts)
	addresses, err := arrangeBindings(allConfigs)
	if err != nil {
		log.Fatal(err)
	}

	// Start each server with its one or more configurations
	for addr, configs := range addresses {
		s, err := server.New(addr, configs, configs[0].TLS.Enabled)
		if err != nil {
			log.Fatal(err)
		}
		s.HTTP2 = http2 // TODO: This setting is temporary
		wg.Add(1)
		go func(s *server.Server) {
			defer wg.Done()
			err := s.Serve()
			if err != nil {
				log.Fatal(err) // kill whole process to avoid a half-alive zombie server
			}
		}(s)

		if !quiet {
			for _, config := range configs {
				fmt.Println(config.Address())
			}
		}
	}

	wg.Wait()
}

// loadConfigs loads configuration from a file or stdin (piped).
// Configuration is obtained from one of three sources, tried
// in this order: 1. -conf flag, 2. stdin, 3. Caddyfile.
// If none of those are available, a default configuration is
// loaded.
func loadConfigs() ([]server.Config, error) {
	// -conf flag
	if conf != "" {
		file, err := os.Open(conf)
		if err != nil {
			return []server.Config{}, err
		}
		defer file.Close()
		return config.Load(path.Base(conf), file)
	}

	// stdin
	fi, err := os.Stdin.Stat()
	if err == nil && fi.Mode()&os.ModeCharDevice == 0 {
		// Note that a non-nil error is not a problem. Windows
		// will not create a stdin if there is no pipe, which
		// produces an error when calling Stat(). But Unix will
		// make one either way, which is why we also check that
		// bitmask.
		confBody, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			log.Fatal(err)
		}
		if len(confBody) > 0 {
			return config.Load("stdin", bytes.NewReader(confBody))
		}
	}

	// Caddyfile
	file, err := os.Open(config.DefaultConfigFile)
	if err != nil {
		if os.IsNotExist(err) {
			return []server.Config{config.Default()}, nil
		}
		return []server.Config{}, err
	}
	defer file.Close()

	return config.Load(config.DefaultConfigFile, file)
}

// arrangeBindings groups configurations by their bind address. For example,
// a server that should listen on localhost and another on 127.0.0.1 will
// be grouped into the same address: 127.0.0.1. It will return an error
// if the address lookup fails or if a TLS listener is configured on the
// same address as a plaintext HTTP listener.
func arrangeBindings(allConfigs []server.Config) (map[string][]server.Config, error) {
	addresses := make(map[string][]server.Config)

	// Group configs by bind address
	for _, conf := range allConfigs {
		addr, err := net.ResolveTCPAddr("tcp", conf.Address())
		if err != nil {
			return addresses, errors.New("Could not serve " + conf.Address() + " - " + err.Error())
		}
		addresses[addr.String()] = append(addresses[addr.String()], conf)
	}

	// Don't allow HTTP and HTTPS to be served on the same address
	for _, configs := range addresses {
		isTLS := configs[0].TLS.Enabled
		for _, config := range configs {
			if config.TLS.Enabled != isTLS {
				thisConfigProto, otherConfigProto := "HTTP", "HTTP"
				if config.TLS.Enabled {
					thisConfigProto = "HTTPS"
				}
				if configs[0].TLS.Enabled {
					otherConfigProto = "HTTPS"
				}
				return addresses, fmt.Errorf("Configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
					configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
			}
		}
	}

	return addresses, nil
}

// setCPU parses string cpu and sets GOMAXPROCS
// according to its value. It accepts either
// a number (e.g. 3) or a percent (e.g. 50%).
func setCPU(cpu string) error {
	var numCPU int

	availCPU := runtime.NumCPU()

	if strings.HasSuffix(cpu, "%") {
		// Percent
		var percent float32
		pctStr := cpu[:len(cpu)-1]
		pctInt, err := strconv.Atoi(pctStr)
		if err != nil || pctInt < 1 || pctInt > 100 {
			return errors.New("Invalid CPU value: percentage must be between 1-100")
		}
		percent = float32(pctInt) / 100
		numCPU = int(float32(availCPU) * percent)
	} else {
		// Number
		num, err := strconv.Atoi(cpu)
		if err != nil || num < 1 {
			return errors.New("Invalid CPU value: provide a number or percent greater than 0")
		}
		numCPU = num
	}

	if numCPU > availCPU {
		numCPU = availCPU
	}

	runtime.GOMAXPROCS(numCPU)
	return nil
}
<|endoftext|>