<commit_before>package main

import (
	"bufio"
	"log"
	"net"
	"net/textproto"
	"os"
	"os/user"
	"regexp"
	"strconv"
	"strings"
)

const version = "1.0.0"

type config struct {
	server        string
	port          string
	nick          string
	user          string
	channel       string
	pass          string
	pread, pwrite chan string
	conn          net.Conn
}

func launchBot() *config {
	return &config{server: "irc.freenode.org",
		port:    "6667",
		nick:    "shelbot",
		channel: "#shelly",
		pass:    "",
		conn:    nil,
		user:    "Sheldon Cooper"}
}

func (bot *config) connect() (conn net.Conn, err error) {
	conn, err = net.Dial("tcp", bot.server+":"+bot.port)
	if err != nil {
		log.Fatal("Failed to connect to IRC server", err)
	}
	bot.conn = conn
	log.Println("Connected to IRC server", bot.server, bot.conn.RemoteAddr())
	return bot.conn, nil
}

func main() {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	logFile, err := os.OpenFile(usr.HomeDir+"/.shelbot.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logFile)

	readKarmaFileJSON()

	ircbot := launchBot()
	conn, _ := ircbot.connect()
	conn.Write([]byte("USER " + ircbot.nick + " 8 * :" + ircbot.user + "\r\n"))
	conn.Write([]byte("NICK " + ircbot.nick + "\r\n"))
	conn.Write([]byte("JOIN " + ircbot.channel + "\r\n"))
	conn.Write([]byte("PRIVMSG " + ircbot.channel + " :Sheldon bot version " + version + " reporting for duty.\r\n"))
	defer conn.Close()

	rKarmaIncrement := regexp.MustCompile(`[A-z]\+\+$`) // matches string++ at EOL
	rKarmaDecrement := regexp.MustCompile(`[A-z]\-\-$`) // matches string-- at EOL

	reader := bufio.NewReader(conn)
	response := textproto.NewReader(reader)
	for {
		line, err := response.ReadLine()
		if err != nil {
			break
		}
		log.Println(line)

		lineElements := strings.Fields(line)
		if lineElements[0] == "PING" {
			conn.Write([]byte("PONG " + lineElements[1] + "\r\n"))
			log.Println("PONG " + lineElements[1])
		} else if rKarmaIncrement.MatchString(lineElements[len(lineElements)-1]) {
			var handle = lineElements[len(lineElements)-1]
			handle = strings.TrimPrefix(handle, ":")
			if err != nil {
				log.Println("Failed to trim prefix", err)
			}
			handle = strings.TrimSuffix(handle, "++")
			if err != nil {
				log.Println("Failed to trim suffix", err)
			}
			karmaTotal := 0
			karmaTotal = karmaIncrement(handle)
			if err != nil {
				log.Println("Error: ", err)
			}
			karmaString := strconv.Itoa(karmaTotal)
			conn.Write([]byte("PRIVMSG " + ircbot.channel + " :Karma for " + handle + " now " + karmaString + "\r\n"))
			log.Println("Karma for " + handle + " now " + karmaString)

			writeKarmaFileJSON()
		} else if rKarmaDecrement.MatchString(lineElements[len(lineElements)-1]) {
			var handle = lineElements[len(lineElements)-1]
			handle = strings.TrimPrefix(handle, ":")
			if err != nil {
				log.Println("Failed to trim prefix", err)
			}
			handle = strings.TrimSuffix(handle, "--")
			if err != nil {
				log.Println("Failed to trim suffix", err)
			}
			karmaTotal := 0
			karmaTotal = karmaDecrement(handle)
			if err != nil {
				log.Println("Error: ", err)
			}
			karmaString := strconv.Itoa(karmaTotal)
			conn.Write([]byte("PRIVMSG " + ircbot.channel + " :Karma for " + handle + " now " + karmaString + "\r\n"))
			log.Println("Karma for " + handle + " now " + karmaString)

			writeKarmaFileJSON()
		}
	}
	logFile.Close()
}
<commit_msg>increment minor version<commit_after>package main

import (
	"bufio"
	"log"
	"net"
	"net/textproto"
	"os"
	"os/user"
	"regexp"
	"strconv"
	"strings"
)

const version = "1.1.0"

type config struct {
	server        string
	port          string
	nick          string
	user          string
	channel       string
	pass          string
	pread, pwrite chan string
	conn          net.Conn
}

func launchBot() *config {
	return &config{server: "irc.freenode.org",
		port:    "6667",
		nick:    "shelbot",
		channel: "#shelly",
		pass:    "",
		conn:    nil,
		user:    "Sheldon Cooper"}
}

func (bot *config) connect() (conn net.Conn, err error) {
	conn, err = net.Dial("tcp", bot.server+":"+bot.port)
	if err != nil {
		log.Fatal("Failed to connect to IRC server", err)
	}
	bot.conn = conn
	log.Println("Connected to IRC server", bot.server, bot.conn.RemoteAddr())
	return bot.conn, nil
}

func main() {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	logFile, err := os.OpenFile(usr.HomeDir+"/.shelbot.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logFile)

	readKarmaFileJSON()

	ircbot := launchBot()
	conn, _ := ircbot.connect()
	conn.Write([]byte("USER " + ircbot.nick + " 8 * :" + ircbot.user + "\r\n"))
	conn.Write([]byte("NICK " + ircbot.nick + "\r\n"))
	conn.Write([]byte("JOIN " + ircbot.channel + "\r\n"))
	conn.Write([]byte("PRIVMSG " + ircbot.channel + " :Sheldon bot version " + version + " reporting for duty.\r\n"))
	defer conn.Close()

	rKarmaIncrement := regexp.MustCompile(`[A-z]\+\+$`) // matches string++ at EOL
	rKarmaDecrement := regexp.MustCompile(`[A-z]\-\-$`) // matches string-- at EOL

	reader := bufio.NewReader(conn)
	response := textproto.NewReader(reader)
	for {
		line, err := response.ReadLine()
		if err != nil {
			break
		}
		log.Println(line)

		lineElements := strings.Fields(line)
		if lineElements[0] == "PING" {
			conn.Write([]byte("PONG " + lineElements[1] + "\r\n"))
			log.Println("PONG " + lineElements[1])
		} else if rKarmaIncrement.MatchString(lineElements[len(lineElements)-1]) {
			var handle = lineElements[len(lineElements)-1]
			handle = strings.TrimPrefix(handle, ":")
			if err != nil {
				log.Println("Failed to trim prefix", err)
			}
			handle = strings.TrimSuffix(handle, "++")
			if err != nil {
				log.Println("Failed to trim suffix", err)
			}
			karmaTotal := 0
			karmaTotal = karmaIncrement(handle)
			if err != nil {
				log.Println("Error: ", err)
			}
			karmaString := strconv.Itoa(karmaTotal)
			conn.Write([]byte("PRIVMSG " + ircbot.channel + " :Karma for " + handle + " now " + karmaString + "\r\n"))
			log.Println("Karma for " + handle + " now " + karmaString)

			writeKarmaFileJSON()
		} else if rKarmaDecrement.MatchString(lineElements[len(lineElements)-1]) {
			var handle = lineElements[len(lineElements)-1]
			handle = strings.TrimPrefix(handle, ":")
			if err != nil {
				log.Println("Failed to trim prefix", err)
			}
			handle = strings.TrimSuffix(handle, "--")
			if err != nil {
				log.Println("Failed to trim suffix", err)
			}
			karmaTotal := 0
			karmaTotal = karmaDecrement(handle)
			if err != nil {
				log.Println("Error: ", err)
			}
			karmaString := strconv.Itoa(karmaTotal)
			conn.Write([]byte("PRIVMSG " + ircbot.channel + " :Karma for " + handle + " now " + karmaString + "\r\n"))
			log.Println("Karma for " + handle + " now " + karmaString)

			writeKarmaFileJSON()
		}
	}
	logFile.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	//"io/ioutil"
	"net/http"
	"os"
	"time"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	namespace  = "rabbitmq"
	configPath = "config.json"
)

type Exporter struct {
	mutex   					sync.RWMutex
	lastSeen					prometheus.Counter
	connections_total 			prometheus.Gauge
	channels_total              prometheus.Gauge
	queues_total                prometheus.Gauge
	consumers_total             prometheus.Gauge
	exchanges_total             prometheus.Gauge
	messages_count              *prometheus.GaugeVec
	consumers_count             *prometheus.GaugeVec
	message_bytes               *prometheus.GaugeVec
}
var log = logrus.New()

// Listed available metrics
func newExporter() *Exporter {
	return &Exporter{
		lastSeen: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "last_seen",
			Help:      "Last time rabbitmq was seen by the exporter",
		}),
		connections_total: prometheus.NewGauge(
		prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "connections_total",
			Help:      "Total number of open connections.",
		}),
		channels_total : prometheus.NewGauge(
		prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "channels_total",
			Help:      "Total number of open channels.",
		}),
		queues_total : prometheus.NewGauge(
		prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "queues_total",
			Help:      "Total number of queues in use.",
		}),
		consumers_total : prometheus.NewGauge(
		prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "consumers_total",
			Help:      "Total number of message consumers.",
		}),
		exchanges_total : prometheus.NewGauge(
		prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "exchanges_total",
			Help:      "Total number of exchanges in use.",
		}),
		messages_count : prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "messages_count",
			Help:      "Current length of queue.",
		},
			append([]string{"queue"}),
		),
		consumers_count : prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "consumers_count",
			Help:      "Number of consumers subscribed to queue.",
		},
			append([]string{"queue"}),
		),
		message_bytes : prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "message_bytes",
			Help:      "Current bytes to store queue.",
		},
			append([]string{"queue"}),
		),
	}
}
/*
type Config struct {
	Node     *Node `json:"node"`
	Port     string  `json:"port"`
}

type Node struct {
	Name     string `json:"name"`
	Url      string `json:"url"`
	Uname    string `json:"uname"`
	Password string `json:"password"`
}*/

type QueueMetrics struct {
	messages_count float64
	consumers_count float64
	message_bytes float64
}

func unpackQueueMetrics(d *json.Decoder) map[string]*QueueMetrics {
	var output []map[string]interface{}

	if err := d.Decode(&output); err != nil {
		log.Error(err)
	}
	metrics := make(map[string]*QueueMetrics)

	for _, v := range output {
		metrics[v["name"].(string)] = &QueueMetrics{
			messages_count : v["messages"].(float64),
			consumers_count : v["consumers"].(float64),
			message_bytes : v["message_bytes"].(float64),
		}
	}
	return metrics
}

func unpackOverviewMetrics(d *json.Decoder) map[string]float64 {
	var output map[string]interface{}

	if err := d.Decode(&output); err != nil {
		log.Error(err)
	}
	metrics := make(map[string]float64)

	for k, v := range output["object_totals"].(map[string]interface{}) {
		metrics[k] = v.(float64)
	}
	return metrics
}

func getMetrics(hostname, username, password, endpoint string) *json.Decoder {
	client := &http.Client{}
	req, err := http.NewRequest("GET", hostname+"/api/" + endpoint, nil)
	req.SetBasicAuth(username, password)

	resp, err := client.Do(req)

	if err != nil {
		log.Error(err)
	}
	return json.NewDecoder(resp.Body)
}

func updateMetrics(overviewMetrics map[string]float64, queueMetrics map[string]*QueueMetrics, exporter *Exporter) {

	exporter.lastSeen.Set(float64(time.Now().Unix()))

	exporter.channels_total.Set(overviewMetrics["channels"])
	exporter.connections_total.Set(overviewMetrics["connections"])
	exporter.consumers_total.Set(overviewMetrics["consumers"])
	exporter.queues_total.Set(overviewMetrics["queues"])
	exporter.exchanges_total.Set(overviewMetrics["exchanges"])

	for queue, stat := range queueMetrics {
		exporter.messages_count.WithLabelValues(queue).Set(stat.messages_count)
		exporter.consumers_count.WithLabelValues(queue).Set(stat.consumers_count)
		exporter.message_bytes.WithLabelValues(queue).Set(stat.message_bytes)
	}
}
/*
func newConfig(path string) (*Config, error) {
	var config Config

	file, err := ioutil.ReadFile(path)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	err = json.Unmarshal(file, &config)
	return &config, err
}*/

func main() {
	log.Out = os.Stdout
	//config, _ := newConfig(configPath)
	exporter := newExporter()
	prometheus.MustRegister(exporter)

	http.Handle("/metrics", prometheus.Handler())
	log.Infof("Starting RabbitMQ exporter on port: %s.", os.Getenv("PUBLISH_PORT"))
	http.ListenAndServe(":"+os.Getenv("PUBLISH_PORT"), nil)
}

func (e *Exporter)fetchRabbit() {

	Url := os.Getenv("RABBIT_URL")
	Uname := os.Getenv("RABBIT_USER")
	Password := os.Getenv("RABBIT_PASSWORD")

	overviewDecoder := getMetrics(Url, Uname, Password, "overview")
	overviewMetrics := unpackOverviewMetrics(overviewDecoder)
	queueDecoder := getMetrics(Url, Uname, Password, "queues")
	queueMetrics := unpackQueueMetrics(queueDecoder)

	updateMetrics(overviewMetrics, queueMetrics, e)
	log.Info("Metrics updated successfully.")
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	e.lastSeen.Describe(ch)
	e.connections_total.Describe(ch)
	e.channels_total.Describe(ch)
	e.queues_total.Describe(ch)
	e.consumers_total.Describe(ch)
	e.exchanges_total.Describe(ch)
	e.messages_count.Describe(ch)
	e.consumers_count.Describe(ch)
	e.message_bytes.Describe(ch)
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	e.mutex.Lock() // To protect metrics from concurrent collects.
	defer e.mutex.Unlock()

	e.messages_count.Reset()
	e.consumers_count.Reset()
	e.message_bytes.Reset()

	e.fetchRabbit()

	e.connections_total.Collect(ch)
	e.channels_total.Collect(ch)
	e.queues_total.Collect(ch)
	e.consumers_total.Collect(ch)
	e.exchanges_total.Collect(ch)
	e.messages_count.Collect(ch)
	e.consumers_count.Collect(ch)
	e.message_bytes.Collect(ch)
}<commit_msg>added more queue statistics<commit_after>package main

import (
	"encoding/json"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	namespace = "rabbitmq"
)

type Exporter struct {
	mutex                         sync.RWMutex
	lastSeen                      prometheus.Counter
	connections_total             prometheus.Gauge
	channels_total                prometheus.Gauge
	queues_total                  prometheus.Gauge
	consumers_total               prometheus.Gauge
	exchanges_total               prometheus.Gauge
	messages_count                *prometheus.GaugeVec
	messages_ready_count          *prometheus.GaugeVec
	messages_unacknowledged_count *prometheus.GaugeVec
	consumers_count               *prometheus.GaugeVec
	message_bytes                 *prometheus.GaugeVec
	disk_reads_count              *prometheus.GaugeVec
	disk_writes_count             *prometheus.GaugeVec
}

var (
	log             = logrus.New()
	queueLabelNames = []string{"queue"}
)

// Listed available metrics
func newExporter() *Exporter {
	return &Exporter{
		lastSeen: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "last_seen",
			Help:      "Last time rabbitmq was seen by the exporter",
		}),
		connections_total: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "connections_total",
				Help:      "Total number of open connections.",
			}),
		channels_total: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "channels_total",
				Help:      "Total number of open channels.",
			}),
		queues_total: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "queues_total",
				Help:      "Total number of queues in use.",
			}),
		consumers_total: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "consumers_total",
				Help:      "Total number of message consumers.",
			}),
		exchanges_total: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: namespace,
				Name:      "exchanges_total",
				Help:      "Total number of exchanges in use.",
			}),
		messages_count: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "messages_count",
			Help:      "Current length of queue.",
		},
			queueLabelNames,
		),
		messages_ready_count: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "messages_ready_count",
			Help:      "Number of messages ready to be delivered to clients.",
		},
			queueLabelNames,
		),
		messages_unacknowledged_count: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "messages_unacknowledged_count",
			Help:      "Number of messages delivered to clients but not yet acknowledged.",
		},
			queueLabelNames,
		),
		consumers_count: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "consumers_count",
			Help:      "Number of consumers subscribed to queue",
		},
			queueLabelNames,
		),
		message_bytes: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "message_bytes",
			Help:      "Current bytes to store queue.",
		},
			queueLabelNames,
		),
		disk_reads_count: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "disk_reads_count",
			Help:      "Total number of times messages have been read from disk by this queue since it started.",
		},
			queueLabelNames,
		),
		disk_writes_count: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "disk_writes_count",
			Help:      "Total number of times messages have been written to disk by this queue since it started.",
		},
			queueLabelNames,
		),
	}
}

type QueueMetrics struct {
	messages_count                float64
	messages_ready_count          float64
	messages_unacknowledged_count float64
	consumers_count               float64
	message_bytes                 float64
	disk_reads_count              float64
	disk_writes_count             float64
}

func unpackQueueMetrics(d *json.Decoder) map[string]*QueueMetrics {
	var output []map[string]interface{}

	if err := d.Decode(&output); err != nil {
		log.Error(err)
	}
	metrics := make(map[string]*QueueMetrics)

	for _, v := range output {
		metrics[v["name"].(string)] = &QueueMetrics{
			messages_count:                v["messages"].(float64),
			messages_ready_count:          v["messages_ready"].(float64),
			messages_unacknowledged_count: v["messages_unacknowledged"].(float64),
			consumers_count:               v["consumers"].(float64),
			message_bytes:                 v["message_bytes"].(float64),
			disk_reads_count:              v["disk_reads"].(float64),
			disk_writes_count:             v["disk_writes"].(float64),
		}
	}
	return metrics
}

func unpackOverviewMetrics(d *json.Decoder) map[string]float64 {
	var output map[string]interface{}

	if err := d.Decode(&output); err != nil {
		log.Error(err)
	}
	metrics := make(map[string]float64)

	for k, v := range output["object_totals"].(map[string]interface{}) {
		metrics[k] = v.(float64)
	}
	return metrics
}

func getMetrics(hostname, username, password, endpoint string) *json.Decoder {
	client := &http.Client{}
	req, err := http.NewRequest("GET", hostname+"/api/"+endpoint, nil)
	req.SetBasicAuth(username, password)

	resp, err := client.Do(req)

	if err != nil {
		log.Error(err)
	}
	return json.NewDecoder(resp.Body)
}

func updateMetrics(overviewMetrics map[string]float64, queueMetrics map[string]*QueueMetrics, exporter *Exporter) {

	exporter.lastSeen.Set(float64(time.Now().Unix()))

	exporter.channels_total.Set(overviewMetrics["channels"])
	exporter.connections_total.Set(overviewMetrics["connections"])
	exporter.consumers_total.Set(overviewMetrics["consumers"])
	exporter.queues_total.Set(overviewMetrics["queues"])
	exporter.exchanges_total.Set(overviewMetrics["exchanges"])

	for queue, stat := range queueMetrics {
		exporter.messages_count.WithLabelValues(queue).Set(stat.messages_count)
		exporter.messages_ready_count.WithLabelValues(queue).Set(stat.messages_ready_count)
		exporter.messages_unacknowledged_count.WithLabelValues(queue).Set(stat.messages_unacknowledged_count)
		exporter.consumers_count.WithLabelValues(queue).Set(stat.consumers_count)
		exporter.message_bytes.WithLabelValues(queue).Set(stat.message_bytes)
		exporter.disk_reads_count.WithLabelValues(queue).Set(stat.disk_reads_count)
		exporter.disk_writes_count.WithLabelValues(queue).Set(stat.disk_writes_count)
	}
}

func main() {
	log.Out = os.Stdout
	exporter := newExporter()
	prometheus.MustRegister(exporter)

	http.Handle("/metrics", prometheus.Handler())
	log.Infof("Starting RabbitMQ exporter on port: %s.", os.Getenv("PUBLISH_PORT"))
	http.ListenAndServe(":"+os.Getenv("PUBLISH_PORT"), nil)
}

func (e *Exporter) fetchRabbit() {

	Url := os.Getenv("RABBIT_URL")
	Uname := os.Getenv("RABBIT_USER")
	Password := os.Getenv("RABBIT_PASSWORD")

	overviewDecoder := getMetrics(Url, Uname, Password, "overview")
	overviewMetrics := unpackOverviewMetrics(overviewDecoder)
	queueDecoder := getMetrics(Url, Uname, Password, "queues")
	queueMetrics := unpackQueueMetrics(queueDecoder)

	updateMetrics(overviewMetrics, queueMetrics, e)
	log.Info("Metrics updated successfully.")
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	e.lastSeen.Describe(ch)
	e.connections_total.Describe(ch)
	e.channels_total.Describe(ch)
	e.queues_total.Describe(ch)
	e.consumers_total.Describe(ch)
	e.exchanges_total.Describe(ch)
	e.messages_count.Describe(ch)
	e.messages_ready_count.Describe(ch)
	e.messages_unacknowledged_count.Describe(ch)
	e.consumers_count.Describe(ch)
	e.message_bytes.Describe(ch)
	e.disk_reads_count.Describe(ch)
	e.disk_writes_count.Describe(ch)
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	e.mutex.Lock() // To protect metrics from concurrent collects.
	defer e.mutex.Unlock()

	e.messages_count.Reset()
	e.messages_ready_count.Reset()
	e.messages_unacknowledged_count.Reset()
	e.consumers_count.Reset()
	e.message_bytes.Reset()
	e.disk_reads_count.Reset()
	e.disk_writes_count.Reset()

	e.fetchRabbit()

	e.connections_total.Collect(ch)
	e.channels_total.Collect(ch)
	e.queues_total.Collect(ch)
	e.consumers_total.Collect(ch)
	e.exchanges_total.Collect(ch)
	e.messages_count.Collect(ch)
	e.messages_ready_count.Collect(ch)
	e.messages_unacknowledged_count.Collect(ch)
	e.consumers_count.Collect(ch)
	e.message_bytes.Collect(ch)
	e.disk_reads_count.Collect(ch)
	e.disk_writes_count.Collect(ch)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"
	shellwords "github.com/mattn/go-shellwords"
	"gopkg.in/urfave/cli.v1"

	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	buildError error
	colorGreen = string([]byte{27, 91, 57, 55, 59, 51, 50, 59, 49, 109})
	colorRed   = string([]byte{27, 91, 57, 55, 59, 51, 49, 59, 49, 109})
	colorReset = string([]byte{27, 91, 48, 109})
)

func main() {
	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A live reload utility for Go web applications."
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "laddr,l",
			Value: "",
			Usage: "listening address for the proxy server",
		},
		cli.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "port for the proxy server",
		},
		cli.IntFlag{
			Name:  "appPort,a",
			Value: 3001,
			Usage: "port for the Go web server",
		},
		cli.StringFlag{
			Name:  "bin,b",
			Value: "gin-bin",
			Usage: "name of generated binary file",
		},
		cli.StringFlag{
			Name:  "path,t",
			Value: ".",
			Usage: "Path to watch files from",
		},
		cli.StringSliceFlag{
			Name:  "excludeDir,x",
			Value: &cli.StringSlice{},
			Usage: "Relative directories to exclude",
		},
		cli.BoolFlag{
			Name:  "immediate,i",
			Usage: "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:  "godep,g",
			Usage: "use godep when building",
		},
		cli.StringFlag{
			Name:  "buildArgs",
			Usage: "Additional go build arguments",
		},
		cli.StringFlag{
			Name:  "certFile",
			Usage: "TLS Certificate",
		},
		cli.StringFlag{
			Name:  "keyFile",
			Usage: "TLS Certificate Key",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "Run the gin proxy in the current working directory",
			Action:    MainAction,
		},
		{
			Name:      "env",
			ShortName: "e",
			Usage:     "Display environment variables set by the .env file",
			Action:    EnvAction,
		},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {
	laddr := c.GlobalString("laddr")
	port := c.GlobalInt("port")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")
	keyFile := c.GlobalString("keyFile")
	certFile := c.GlobalString("certFile")

	// Bootstrap the environment
	envy.Bootstrap()

	// Set the PORT env
	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	buildArgs, err := shellwords.Parse(c.GlobalString("buildArgs"))
	if err != nil {
		logger.Fatal(err)
	}

	builder := gin.NewBuilder(c.GlobalString("path"), c.GlobalString("bin"), c.GlobalBool("godep"), wd, buildArgs)
	runner := gin.NewRunner(filepath.Join(wd, builder.Binary()), c.Args()...)
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Laddr:    laddr,
		Port:     port,
		ProxyTo:  "http://localhost:" + appPort,
		KeyFile:  keyFile,
		CertFile: certFile,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	if laddr != "" {
		logger.Printf("listening at %s:%d\n", laddr, port)
	} else {
		logger.Printf("listening on port %d\n", port)
	}

	shutdown(runner)

	// build right now
	build(builder, runner, logger)

	// scan for changes
	scanChanges(c.GlobalString("path"), c.GlobalStringSlice("excludeDir"), func(path string) {
		runner.Kill()
		build(builder, runner, logger)
	})
}

func EnvAction(c *cli.Context) {
	// Bootstrap the environment
	env, err := envy.Bootstrap()
	if err != nil {
		logger.Fatalln(err)
	}

	for k, v := range env {
		fmt.Printf("%s: %s\n", k, v)
	}

}

func build(builder gin.Builder, runner gin.Runner, logger *log.Logger) {
	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Printf("%sERROR! Build failed.%s\n", colorRed, colorReset)
		fmt.Println(builder.Errors())
	} else {
		// print success only if there were errors before
		if buildError != nil {
			logger.Printf("%sBuild Successful%s\n", colorGreen, colorReset)
		}
		buildError = nil
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, excludeDirs []string, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" {
				return filepath.SkipDir
			}
			for _, x := range excludeDirs {
				if x == path {
					return filepath.SkipDir
				}
			}

			// ignore hidden files
			if filepath.Base(path)[0] == '.' {
				return nil
			}

			if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}

func shutdown(runner gin.Runner) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		s := <-c
		log.Println("Got signal: ", s)
		err := runner.Kill()
		if err != nil {
			log.Print("Error killing: ", err)
		}
		os.Exit(1)
	}()
}
<commit_msg>added --all flag<commit_after>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"
	shellwords "github.com/mattn/go-shellwords"
	"gopkg.in/urfave/cli.v1"

	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	buildError error
	colorGreen = string([]byte{27, 91, 57, 55, 59, 51, 50, 59, 49, 109})
	colorRed   = string([]byte{27, 91, 57, 55, 59, 51, 49, 59, 49, 109})
	colorReset = string([]byte{27, 91, 48, 109})
)

func main() {
	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A live reload utility for Go web applications."
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "laddr,l",
			Value: "",
			Usage: "listening address for the proxy server",
		},
		cli.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "port for the proxy server",
		},
		cli.IntFlag{
			Name:  "appPort,a",
			Value: 3001,
			Usage: "port for the Go web server",
		},
		cli.StringFlag{
			Name:  "bin,b",
			Value: "gin-bin",
			Usage: "name of generated binary file",
		},
		cli.StringFlag{
			Name:  "path,t",
			Value: ".",
			Usage: "Path to watch files from",
		},
		cli.StringSliceFlag{
			Name:  "excludeDir,x",
			Value: &cli.StringSlice{},
			Usage: "Relative directories to exclude",
		},
		cli.BoolFlag{
			Name:  "immediate,i",
			Usage: "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:  "all",
			Usage: "reloads whenever any file changes, as opposed to reloading only on .go file change",
		},
		cli.BoolFlag{
			Name:  "godep,g",
			Usage: "use godep when building",
		},
		cli.StringFlag{
			Name:  "buildArgs",
			Usage: "Additional go build arguments",
		},
		cli.StringFlag{
			Name:  "certFile",
			Usage: "TLS Certificate",
		},
		cli.StringFlag{
			Name:  "keyFile",
			Usage: "TLS Certificate Key",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "Run the gin proxy in the current working directory",
			Action:    MainAction,
		},
		{
			Name:      "env",
			ShortName: "e",
			Usage:     "Display environment variables set by the .env file",
			Action:    EnvAction,
		},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {
	laddr := c.GlobalString("laddr")
	port := c.GlobalInt("port")
	all := c.GlobalBool("all")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")
	keyFile := c.GlobalString("keyFile")
	certFile := c.GlobalString("certFile")

	// Bootstrap the environment
	envy.Bootstrap()

	// Set the PORT env
	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	buildArgs, err := shellwords.Parse(c.GlobalString("buildArgs"))
	if err != nil {
		logger.Fatal(err)
	}

	builder := gin.NewBuilder(c.GlobalString("path"), c.GlobalString("bin"), c.GlobalBool("godep"), wd, buildArgs)
	runner := gin.NewRunner(filepath.Join(wd, builder.Binary()), c.Args()...)
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Laddr:    laddr,
		Port:     port,
		ProxyTo:  "http://localhost:" + appPort,
		KeyFile:  keyFile,
		CertFile: certFile,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	if laddr != "" {
		logger.Printf("listening at %s:%d\n", laddr, port)
	} else {
		logger.Printf("listening on port %d\n", port)
	}

	shutdown(runner)

	// build right now
	build(builder, runner, logger)

	// scan for changes
	scanChanges(c.GlobalString("path"), c.GlobalStringSlice("excludeDir"), all, func(path string) {
		runner.Kill()
		build(builder, runner, logger)
	})
}

func EnvAction(c *cli.Context) {
	// Bootstrap the environment
	env, err := envy.Bootstrap()
	if err != nil {
		logger.Fatalln(err)
	}

	for k, v := range env {
		fmt.Printf("%s: %s\n", k, v)
	}

}

func build(builder gin.Builder, runner gin.Runner, logger *log.Logger) {
	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Printf("%sERROR! Build failed.%s\n", colorRed, colorReset)
		fmt.Println(builder.Errors())
	} else {
		// print success only if there were errors before
		if buildError != nil {
			logger.Printf("%sBuild Successful%s\n", colorGreen, colorReset)
		}
		buildError = nil
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, excludeDirs []string, allFiles bool, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" {
				return filepath.SkipDir
			}
			for _, x := range excludeDirs {
				if x == path {
					return filepath.SkipDir
				}
			}

			// ignore hidden files
			if filepath.Base(path)[0] == '.' {
				return nil
			}

			if (allFiles || filepath.Ext(path) == ".go") && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}

func shutdown(runner gin.Runner) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		s := <-c
		log.Println("Got signal: ", s)
		err := runner.Kill()
		if err != nil {
			log.Print("Error killing: ", err)
		}
		os.Exit(1)
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"log"
	"os"
	"strings"

	"github.com/google/go-github/github"
)

func makeGist(fileBody, fileName string) string {
	if fileName == "" {
		fileName = "File one"
	}
	gistFilename := github.GistFilename(fileName)
	isPublic := false
	desc := ""
	gf := github.GistFile{Content: &fileBody}
	files := map[github.GistFilename]github.GistFile{gistFilename: gf}

	newGist := &github.Gist{
		Files:       files,
		Public:      &isPublic,
		Description: &desc}

	clt := github.NewClient(nil).Gists
	gist, _, err := clt.Create(newGist)

	if err != nil {
		log.Fatal("Upload error:", err)
	}

	return *gist.HTMLURL
}

func getStringsFromReader(reader *bufio.Reader) []string {
	result := make([]string, 0)

	for {
		line, err := reader.ReadString('\n')
		if err == nil || err == io.EOF {
			result = append(result, line)
		}

		if err != nil {
			break
		}
	}

	return result
}

func stdInToString() []string {
	reader := bufio.NewReader(os.Stdin)
	return getStringsFromReader(reader)
}

func fileToString(fileName string) []string {
	file, err := os.Open(os.Args[1])

	if err != nil {
		log.Fatal(err)
	}

	reader := bufio.NewReader(file)
	return getStringsFromReader(reader)
}

func main() {
	var body []string
	fileName := ""

	if len(os.Args) > 1 {
		fileName = os.Args[1]
		body = fileToString(fileName)
	} else {
		body = stdInToString()
	}

	url := makeGist(strings.Join(body, ""), fileName)
	println(url)
}
<commit_msg>Stripping to base file path for gist compatibility<commit_after>package main

import (
	"bufio"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/go-github/github"
)

func makeGist(fileBody, fileName string) string {
	if fileName == "" {
		fileName = "File one"
	}
	gistFilename := github.GistFilename(filepath.Base(fileName))
	isPublic := false
	desc := ""
	gf := github.GistFile{Content: &fileBody}
	files := map[github.GistFilename]github.GistFile{gistFilename: gf}

	newGist := &github.Gist{
		Files:       files,
		Public:      &isPublic,
		Description: &desc}

	clt := github.NewClient(nil).Gists
	gist, _, err := clt.Create(newGist)

	if err != nil {
		log.Fatal("Upload error:", err)
	}

	return *gist.HTMLURL
}

func getStringsFromReader(reader *bufio.Reader) []string {
	result := make([]string, 0)

	for {
		line, err := reader.ReadString('\n')
		if err == nil || err == io.EOF {
			result = append(result, line)
		}

		if err != nil {
			break
		}
	}

	return result
}

func stdInToString() []string {
	reader := bufio.NewReader(os.Stdin)
	return getStringsFromReader(reader)
}

func fileToString(fileName string) []string {
	file, err := os.Open(os.Args[1])

	if err != nil {
		log.Fatal(err)
	}

	reader := bufio.NewReader(file)
	return getStringsFromReader(reader)
}

func main() {
	var body []string
	fileName := ""

	if len(os.Args) > 1 {
		fileName = os.Args[1]
		body = fileToString(fileName)
	} else {
		body = stdInToString()
	}

	url := makeGist(strings.Join(body, ""), fileName)
	println(url)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"text/template"
	"time"
)

const (
	httpListenPort                   = 8081
	refreshRate                      = 60 //in seconds
	wikiURI                          = "https://wiki.tox.chat/users/nodes?do=export_raw"
	maxUDPPacketSize                 = 2048
	getNodesPacketID                 = 2
	sendNodesIpv6PacketID            = 4
	bootstrapInfoPacketID            = 240
	bootstrapInfoPacketLength        = 78
	tcpHandshakePacketLength         = 128
	tcpHandshakeResponsePacketLength = 96
	maxMOTDLength                    = 256
	queryTimeout                     = 4 //in seconds
	dialerTimeout                    = 4 //in seconds
)

var (
	lastScan     int64
	nodesList    = list.New()
	crypto, _    = NewCrypto()
	tcpPorts     = []int{443, 3389, 33445}
	lowerFuncMap = template.FuncMap{"lower": strings.ToLower}
)

//flags
var (
	networkFlag = flag.String("net", "udp", "network type, either 'udp' or 'tcp'")
	ipFlag      = flag.String("ip", "127.0.0.1", "ip address to scan, can only be ipv4 for now")
	portFlag    = flag.Int("port", 33445, "port(s) to scan")
	keyFlag     = flag.String("key", "", "public key of the node")
)

type tcpHandshakeResult struct {
	Port  int
	Error error
}

type toxStatus struct {
	LastScan       int64     `json:"last_scan"`
	LastScanString string    `json:"last_scan_string"`
	Nodes          []toxNode `json:"nodes"`
}

type toxNode struct {
	Ipv4Address    string `json:"ipv4"`
	Ipv6Address    string `json:"ipv6"`
	Port           int    `json:"port"`
	TCPPorts       []int  `json:"tcp_ports"`
	PublicKey      string `json:"public_key"`
	Maintainer     string `json:"maintainer"`
	Location       string `json:"location"`
	Status         bool   `json:"status"`
	Version        string `json:"version"`
	MOTD           string `json:"motd"`
	LastPing       int64  `json:"last_ping"`
	LastPingString string `json:"last_ping_string"`
}

func main() {
	if crypto == nil {
		log.Fatalf("Could not generate keypair")
	}

	if handleFlags() {
		return
	}

	go probeLoop()

	http.HandleFunc("/", handleHTTPRequest)
	http.HandleFunc("/json", handleJSONRequest)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", httpListenPort), nil))
}

func handleFlags() bool {
	if len(os.Args) < 2 {
		return false
	}

	flag.Parse()

	if len(*keyFlag) != 64 {
		log.Fatalln("error: public key must have a lenght of 64 hex characters")
	}

	node := toxNode{}
	node.Ipv4Address = *ipFlag
	node.PublicKey = *keyFlag
	node.Port = *portFlag

	if *networkFlag == "udp" {
		probeNode(&node)
	} else if *networkFlag == "tcp" {

	} else {
		log.Fatalf("error: unsupported network specified: %s", *networkFlag)
	}

	return true
}

func handleHTTPRequest(w http.ResponseWriter, r *http.Request) {
	urlPath := r.URL.Path[1:]
	if r.URL.Path == "/" {
		renderMainPage(w, "index.html")
		return
	}

	//TODO: make this more efficient
	data, err := ioutil.ReadFile(path.Join("./assets/", string(urlPath)))
	if err != nil {
		http.Error(w, http.StatusText(404), 404)
	} else {
		w.Write(data)
	}
}

func renderMainPage(w http.ResponseWriter, urlPath string) {
	tmpl, err := template.New("index.html").
		Funcs(lowerFuncMap).
		ParseFiles(path.Join("./assets/", string(urlPath)))

	if err != nil {
		http.Error(w, http.StatusText(500), 500)
		log.Printf("Internal server error while trying to serve index: %s", err.Error())
	} else {
		nodes := nodesListToSlice(nodesList)
		response := toxStatus{lastScan, time.Unix(lastScan, 0).String(), nodes}
		tmpl.Execute(w, response)
	}
}

func handleJSONRequest(w http.ResponseWriter, r *http.Request) {
	nodes := nodesListToSlice(nodesList)
	response := toxStatus{lastScan, time.Unix(lastScan, 0).String(), nodes}

	bytes, err := json.Marshal(response)
	if err != nil {
		http.Error(w, http.StatusText(500), 500)
		return
	}

	w.Write(bytes)
}

func probeLoop() {
	for {
		nodes, err := parseNodes()
		if err != nil {
			log.Printf("Error while trying to parse nodes: %s", err.Error())
		} else {
			c := make(chan *toxNode)
			for e := nodes.Front(); e != nil; e = e.Next() {
				node, _ := e.Value.(*toxNode)
				go func() { c <- probeNode(node) }()
			}

			for i := 0; i < nodes.Len(); i++ {
				_ = <-c
			}

			nodesList = nodes
			lastScan = time.Now().Unix()
		}

		time.Sleep(refreshRate * time.Second)
	}
}

func probeNode(node *toxNode) *toxNode {
	conn, err := newNodeConn(node, node.Port, "udp")
	if err != nil {
		return node
	}

	err = getBootstrapInfo(node, conn)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
	}

	conn.Close()
	conn, err = newNodeConn(node, node.Port, "udp")
	if err != nil {
		return node
	}

	err = getNodes(node, conn)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return node
	}
	conn.Close()

	ports := tcpPorts
	if !contains(tcpPorts, node.Port) {
		ports = append(ports, node.Port)
	}

	c := make(chan tcpHandshakeResult)
	for _, port := range ports {
		go func(p int) {
			conn, err = newNodeConn(node, p, "tcp")
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				c <- tcpHandshakeResult{p, err}
			} else {
				c <- tryTCPHandshake(node, conn, p)
			}
		}(port)
	}

	for i := 0; i < len(ports); i++ {
		result := <-c
		if result.Error != nil {
			fmt.Printf("%s\n", result.Error.Error())
		} else {
			node.TCPPorts = append(node.TCPPorts, result.Port)
		}
	}

	node.LastPing = time.Now().Unix()
	node.Status = true
	return node
}

func getNodes(node *toxNode, conn net.Conn) error {
	nodePublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return err
	}

	plain := make([]byte, len(crypto.PublicKey)+8)
	copy(plain, crypto.PublicKey)
	copy(plain[len(crypto.PublicKey):], nextBytes(8)) //ping id

	nonce := nextNonce()
	sharedKey := crypto.CreateSharedKey(nodePublicKey)
	encrypted := encryptData(plain, sharedKey, nonce)[16:]

	payload := make([]byte, 1+len(crypto.PublicKey)+len(nonce)+len(encrypted))
	payload[0] = getNodesPacketID
	copy(payload[1:], crypto.PublicKey)
	copy(payload[1+len(crypto.PublicKey):], nonce)
	copy(payload[1+len(crypto.PublicKey)+len(nonce):], encrypted)
	conn.Write(payload)

	buffer := make([]byte, maxUDPPacketSize)
	_, err = conn.Read(buffer)

	if err != nil {
		return err
	} /*else if payload[0] != sendNodesIpv6PacketID {
		return fmt.Errorf("packet id: %d is not a sendnodesipv6 packet", payload[0])
	}

	right now we're happy if a node responds to our 'getnodes' request, without even validating the response
	this needs some more work

	on a side note: it looks like nodes are sending a 'getnodes' packet before 'sendnodesipv6',
	*/

	return nil
}

func getBootstrapInfo(node *toxNode, conn net.Conn) error {
	payload := make([]byte, bootstrapInfoPacketLength)
	payload[0] = bootstrapInfoPacketID
	conn.Write(payload)

	buffer := make([]byte, 1+4+maxMOTDLength)
	read, err := conn.Read(buffer)

	if err != nil {
		return err
	} else if buffer[0] != bootstrapInfoPacketID {
		return fmt.Errorf("packet id: %d is not a bootstrap info packet", buffer[0])
	}

	buffer = buffer[:read]
	if len(buffer) < 1+4 {
		return errors.New("bootstrap info packet too small")
	}

	node.Version = fmt.Sprintf("%d", binary.BigEndian.Uint32(buffer[1:1+4]))
	node.MOTD = string(bytes.Trim(buffer[1+4:], "\x00"))
	return nil
}

func tryTCPHandshake(node *toxNode, conn net.Conn, port int) tcpHandshakeResult {
	/* NOTE: conn is closed at the end of this function */
	nodePublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return tcpHandshakeResult{port, err}
	}

	nonce := nextNonce()
	baseNonce := nextNonce()
	plain := make([]byte, len(crypto.PublicKey)+len(baseNonce))
	tempCrypto, _ := NewCrypto()

	copy(plain, tempCrypto.PublicKey)
	copy(plain[len(tempCrypto.PublicKey):], baseNonce)
	sharedKey := crypto.CreateSharedKey(nodePublicKey)
	encrypted := encryptData(plain, sharedKey, nonce)[16:]

	payload := make([]byte, tcpHandshakePacketLength)
	copy(payload, crypto.PublicKey)
	copy(payload[len(crypto.PublicKey):], nonce)
	copy(payload[len(crypto.PublicKey)+len(nonce):], encrypted)
	conn.Write(payload)

	buffer := make([]byte, tcpHandshakeResponsePacketLength)
	read, err := conn.Read(buffer)

	var result tcpHandshakeResult

	if err != nil {
		result = tcpHandshakeResult{port, err}
	} else if read != tcpHandshakeResponsePacketLength {
		result = tcpHandshakeResult{
			port,
			errors.New("tcp handshake response has an incorrect length"),
		}
	} else {
		result = tcpHandshakeResult{port, nil}
	}

	conn.Close()
	return result
}

func newNodeConn(node *toxNode, port int, network string) (net.Conn, error) {
	dialer := net.Dialer{}
	dialer.Deadline = time.Now().Add(dialerTimeout * time.Second)

	conn, err := dialer.Dial(network, fmt.Sprintf("%s:%d", node.Ipv4Address, port))
	if err != nil {
		return nil, err
	}

	conn.SetReadDeadline(time.Now().Add(queryTimeout * time.Second))
	return conn, nil
}

func parseNode(nodeString string) *toxNode {
	nodeString = stripSpaces(nodeString)
	if !strings.HasPrefix(nodeString, "|") {
		return nil
	}

	lineParts := strings.Split(nodeString, "|")
	if port, err := strconv.Atoi(strings.TrimSpace(lineParts[3])); err == nil && len(lineParts) == 8 {
		node := toxNode{
			strings.TrimSpace(lineParts[1]),
			strings.TrimSpace(lineParts[2]),
			port,
			[]int{},
			strings.TrimSpace(lineParts[4]),
			strings.TrimSpace(lineParts[5]),
			strings.TrimSpace(lineParts[6]),
			false,
			"",
			"",
			0,
			"Never",
		}

		if node.Ipv6Address == "NONE" {
			node.Ipv6Address = "-"
		}

		return &node
	}

	return nil
}

func parseNodes() (*list.List, error) {
	res, err := http.Get(wikiURI)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	nodes := list.New()
	content, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	lines := strings.Split(string(content), "\n")
	for _, line := range lines {
		node := parseNode(line)
		if node == nil {
			continue
		}

		oldNode := getOldNode(node.PublicKey)
		if oldNode != nil { //transfer last ping info
			node.LastPing = oldNode.LastPing
			node.LastPingString = oldNode.LastPingString
		}

		nodes.PushBack(node)
	}
	return nodes, nil
}

func getOldNode(publicKey string) *toxNode {
	for e := nodesList.Front(); e != nil; e = e.Next() {
		node, _ := e.Value.(*toxNode)
		if node.PublicKey == publicKey {
			return node
		}
	}
	return nil
}
<commit_msg>Command line tool basically works now<commit_after>package main

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"text/template"
	"time"
)

const (
	httpListenPort                   = 8081
	refreshRate                      = 60 //in seconds
	wikiURI                          = "https://wiki.tox.chat/users/nodes?do=export_raw"
	maxUDPPacketSize                 = 2048
	getNodesPacketID                 = 2
	sendNodesIpv6PacketID            = 4
	bootstrapInfoPacketID            = 240
	bootstrapInfoPacketLength        = 78
	tcpHandshakePacketLength         = 128
	tcpHandshakeResponsePacketLength = 96
	maxMOTDLength                    = 256
	queryTimeout                     = 4 //in seconds
	dialerTimeout                    = 4 //in seconds
)

var (
	lastScan     int64
	nodesList    = list.New()
	crypto, _    = NewCrypto()
	tcpPorts     = []int{443, 3389, 33445}
	lowerFuncMap = template.FuncMap{"lower": strings.ToLower}
)

//flags
var (
	networkFlag = flag.String("net", "udp", "network type, either 'udp' or 'tcp'")
	ipFlag      = flag.String("ip", "127.0.0.1", "ip address to probe, ipv4 and ipv6 are both supported")
	portFlag    = flag.Int("port", 33445, "port to probe")
	keyFlag     = flag.String("key", "", "public key of the node")
)

type tcpHandshakeResult struct {
	Port  int
	Error error
}

type toxStatus struct {
	LastScan       int64     `json:"last_scan"`
	LastScanString string    `json:"last_scan_string"`
	Nodes          []toxNode `json:"nodes"`
}

type toxNode struct {
	Ipv4Address    string `json:"ipv4"`
	Ipv6Address    string `json:"ipv6"`
	Port           int    `json:"port"`
	TCPPorts       []int  `json:"tcp_ports"`
	PublicKey      string `json:"public_key"`
	Maintainer     string `json:"maintainer"`
	Location       string `json:"location"`
	Status         bool   `json:"status"`
	Version        string `json:"version"`
	MOTD           string `json:"motd"`
	LastPing       int64  `json:"last_ping"`
	LastPingString string `json:"last_ping_string"`
}

func main() {
	if crypto == nil {
		log.Fatalf("Could not generate keypair")
	}

	if handleFlags() {
		return
	}

	go probeLoop()

	http.HandleFunc("/", handleHTTPRequest)
	http.HandleFunc("/json", handleJSONRequest)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", httpListenPort), nil))
}

func handleFlags() bool {
	if len(os.Args) < 2 {
		return false
	}

	flag.Parse()

	if len(*keyFlag) != 64 {
		log.Fatalln("error: public key must have a lenght of 64 hex characters")
	}

	node := toxNode{}
	node.Ipv4Address = *ipFlag //HACK: ipv6 addresses will also end up here
	node.PublicKey = *keyFlag
	node.Port = *portFlag

	if *networkFlag == "udp" {
		err := probeNode(&node)
		if err == nil {
			log.Println("success: this node appears to be online!")
		} else {
			log.Printf("error: %s", err.Error())
			log.Println("fail: this node appears to be offline!")
		}
	} else if *networkFlag == "tcp" {
		err := probeNodeTCP(&node)
		if err == nil {
			log.Println("success: this relay appears to be online!")
		} else {
			log.Printf("error: %s", err.Error())
			log.Println("fail: this relay appears to be offline!")
		}
	} else {
		log.Fatalf("error: unsupported network specified: %s", *networkFlag)
	}

	return true
}

func handleHTTPRequest(w http.ResponseWriter, r *http.Request) {
	urlPath := r.URL.Path[1:]
	if r.URL.Path == "/" {
		renderMainPage(w, "index.html")
		return
	}

	//TODO: make this more efficient
	data, err := ioutil.ReadFile(path.Join("./assets/", string(urlPath)))
	if err != nil {
		http.Error(w, http.StatusText(404), 404)
	} else {
		w.Write(data)
	}
}

func renderMainPage(w http.ResponseWriter, urlPath string) {
	tmpl, err := template.New("index.html").
		Funcs(lowerFuncMap).
		ParseFiles(path.Join("./assets/", string(urlPath)))

	if err != nil {
		http.Error(w, http.StatusText(500), 500)
		log.Printf("Internal server error while trying to serve index: %s", err.Error())
	} else {
		nodes := nodesListToSlice(nodesList)
		response := toxStatus{lastScan, time.Unix(lastScan, 0).String(), nodes}
		tmpl.Execute(w, response)
	}
}

func handleJSONRequest(w http.ResponseWriter, r *http.Request) {
	nodes := nodesListToSlice(nodesList)
	response := toxStatus{lastScan, time.Unix(lastScan, 0).String(), nodes}

	bytes, err := json.Marshal(response)
	if err != nil {
		http.Error(w, http.StatusText(500), 500)
		return
	}

	w.Write(bytes)
}

func probeLoop() {
	for {
		nodes, err := parseNodes()
		if err != nil {
			log.Printf("Error while trying to parse nodes: %s", err.Error())
		} else {
			c := make(chan error)
			for e := nodes.Front(); e != nil; e = e.Next() {
				node, _ := e.Value.(*toxNode)
				go func() {
					err := probeNode(node)
					if err == nil {
						ports := tcpPorts
						if !contains(tcpPorts, node.Port) {
							ports = append(ports, node.Port)
						}

						probeNodeTCPPorts(node, ports)
					}
					c <- err
				}()
			}

			for i := 0; i < nodes.Len(); i++ {
				err = <-c
				if err != nil {
					log.Printf("error: %s", err.Error())
				}
			}

			nodesList = nodes
			lastScan = time.Now().Unix()
		}

		time.Sleep(refreshRate * time.Second)
	}
}

func probeNodeTCPPorts(node *toxNode, ports []int) {
	c := make(chan tcpHandshakeResult)
	for _, port := range ports {
		go func(p int) {
			conn, err := newNodeConn(node, p, "tcp")
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				c <- tcpHandshakeResult{p, err}
			} else {
				c <- tryTCPHandshake(node, conn, p)
			}
		}(port)
	}

	for i := 0; i < len(ports); i++ {
		result := <-c
		if result.Error != nil {
			fmt.Printf("%s\n", result.Error.Error())
		} else {
			node.TCPPorts = append(node.TCPPorts, result.Port)
		}
	}
}

func probeNodeTCP(node *toxNode) error {
	conn, err := newNodeConn(node, node.Port, "tcp")
	if err != nil {
		return err
	}

	return tryTCPHandshake(node, conn, node.Port).Error
}

func probeNode(node *toxNode) error {
	conn, err := newNodeConn(node, node.Port, "udp")
	if err != nil {
		return err
	}

	err = getBootstrapInfo(node, conn)
	if err != nil {
		return err
	}

	conn.Close()
	conn, err = newNodeConn(node, node.Port, "udp")
	if err != nil {
		return err
	}

	err = getNodes(node, conn)
	if err != nil {
		return err
	}
	conn.Close()

	node.LastPing = time.Now().Unix()
	node.Status = true
	return nil
}

func getNodes(node *toxNode, conn net.Conn) error {
	nodePublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return err
	}

	plain := make([]byte, len(crypto.PublicKey)+8)
	copy(plain, crypto.PublicKey)
	copy(plain[len(crypto.PublicKey):], nextBytes(8)) //ping id

	nonce := nextNonce()
	sharedKey := crypto.CreateSharedKey(nodePublicKey)
	encrypted := encryptData(plain, sharedKey, nonce)[16:]

	payload := make([]byte, 1+len(crypto.PublicKey)+len(nonce)+len(encrypted))
	payload[0] = getNodesPacketID
	copy(payload[1:], crypto.PublicKey)
	copy(payload[1+len(crypto.PublicKey):], nonce)
	copy(payload[1+len(crypto.PublicKey)+len(nonce):], encrypted)
	conn.Write(payload)

	buffer := make([]byte, maxUDPPacketSize)
	_, err = conn.Read(buffer)

	if err != nil {
		return err
	} /*else if payload[0] != sendNodesIpv6PacketID {
		return fmt.Errorf("packet id: %d is not a sendnodesipv6 packet", payload[0])
	}

	right now we're happy if a node responds to our 'getnodes' request, without even validating the response
	this needs some more work

	on a side note: it looks like nodes are sending a 'getnodes' packet before 'sendnodesipv6',
	*/

	return nil
}

func getBootstrapInfo(node *toxNode, conn net.Conn) error {
	payload := make([]byte, bootstrapInfoPacketLength)
	payload[0] = bootstrapInfoPacketID
	conn.Write(payload)

	buffer := make([]byte, 1+4+maxMOTDLength)
	read, err := conn.Read(buffer)

	if err != nil {
		return err
	} else if buffer[0] != bootstrapInfoPacketID {
		return fmt.Errorf("packet id: %d is not a bootstrap info packet", buffer[0])
	}

	buffer = buffer[:read]
	if len(buffer) < 1+4 {
		return errors.New("bootstrap info packet too small")
	}

	node.Version = fmt.Sprintf("%d", binary.BigEndian.Uint32(buffer[1:1+4]))
	node.MOTD = string(bytes.Trim(buffer[1+4:], "\x00"))
	return nil
}

func tryTCPHandshake(node *toxNode, conn net.Conn, port int) tcpHandshakeResult {
	/* NOTE: conn is closed at the end of this function */
	nodePublicKey, err := hex.DecodeString(node.PublicKey)
	if err != nil {
		return tcpHandshakeResult{port, err}
	}

	nonce := nextNonce()
	baseNonce := nextNonce()
	plain := make([]byte, len(crypto.PublicKey)+len(baseNonce))
	tempCrypto, _ := NewCrypto()

	copy(plain, tempCrypto.PublicKey)
	copy(plain[len(tempCrypto.PublicKey):], baseNonce)
	sharedKey := crypto.CreateSharedKey(nodePublicKey)
	encrypted := encryptData(plain, sharedKey, nonce)[16:]

	payload := make([]byte, tcpHandshakePacketLength)
	copy(payload, crypto.PublicKey)
	copy(payload[len(crypto.PublicKey):], nonce)
	copy(payload[len(crypto.PublicKey)+len(nonce):], encrypted)
	conn.Write(payload)

	buffer := make([]byte, tcpHandshakeResponsePacketLength)
	read, err := conn.Read(buffer)

	var result tcpHandshakeResult

	if err != nil {
		result = tcpHandshakeResult{port, err}
	} else if read != tcpHandshakeResponsePacketLength {
		result = tcpHandshakeResult{
			port,
			errors.New("tcp handshake response has an incorrect length"),
		}
	} else {
		result = tcpHandshakeResult{port, nil}
	}

	conn.Close()
	return result
}

func newNodeConn(node *toxNode, port int, network string) (net.Conn, error) {
	dialer := net.Dialer{}
	dialer.Deadline = time.Now().Add(dialerTimeout * time.Second)

	conn, err := dialer.Dial(network, fmt.Sprintf("%s:%d", node.Ipv4Address, port))
	if err != nil {
		return nil, err
	}

	conn.SetReadDeadline(time.Now().Add(queryTimeout * time.Second))
	return conn, nil
}

func parseNode(nodeString string) *toxNode {
	nodeString = stripSpaces(nodeString)
	if !strings.HasPrefix(nodeString, "|") {
		return nil
	}

	lineParts := strings.Split(nodeString, "|")
	if port, err := strconv.Atoi(strings.TrimSpace(lineParts[3])); err == nil && len(lineParts) == 8 {
		node := toxNode{
			strings.TrimSpace(lineParts[1]),
			strings.TrimSpace(lineParts[2]),
			port,
			[]int{},
			strings.TrimSpace(lineParts[4]),
			strings.TrimSpace(lineParts[5]),
			strings.TrimSpace(lineParts[6]),
			false,
			"",
			"",
			0,
			"Never",
		}

		if node.Ipv6Address == "NONE" {
			node.Ipv6Address = "-"
		}

		return &node
	}

	return nil
}

func parseNodes() (*list.List, error) {
	res, err := http.Get(wikiURI)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	nodes := list.New()
	content, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	lines := strings.Split(string(content), "\n")
	for _, line := range lines {
		node := parseNode(line)
		if node == nil {
			continue
		}

		oldNode := getOldNode(node.PublicKey)
		if oldNode != nil { //transfer last ping info
			node.LastPing = oldNode.LastPing
			node.LastPingString = oldNode.LastPingString
		}

		nodes.PushBack(node)
	}
	return nodes, nil
}

func getOldNode(publicKey string) *toxNode {
	for e := nodesList.Front(); e != nil; e = e.Next() {
		node, _ := e.Value.(*toxNode)
		if node.PublicKey == publicKey {
			return node
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

// vouch
// github.com/vouch/vouch-proxy

import (
	"net/http"
	"os"
	"strconv"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/gorilla/mux"

	"github.com/vouch/vouch-proxy/handlers"
	"github.com/vouch/vouch-proxy/pkg/cfg"
	"github.com/vouch/vouch-proxy/pkg/timelog"
	tran "github.com/vouch/vouch-proxy/pkg/transciever"
)

// version and semver get overwritten by build with
// go build -i -v -ldflags="-X main.version=$(git describe --always --long) -X main.semver=v$(git semver get)"

var (
	version   = "undefined"
	builddt   = "undefined"
	host      = "undefined"
	semver    = "undefined"
	branch    = "undefined"
	staticDir = "/static/"
)

func init() {
	// var listen = cfg.Cfg.Listen + ":" + strconv.Itoa(cfg.Cfg.Port)
}

func main() {
	var listen = cfg.Cfg.Listen + ":" + strconv.Itoa(cfg.Cfg.Port)
	log.WithFields(log.Fields{
		// "semver":    semver,
		"version":   version,
		"buildtime": builddt,
		"buildhost": host,
		"branch":    branch,
		"semver":    semver,
		"listen":    listen}).Info("starting " + cfg.Branding.CcName)

	mux := mux.NewRouter()

	authH := http.HandlerFunc(handlers.ValidateRequestHandler)
	mux.HandleFunc("/validate", timelog.TimeLog(authH))
	mux.HandleFunc("/_external-auth-{id}", timelog.TimeLog(authH))

	loginH := http.HandlerFunc(handlers.LoginHandler)
	mux.HandleFunc("/login", timelog.TimeLog(loginH))

	logoutH := http.HandlerFunc(handlers.LogoutHandler)
	mux.HandleFunc("/logout", timelog.TimeLog(logoutH))

	callH := http.HandlerFunc(handlers.CallbackHandler)
	mux.HandleFunc("/auth", timelog.TimeLog(callH))

	healthH := http.HandlerFunc(handlers.HealthcheckHandler)
	mux.HandleFunc("/healthcheck", timelog.TimeLog(healthH))

	// serve static files from /static if present (probably in a docker container), otherwise from ./static
	_, err := os.Stat(staticDir)
	staticFiles := staticDir
	if err != nil {
		staticFiles = "." + staticDir
	}
	log.Debugf("serving static files from %s", staticFiles)
	// https://golangcode.com/serve-static-assets-using-the-mux-router/
	mux.Handle(staticDir, http.StripPrefix(staticDir, (http.FileServer(http.Dir(staticFiles)))))

	if cfg.Cfg.WebApp {
		log.Info("enabling websocket")
		tran.ExplicitInit()
		mux.Handle("/ws", tran.WS)
	}

	// socketio := tran.NewServer()
	// mux.Handle("/socket.io/", cors.AllowAll(socketio))
	// http.Handle("/socket.io/", tran.Server)

	srv := &http.Server{
		Handler: mux,
		Addr:    listen,
		// Good practice: enforce timeouts for servers you create!
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
		/// logrus has an example of using ErrorLog but it doesn't apply to this MUX implimentation
		// https://github.com/sirupsen/logrus#logger-as-an-iowriter
		// ErrorLog:     log.New(w, "", 0),
	}

	log.Fatal(srv.ListenAndServe())

}
<commit_msg>Serve everyting from /static to the file handler, not just /static exactly<commit_after>package main

// vouch
// github.com/vouch/vouch-proxy

import (
	"net/http"
	"os"
	"strconv"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/gorilla/mux"

	"github.com/vouch/vouch-proxy/handlers"
	"github.com/vouch/vouch-proxy/pkg/cfg"
	"github.com/vouch/vouch-proxy/pkg/timelog"
	tran "github.com/vouch/vouch-proxy/pkg/transciever"
)

// version and semver get overwritten by build with
// go build -i -v -ldflags="-X main.version=$(git describe --always --long) -X main.semver=v$(git semver get)"

var (
	version   = "undefined"
	builddt   = "undefined"
	host      = "undefined"
	semver    = "undefined"
	branch    = "undefined"
	staticDir = "/static/"
)

func init() {
	// var listen = cfg.Cfg.Listen + ":" + strconv.Itoa(cfg.Cfg.Port)
}

func main() {
	var listen = cfg.Cfg.Listen + ":" + strconv.Itoa(cfg.Cfg.Port)
	log.WithFields(log.Fields{
		// "semver":    semver,
		"version":   version,
		"buildtime": builddt,
		"buildhost": host,
		"branch":    branch,
		"semver":    semver,
		"listen":    listen}).Info("starting " + cfg.Branding.CcName)

	mux := mux.NewRouter()

	authH := http.HandlerFunc(handlers.ValidateRequestHandler)
	mux.HandleFunc("/validate", timelog.TimeLog(authH))
	mux.HandleFunc("/_external-auth-{id}", timelog.TimeLog(authH))

	loginH := http.HandlerFunc(handlers.LoginHandler)
	mux.HandleFunc("/login", timelog.TimeLog(loginH))

	logoutH := http.HandlerFunc(handlers.LogoutHandler)
	mux.HandleFunc("/logout", timelog.TimeLog(logoutH))

	callH := http.HandlerFunc(handlers.CallbackHandler)
	mux.HandleFunc("/auth", timelog.TimeLog(callH))

	healthH := http.HandlerFunc(handlers.HealthcheckHandler)
	mux.HandleFunc("/healthcheck", timelog.TimeLog(healthH))

	// serve static files from /static if present (probably in a docker container), otherwise from ./static
	_, err := os.Stat(staticDir)
	staticFiles := staticDir
	if err != nil {
		staticFiles = "." + staticDir
	}
	log.Debugf("serving static files from %s", staticFiles)
	// https://golangcode.com/serve-static-assets-using-the-mux-router/
	mux.PathPrefix(staticDir).Handler(http.StripPrefix(staticDir, (http.FileServer(http.Dir(staticFiles)))))

	if cfg.Cfg.WebApp {
		log.Info("enabling websocket")
		tran.ExplicitInit()
		mux.Handle("/ws", tran.WS)
	}

	// socketio := tran.NewServer()
	// mux.Handle("/socket.io/", cors.AllowAll(socketio))
	// http.Handle("/socket.io/", tran.Server)

	srv := &http.Server{
		Handler: mux,
		Addr:    listen,
		// Good practice: enforce timeouts for servers you create!
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
		/// logrus has an example of using ErrorLog but it doesn't apply to this MUX implimentation
		// https://github.com/sirupsen/logrus#logger-as-an-iowriter
		// ErrorLog:     log.New(w, "", 0),
	}

	log.Fatal(srv.ListenAndServe())

}
<|endoftext|>


<commit_before>// gcvis is a tool to assist you visualising the operation of
// the go runtime garbage collector.
//
// usage:
//
//     gcvis program [arguments]...
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/pkg/browser"

	"golang.org/x/crypto/ssh/terminal"
)

var iface = flag.String("i", "127.0.0.1", "specify interface to use. defaults to 127.0.0.1.")
var port = flag.String("p", "0", "specify port to use.")
var openBrowser = flag.Bool("o", true, "automatically open browser")

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s: command <args>...\n", os.Args[0])
		flag.PrintDefaults()
	}

	var pipeRead io.ReadCloser
	var subcommand *SubCommand

	flag.Parse()
	if len(flag.Args()) < 1 {
		if terminal.IsTerminal(int(os.Stdin.Fd())) {
			flag.Usage()
			return
		} else {
			pipeRead = os.Stdin
		}
	} else {
		subcommand = NewSubCommand(flag.Args())
		pipeRead = subcommand.PipeRead
		go subcommand.Run()
	}

	parser := NewParser(pipeRead)
	gcvisGraph := NewGraph(strings.Join(flag.Args(), " "), GCVIS_TMPL)
	server := NewHttpServer(*iface, *port, &gcvisGraph)

	go parser.Run()
	go server.Start()

	url := server.Url()

	if *openBrowser {
		log.Printf("opening browser window, if this fails, navigate to %s", url)
		browser.OpenURL(url)
	} else {
		log.Printf("server started on %s", url)
	}

	for {
		select {
		case gcTrace := <-parser.GcChan:
			gcvisGraph.AddGCTraceGraphPoint(gcTrace)
		case scvgTrace := <-parser.ScvgChan:
			gcvisGraph.AddScavengerGraphPoint(scvgTrace)
		case output := <-parser.NoMatchChan:
			fmt.Fprintln(os.Stderr, output)
		case <-parser.done:
			if parser.Err != nil {
				fmt.Fprintf(os.Stderr, parser.Err.Error())
				os.Exit(1)
			}

			break
		}
	}

	if subcommand != nil && subcommand.Err() != nil {
		fmt.Fprintf(os.Stderr, subcommand.Err().Error())
		os.Exit(1)
	}

	os.Exit(0)
}
<commit_msg>Replace break with os.Exit(0) in main loop<commit_after>// gcvis is a tool to assist you visualising the operation of
// the go runtime garbage collector.
//
// usage:
//
//     gcvis program [arguments]...
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/pkg/browser"

	"golang.org/x/crypto/ssh/terminal"
)

var iface = flag.String("i", "127.0.0.1", "specify interface to use. defaults to 127.0.0.1.")
var port = flag.String("p", "0", "specify port to use.")
var openBrowser = flag.Bool("o", true, "automatically open browser")

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s: command <args>...\n", os.Args[0])
		flag.PrintDefaults()
	}

	var pipeRead io.ReadCloser
	var subcommand *SubCommand

	flag.Parse()
	if len(flag.Args()) < 1 {
		if terminal.IsTerminal(int(os.Stdin.Fd())) {
			flag.Usage()
			return
		} else {
			pipeRead = os.Stdin
		}
	} else {
		subcommand = NewSubCommand(flag.Args())
		pipeRead = subcommand.PipeRead
		go subcommand.Run()
	}

	parser := NewParser(pipeRead)
	gcvisGraph := NewGraph(strings.Join(flag.Args(), " "), GCVIS_TMPL)
	server := NewHttpServer(*iface, *port, &gcvisGraph)

	go parser.Run()
	go server.Start()

	url := server.Url()

	if *openBrowser {
		log.Printf("opening browser window, if this fails, navigate to %s", url)
		browser.OpenURL(url)
	} else {
		log.Printf("server started on %s", url)
	}

	for {
		select {
		case gcTrace := <-parser.GcChan:
			gcvisGraph.AddGCTraceGraphPoint(gcTrace)
		case scvgTrace := <-parser.ScvgChan:
			gcvisGraph.AddScavengerGraphPoint(scvgTrace)
		case output := <-parser.NoMatchChan:
			fmt.Fprintln(os.Stderr, output)
		case <-parser.done:
			if parser.Err != nil {
				fmt.Fprintf(os.Stderr, parser.Err.Error())
				os.Exit(1)
			}

			os.Exit(0)
		}
	}

	if subcommand != nil && subcommand.Err() != nil {
		fmt.Fprintf(os.Stderr, subcommand.Err().Error())
		os.Exit(1)
	}

	os.Exit(0)
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
	"regexp"
)

var (
	verbose = flag.Bool("v", false, "print the names of packages as they are checked")
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

func typesMatch(wanted, got []types.Type) bool {
	if len(wanted) != len(got) {
		return false
	}
	for i, w := range wanted {
		g := got[i]
		if !types.ConvertibleTo(g, w) {
			return false
		}
	}
	return true
}

func resultsMatch(wanted, got []types.Type) bool {
	if len(got) == 0 {
		return true
	}
	return typesMatch(wanted, got)
}

func usedMatch(t types.Type, usedAs []types.Type) bool {
	if len(usedAs) < 1 {
		return true
	}
	for _, u := range usedAs {
		if !types.ConvertibleTo(t, u) {
			return false
		}
	}
	return true
}

func matchesIface(p *param, iface ifaceSign) bool {
	if len(p.calls) > len(iface.funcs) {
		return false
	}
	if !usedMatch(iface.t, p.usedAs) {
		return false
	}
	for name, f := range iface.funcs {
		c, e := p.calls[name]
		if !e {
			return false
		}
		if !typesMatch(f.params, c.params) {
			return false
		}
		if !resultsMatch(f.results, c.results) {
			return false
		}
	}
	for _, to := range p.assigned {
		if !matchesIface(to, iface) {
			return false
		}
	}
	return true
}

func interfaceMatching(p *param) string {
	for name, iface := range parsed {
		if matchesIface(p, iface) {
			return name
		}
	}
	return ""
}

var skipDir = regexp.MustCompile(`^(testdata|vendor|_.*)$`)

func getDirs(d string, recursive bool) ([]string, error) {
	var dirs []string
	walkFn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if skipDir.MatchString(info.Name()) {
			return filepath.SkipDir
		}
		if info.IsDir() {
			dirs = append(dirs, path)
			if !recursive {
				return filepath.SkipDir
			}
		}
		return nil
	}
	if err := filepath.Walk(d, walkFn); err != nil {
		return nil, err
	}
	return dirs, nil
}

func getPkgs(p string) ([]*build.Package, []string, error) {
	recursive := filepath.Base(p) == "..."
	if !recursive {
		info, err := os.Stat(p)
		if err != nil {
			return nil, nil, err
		}
		if !info.IsDir() {
			pkg := &build.Package{
				Name:    "stdin",
				GoFiles: []string{p},
			}
			return []*build.Package{pkg}, []string{"."}, nil
		}
	}
	d := p
	if recursive {
		d = p[:len(p)-4]
	}
	dirs, err := getDirs(d, recursive)
	if err != nil {
		return nil, nil, err
	}
	wd, err := os.Getwd()
	if err != nil {
		return nil, nil, err
	}
	var pkgs []*build.Package
	var basedirs []string
	for _, d := range dirs {
		pkg, err := build.Import("./"+d, wd, 0)
		if _, ok := err.(*build.NoGoError); ok {
			continue
		}
		if err != nil {
			return nil, nil, err
		}
		pkgs = append(pkgs, pkg)
		basedirs = append(basedirs, d)
	}
	return pkgs, basedirs, nil
}

func checkPaths(paths []string, w io.Writer) error {
	conf := &types.Config{Importer: importer.Default()}
	for _, p := range paths {
		pkgs, basedirs, err := getPkgs(p)
		if err != nil {
			return err
		}
		for i, pkg := range pkgs {
			basedir := basedirs[i]
			if err := checkPkg(conf, pkg, basedir, w); err != nil {
				return err
			}
		}
	}
	return nil
}

func checkPkg(conf *types.Config, pkg *build.Package, basedir string, w io.Writer) error {
	if *verbose {
		importPath := pkg.ImportPath
		if importPath == "" {
			importPath = "command-line-arguments"
		}
		fmt.Fprintln(w, importPath)
	}
	gp := &goPkg{
		Package: pkg,
		fset:    token.NewFileSet(),
	}
	for _, p := range pkg.GoFiles {
		fp := filepath.Join(basedir, p)
		if err := gp.parsePath(fp); err != nil {
			return err
		}
	}
	if err := gp.check(conf, w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	*build.Package

	fset  *token.FileSet
	files []*ast.File
}

func (gp *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := gp.parseReader(fp, f); err != nil {
		return err
	}
	return nil
}

func (gp *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(gp.fset, name, r, 0)
	if err != nil {
		return err
	}
	gp.files = append(gp.files, f)
	return nil
}

func (gp *goPkg) check(conf *types.Config, w io.Writer) error {
	info := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}
	_, err := conf.Check(gp.Name, gp.fset, gp.files, info)
	if err != nil {
		return err
	}
	v := &Visitor{
		Info: info,
		w:    w,
		fset: gp.fset,
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type param struct {
	t types.Type

	calls   map[string]funcSign
	usedAs  []types.Type
	discard bool

	assigned []*param
}

type Visitor struct {
	*types.Info

	w     io.Writer
	fset  *token.FileSet
	nodes []ast.Node

	params  map[string]*param
	extras  map[string]*param
	inBlock bool
}

func paramsMap(t *types.Tuple) map[string]*param {
	m := make(map[string]*param, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = &param{
			t:     p.Type(),
			calls: make(map[string]funcSign),
		}
	}
	return m
}

func paramType(sign *types.Signature, i int) types.Type {
	params := sign.Params()
	extra := sign.Variadic() && i >= params.Len()-1
	if !extra {
		if i >= params.Len() {
			// builtins with multiple signatures
			return nil
		}
		return params.At(i).Type()
	}
	stype := params.At(params.Len() - 1).Type().(*types.Slice)
	return stype.Elem()
}

func (v *Visitor) param(name string) *param {
	if p, e := v.params[name]; e {
		return p
	}
	if p, e := v.extras[name]; e {
		return p
	}
	p := &param{
		calls: make(map[string]funcSign),
	}
	v.extras[name] = p
	return p
}

func (v *Visitor) addUsed(name string, as types.Type) {
	if as == nil {
		return
	}
	p := v.param(name)
	p.usedAs = append(p.usedAs, as)
}

func (v *Visitor) addAssign(to, from string) {
	pto := v.param(to)
	pfrom := v.param(from)
	pfrom.assigned = append(pfrom.assigned, pto)
}

func (v *Visitor) discard(name string) {
	p := v.param(name)
	p.discard = true
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	var top ast.Node
	if len(v.nodes) > 0 {
		top = v.nodes[len(v.nodes)-1]
	}
	switch x := node.(type) {
	case *ast.FuncDecl:
		sign := v.Defs[x.Name].Type().(*types.Signature)
		v.params = paramsMap(sign.Params())
		v.extras = make(map[string]*param)
	case *ast.BlockStmt:
		v.inBlock = true
	case *ast.SelectorExpr:
		if !v.inBlock{
			return nil
		}
		if _, ok := top.(*ast.CallExpr); ok {
			break
		}
		id, ok := x.X.(*ast.Ident)
		if !ok {
			break
		}
		v.discard(id.Name)
	case *ast.AssignStmt:
		if !v.inBlock {
			return nil
		}
		for i, e := range x.Rhs {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}
			left := x.Lhs[i]
			v.addUsed(id.Name, v.Types[left].Type)
			if lid, ok := left.(*ast.Ident); ok {
				v.addAssign(lid.Name, id.Name)
			}
		}
	case *ast.CallExpr:
		if !v.inBlock {
			return nil
		}
		sign := funcSignature(v.Types[x.Fun].Type)
		if sign == nil {
			break
		}
		for i, e := range x.Args {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}
			v.addUsed(id.Name, paramType(sign, i))
		}
		v.onCall(x)
	case nil:
		v.nodes = v.nodes[:len(v.nodes)-1]
		if _, ok := top.(*ast.FuncDecl); ok {
			v.funcEnded(top.Pos())
			v.params = nil
			v.extras = nil
			v.inBlock = false
		}
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func funcSignature(t types.Type) *types.Signature {
	switch x := t.(type) {
	case *types.Signature:
		return x
	case *types.Named:
		return funcSignature(x.Underlying())
	default:
		return nil
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) {
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	p := v.param(left.Name)
	sign := funcSignature(v.Types[ce.Fun].Type)
	if sign == nil {
		return
	}
	c := funcSign{}
	results := sign.Results()
	for i := 0; i < results.Len(); i++ {
		c.results = append(c.results, results.At(i).Type())
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.Types[a].Type)
	}
	p.calls[sel.Sel.Name] = c
	return
}

func (v *Visitor) funcEnded(pos token.Pos) {
	for name, p := range v.params {
		if p.discard {
			continue
		}
		iface := interfaceMatching(p)
		if iface == "" {
			continue
		}
		if iface == p.t.String() {
			continue
		}
		pos := v.fset.Position(pos)
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<commit_msg>Fix param recursion issues<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
	"regexp"
)

var (
	verbose = flag.Bool("v", false, "print the names of packages as they are checked")
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

func typesMatch(wanted, got []types.Type) bool {
	if len(wanted) != len(got) {
		return false
	}
	for i, w := range wanted {
		g := got[i]
		if !types.ConvertibleTo(g, w) {
			return false
		}
	}
	return true
}

func resultsMatch(wanted, got []types.Type) bool {
	if len(got) == 0 {
		return true
	}
	return typesMatch(wanted, got)
}

func usedMatch(t types.Type, usedAs []types.Type) bool {
	if len(usedAs) < 1 {
		return true
	}
	for _, u := range usedAs {
		if !types.ConvertibleTo(t, u) {
			return false
		}
	}
	return true
}

func matchesIface(p *param, iface ifaceSign, canEmpty bool) bool {
	if len(p.calls) > len(iface.funcs) {
		return false
	}
	if !usedMatch(iface.t, p.usedAs) {
		return false
	}
	for name, f := range iface.funcs {
		c, e := p.calls[name]
		if !e {
			return canEmpty
		}
		if !typesMatch(f.params, c.params) {
			return false
		}
		if !resultsMatch(f.results, c.results) {
			return false
		}
	}
	for _, to := range p.assigned {
		if to.discard {
			return false
		}
		if !matchesIface(to, iface, true) {
			return false
		}
	}
	return true
}

func interfaceMatching(p *param) string {
	for name, iface := range parsed {
		if matchesIface(p, iface, false) {
			return name
		}
	}
	return ""
}

var skipDir = regexp.MustCompile(`^(testdata|vendor|_.*)$`)

func getDirs(d string, recursive bool) ([]string, error) {
	var dirs []string
	walkFn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if skipDir.MatchString(info.Name()) {
			return filepath.SkipDir
		}
		if info.IsDir() {
			dirs = append(dirs, path)
			if !recursive {
				return filepath.SkipDir
			}
		}
		return nil
	}
	if err := filepath.Walk(d, walkFn); err != nil {
		return nil, err
	}
	return dirs, nil
}

func getPkgs(p string) ([]*build.Package, []string, error) {
	recursive := filepath.Base(p) == "..."
	if !recursive {
		info, err := os.Stat(p)
		if err != nil {
			return nil, nil, err
		}
		if !info.IsDir() {
			pkg := &build.Package{
				Name:    "stdin",
				GoFiles: []string{p},
			}
			return []*build.Package{pkg}, []string{"."}, nil
		}
	}
	d := p
	if recursive {
		d = p[:len(p)-4]
	}
	dirs, err := getDirs(d, recursive)
	if err != nil {
		return nil, nil, err
	}
	wd, err := os.Getwd()
	if err != nil {
		return nil, nil, err
	}
	var pkgs []*build.Package
	var basedirs []string
	for _, d := range dirs {
		pkg, err := build.Import("./"+d, wd, 0)
		if _, ok := err.(*build.NoGoError); ok {
			continue
		}
		if err != nil {
			return nil, nil, err
		}
		pkgs = append(pkgs, pkg)
		basedirs = append(basedirs, d)
	}
	return pkgs, basedirs, nil
}

func checkPaths(paths []string, w io.Writer) error {
	conf := &types.Config{Importer: importer.Default()}
	for _, p := range paths {
		pkgs, basedirs, err := getPkgs(p)
		if err != nil {
			return err
		}
		for i, pkg := range pkgs {
			basedir := basedirs[i]
			if err := checkPkg(conf, pkg, basedir, w); err != nil {
				return err
			}
		}
	}
	return nil
}

func checkPkg(conf *types.Config, pkg *build.Package, basedir string, w io.Writer) error {
	if *verbose {
		importPath := pkg.ImportPath
		if importPath == "" {
			importPath = "command-line-arguments"
		}
		fmt.Fprintln(w, importPath)
	}
	gp := &goPkg{
		Package: pkg,
		fset:    token.NewFileSet(),
	}
	for _, p := range pkg.GoFiles {
		fp := filepath.Join(basedir, p)
		if err := gp.parsePath(fp); err != nil {
			return err
		}
	}
	if err := gp.check(conf, w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	*build.Package

	fset  *token.FileSet
	files []*ast.File
}

func (gp *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := gp.parseReader(fp, f); err != nil {
		return err
	}
	return nil
}

func (gp *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(gp.fset, name, r, 0)
	if err != nil {
		return err
	}
	gp.files = append(gp.files, f)
	return nil
}

func (gp *goPkg) check(conf *types.Config, w io.Writer) error {
	info := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}
	_, err := conf.Check(gp.Name, gp.fset, gp.files, info)
	if err != nil {
		return err
	}
	v := &Visitor{
		Info: info,
		w:    w,
		fset: gp.fset,
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type param struct {
	t types.Type

	calls   map[string]funcSign
	usedAs  []types.Type
	discard bool

	assigned []*param
}

type Visitor struct {
	*types.Info

	w     io.Writer
	fset  *token.FileSet
	nodes []ast.Node

	params  map[string]*param
	extras  map[string]*param
	inBlock bool
}

func paramsMap(t *types.Tuple) map[string]*param {
	m := make(map[string]*param, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = &param{
			t:     p.Type(),
			calls: make(map[string]funcSign),
		}
	}
	return m
}

func paramType(sign *types.Signature, i int) types.Type {
	params := sign.Params()
	extra := sign.Variadic() && i >= params.Len()-1
	if !extra {
		if i >= params.Len() {
			// builtins with multiple signatures
			return nil
		}
		return params.At(i).Type()
	}
	stype := params.At(params.Len() - 1).Type().(*types.Slice)
	return stype.Elem()
}

func (v *Visitor) param(name string) *param {
	if p, e := v.params[name]; e {
		return p
	}
	if p, e := v.extras[name]; e {
		return p
	}
	p := &param{
		calls: make(map[string]funcSign),
	}
	v.extras[name] = p
	return p
}

func (v *Visitor) addUsed(name string, as types.Type) {
	if as == nil {
		return
	}
	p := v.param(name)
	p.usedAs = append(p.usedAs, as)
}

func (v *Visitor) addAssign(to, from string) {
	pto := v.param(to)
	pfrom := v.param(from)
	pfrom.assigned = append(pfrom.assigned, pto)
}

func (v *Visitor) discard(name string) {
	p := v.param(name)
	p.discard = true
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	var top ast.Node
	if len(v.nodes) > 0 {
		top = v.nodes[len(v.nodes)-1]
	}
	switch x := node.(type) {
	case *ast.FuncDecl:
		sign := v.Defs[x.Name].Type().(*types.Signature)
		v.params = paramsMap(sign.Params())
		v.extras = make(map[string]*param)
	case *ast.BlockStmt:
		v.inBlock = true
	case *ast.SelectorExpr:
		if !v.inBlock {
			return nil
		}
		if _, ok := top.(*ast.CallExpr); ok {
			break
		}
		id, ok := x.X.(*ast.Ident)
		if !ok {
			break
		}
		v.discard(id.Name)
	case *ast.AssignStmt:
		if !v.inBlock {
			return nil
		}
		for i, e := range x.Rhs {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}
			left := x.Lhs[i]
			v.addUsed(id.Name, v.Types[left].Type)
			if lid, ok := left.(*ast.Ident); ok {
				v.addAssign(lid.Name, id.Name)
			}
		}
	case *ast.CallExpr:
		if !v.inBlock {
			return nil
		}
		sign := funcSignature(v.Types[x.Fun].Type)
		if sign == nil {
			break
		}
		for i, e := range x.Args {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}
			v.addUsed(id.Name, paramType(sign, i))
		}
		v.onCall(x)
	case nil:
		v.nodes = v.nodes[:len(v.nodes)-1]
		if _, ok := top.(*ast.FuncDecl); ok {
			v.funcEnded(top.Pos())
			v.params = nil
			v.extras = nil
			v.inBlock = false
		}
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func funcSignature(t types.Type) *types.Signature {
	switch x := t.(type) {
	case *types.Signature:
		return x
	case *types.Named:
		return funcSignature(x.Underlying())
	default:
		return nil
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) {
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	p := v.param(left.Name)
	sign := funcSignature(v.Types[ce.Fun].Type)
	if sign == nil {
		return
	}
	c := funcSign{}
	results := sign.Results()
	for i := 0; i < results.Len(); i++ {
		c.results = append(c.results, results.At(i).Type())
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.Types[a].Type)
	}
	p.calls[sel.Sel.Name] = c
	return
}

func (v *Visitor) funcEnded(pos token.Pos) {
	for name, p := range v.params {
		if p.discard {
			continue
		}
		iface := interfaceMatching(p)
		if iface == "" {
			continue
		}
		if iface == p.t.String() {
			continue
		}
		pos := v.fset.Position(pos)
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<|endoftext|>


<commit_before>package main

import (
	// Stdlib
	"net/http"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow-daemon/internal/handlers/github"
	"github.com/salsaflow/salsaflow-daemon/internal/handlers/jira"
	"github.com/salsaflow/salsaflow-daemon/internal/handlers/pivotaltracker"

	// Vendor
	"github.com/codegangsta/negroni"
)

func main() {
	// Create the top-level mux.
	mux := http.NewServeMux()

	// Register GitHub handlers.
	var githubOptions []github.OptionFunc
	if secret := os.Getenv("GITHUB_SECRET"); secret != "" {
		githubOptions = append(githubOptions, github.SetSecret(secret))
	}
	mux.Handle("/events/github", github.NewHandler(githubOptions...))

	// Register Pivotal Tracker handlers.
	var ptOptions []pivotaltracker.OptionFunc
	if secret := os.Getenv("PIVOTALTRACKER_SECRET"); secret != "" {
		ptOptions = append(ptOptions, pivotaltracker.SetSecret(secret))
	}
	mux.Handle("/events/pivotaltracker", pivotaltracker.NewHandler(ptOptions...))

	// Set up Negroni and start listening.
	n := negroni.Classic()
	n.UseHandler(mux)
	n.Run(":" + os.Getenv("PORT"))
}
<commit_msg>main: Refactor to use the new module interfaces<commit_after>package main

import (
	// Stdlib
	"log"
	"net/http"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow-daemon/internal/modules/endpoints"

	// Vendor
	"github.com/codegangsta/negroni"
)

func main() {
	// Register the module endpoints with the main mux.
	var nuked bool
	mux := http.NewServeMux()
	for _, endpoint := range endpoints.Endpoints() {
		handler, err := endpoint.NewHandler()
		if err != nil {
			log.Println(err)
			nuked = true
		}

		prefix := "/modules/" + endpoint.ModuleId()
		mux.Handle(prefix+"/", http.StripPrefix(prefix, handler))
	}
	if nuked {
		os.Exit(1)
	}

	// Set up Negroni and start listening.
	n := negroni.Classic()
	n.Use(newRewriteObsoletePathsMiddleware())
	n.UseHandler(mux)
	n.Run(":" + os.Getenv("PORT"))
}

func newRewriteObsoletePathsMiddleware() negroni.Handler {
	return negroni.HandlerFunc(
		func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
			// Handle obsolete paths by rewriting them internally to the new paths.
			switch r.URL.Path {
			case "/events/github":
				r.URL.Path = "/modules/salsaflow.modules.codereview.github/events"
			case "/events/pivotaltracker":
				r.URL.Path = "/modules/salsaflow.modules.issuetracking.pivotaltracker/events"
			}

			// Pass the request to the next handler.
			next(rw, r)
		})
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/cli"
	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"

	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	buildError error
)

func main() {
	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A live reload utility for Go web applications."
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "port for the proxy server",
		},
		cli.IntFlag{
			Name:  "appPort,a",
			Value: 3001,
			Usage: "port for the Go web server",
		},
		cli.StringFlag{
			Name:  "bin,b",
			Value: "gin-bin",
			Usage: "name of generated binary file",
		},
		cli.StringFlag{
			Name:  "path,t",
			Value: ".",
			Usage: "Path to build files from",
		},
		cli.StringFlag{
			Name:  "watch,w",
			Value: ".",
			Usage: "Path to watch files from",
		},
		cli.BoolFlag{
			Name:  "immediate,i",
			Usage: "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:  "godep,g",
			Usage: "use godep when building",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "Run the gin proxy in the current working directory",
			Action:    MainAction,
		},
		{
			Name:      "env",
			ShortName: "e",
			Usage:     "Display environment variables set by the .env file",
			Action:    EnvAction,
		},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {
	port := c.GlobalInt("port")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")

	// Bootstrap the environment
	envy.Bootstrap()

	// Set the PORT env
	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	builder := gin.NewBuilder(c.GlobalString("path"), c.GlobalString("bin"), c.GlobalBool("godep"))
	runner := gin.NewRunner(filepath.Join(wd, builder.Binary()), c.Args()...)
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Port:    port,
		ProxyTo: "http://localhost:" + appPort,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	logger.Printf("listening on port %d\n", port)

	shutdown(runner)

	// build right now
	build(builder, runner, logger)

	// scan for changes
	scanChanges(c.GlobalString("watch"), func(path string) {
		runner.Kill()
		build(builder, runner, logger)
	})
}

func EnvAction(c *cli.Context) {
	// Bootstrap the environment
	env, err := envy.Bootstrap()
	if err != nil {
		logger.Fatalln(err)
	}

	for k, v := range env {
		fmt.Printf("%s: %s\n", k, v)
	}

}

func build(builder gin.Builder, runner gin.Runner, logger *log.Logger) {
	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Println("ERROR! Build failed.")
		fmt.Println(builder.Errors())
	} else {
		// print success only if there were errors before
		if buildError != nil {
			logger.Println("Build Successful")
		}
		buildError = nil
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" {
				return filepath.SkipDir
			}

			// ignore hidden files
			if filepath.Base(path)[0] == '.' {
				return nil
			}

			if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}

func shutdown(runner gin.Runner) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		s := <-c
		log.Println("Got signal: ", s)
		err := runner.Kill()
		if err != nil {
			log.Print("Error killing: ", err)
		}
		os.Exit(1)
	}()
}
<commit_msg>Skip vendor directory<commit_after>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/cli"
	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"

	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	buildError error
)

func main() {
	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A live reload utility for Go web applications."
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "port for the proxy server",
		},
		cli.IntFlag{
			Name:  "appPort,a",
			Value: 3001,
			Usage: "port for the Go web server",
		},
		cli.StringFlag{
			Name:  "bin,b",
			Value: "gin-bin",
			Usage: "name of generated binary file",
		},
		cli.StringFlag{
			Name:  "path,t",
			Value: ".",
			Usage: "Path to build files from",
		},
		cli.StringFlag{
			Name:  "watch,w",
			Value: ".",
			Usage: "Path to watch files from",
		},
		cli.BoolFlag{
			Name:  "immediate,i",
			Usage: "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:  "godep,g",
			Usage: "use godep when building",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "Run the gin proxy in the current working directory",
			Action:    MainAction,
		},
		{
			Name:      "env",
			ShortName: "e",
			Usage:     "Display environment variables set by the .env file",
			Action:    EnvAction,
		},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {
	port := c.GlobalInt("port")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")

	// Bootstrap the environment
	envy.Bootstrap()

	// Set the PORT env
	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	builder := gin.NewBuilder(c.GlobalString("path"), c.GlobalString("bin"), c.GlobalBool("godep"))
	runner := gin.NewRunner(filepath.Join(wd, builder.Binary()), c.Args()...)
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Port:    port,
		ProxyTo: "http://localhost:" + appPort,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	logger.Printf("listening on port %d\n", port)

	shutdown(runner)

	// build right now
	build(builder, runner, logger)

	// scan for changes
	scanChanges(c.GlobalString("watch"), func(path string) {
		runner.Kill()
		build(builder, runner, logger)
	})
}

func EnvAction(c *cli.Context) {
	// Bootstrap the environment
	env, err := envy.Bootstrap()
	if err != nil {
		logger.Fatalln(err)
	}

	for k, v := range env {
		fmt.Printf("%s: %s\n", k, v)
	}

}

func build(builder gin.Builder, runner gin.Runner, logger *log.Logger) {
	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Println("ERROR! Build failed.")
		fmt.Println(builder.Errors())
	} else {
		// print success only if there were errors before
		if buildError != nil {
			logger.Println("Build Successful")
		}
		buildError = nil
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" || path == "vendor" {
				return filepath.SkipDir
			}

			// ignore hidden files
			if filepath.Base(path)[0] == '.' {
				return nil
			}

			if filepath.Ext(path) == ".go" && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}

func shutdown(runner gin.Runner) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		s := <-c
		log.Println("Got signal: ", s)
		err := runner.Kill()
		if err != nil {
			log.Print("Error killing: ", err)
		}
		os.Exit(1)
	}()
}
<|endoftext|>