<commit_before>/*Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"github.com/skyrings/skyring-common/conf"
	"github.com/skyrings/skyring-common/tools/logger"
	"github.com/skyrings/skyring/apps"
	"github.com/skyrings/skyring/apps/skyring"
	"github.com/skyrings/skyring/event"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

const (
	// ConfigFile default configuration file
	ConfigFile = "skyring.conf"
	// DefaultLogLevel default log level
	DefaultLogLevel = logging.DEBUG
	//SkyringEventSocketFile skyring event socket file
	SkyringEventSocketFile = "/var/run/.skyring-event"
)

var (
	configDir     string
	logFile       string
	logToStderr   bool
	logLevel      string
	providersDir  string
	staticFileDir string
	websocketPort string
	httpPort      int
	sslPort       int
)

func main() {
	app := cli.NewApp()
	app.EnableBashCompletion = true
	app.Name = "skyring"
	app.Version = VERSION + "-" + RELEASE
	app.Authors = []cli.Author{{Name: "SkyRing", Email: "skyring@redhat.com"}}
	app.Usage = "Unified storage management service"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "config-dir, d",
			Value:  "/etc/skyring",
			Usage:  "Override default configuration directory",
			EnvVar: "SKYRING_CONFIGDIR",
		},
		cli.StringFlag{
			Name:   "providers-dir, b",
			Value:  "/var/lib/skyring",
			Usage:  "Override default bin directory",
			EnvVar: "SKYRING_PROVIDERSDIR",
		},
		cli.BoolFlag{
			Name:  "log-to-stderr",
			Usage: "log to console's standard error",
		},
		cli.StringFlag{
			Name:  "log-file, l",
			Value: "/var/log/skyring/skyring.log",
			Usage: "Override default log file",
		},
		cli.StringFlag{
			Name:  "log-level",
			Value: DefaultLogLevel.String(),
			Usage: "Set log level",
		},
		cli.StringFlag{
			Name:   "static-file-dir, s",
			Value:  "/usr/share/skyring/webapp",
			Usage:  "Override default static file serve directory",
			EnvVar: "SKYRING_STATICFILEDIR",
		},
		cli.StringFlag{
			Name:  "websocket-port",
			Value: "8081",
			Usage: "Websocket server port",
		},
		cli.IntFlag{
			Name:  "http-port",
			Value: 8080,
			Usage: "Http server port",
		},
		cli.IntFlag{
			Name:  "ssl-port",
			Value: 10443,
			Usage: "SSL server port",
		},
	}

	app.Before = func(c *cli.Context) error {
		// Set global configuration values
		configDir = c.String("config-dir")
		logToStderr = c.Bool("log-to-stderr")
		logFile = c.String("log-file")
		logLevel = c.String("log-level")
		providersDir = c.String("providers-dir")
		staticFileDir = c.String("static-file-dir")
		websocketPort = c.String("websocket-port")
		httpPort = c.Int("http-port")
		sslPort = c.Int("ssl-port")
		return nil
	}

	app.Action = func(c *cli.Context) {
		args := c.Args()
		if len(args) != 0 {
			fmt.Fprintf(os.Stderr, "invalid arguments: [%s]\n", strings.Join(args, " "))
			os.Exit(-1)
		}
		start()
	}

	app.Run(os.Args)
}

func runHTTP(addr string, ssl bool, sslAddr string, sslParams map[string]string, n *negroni.Negroni) {
	// Start ssl server
	if ssl {
		go func() {
			logger.Get().Info("Started listening on %s", sslAddr)
			if err := http.ListenAndServeTLS(sslAddr, sslParams["cert"], sslParams["key"], n); err != nil {
				logger.Get().Fatalf("Unable to start the webserver. err: %v", err)
			}
		}()
	} else {
		// Start http server
		go func() {
			logger.Get().Info("Started listening on %s", addr)
			if err := http.ListenAndServe(addr, n); err != nil {
				logger.Get().Fatalf("Unable to start the webserver. err: %v", err)
			}
		}()
	}
}

func start() {
	var (
		application app.Application
	)
	var level logging.Level
	var err error
	if level, err = logging.LogLevel(logLevel); err != nil {
		fmt.Fprintf(os.Stderr, "log level error. %s\n", err)
		fmt.Fprintf(os.Stderr, "Using default log level %s\n", DefaultLogLevel)
		level = DefaultLogLevel
	}
	// TODO: use logToStderr when deamonizing.
	if err := logger.Init("skyring", logFile, true, level); err != nil {
		panic(fmt.Sprintf("log init failed. %s", err))
	}

	conf.LoadAppConfiguration(configDir, ConfigFile)
	conf.SystemConfig.Logging.LogToStderr = logToStderr
	conf.SystemConfig.Logging.Filename = logFile
	conf.SystemConfig.Logging.Level = level
	conf.SystemConfig.Config.HttpPort = httpPort
	conf.SystemConfig.Config.SslPort = sslPort

	application = skyring.NewApp(configDir, providersDir)
	if application == nil {
		logger.Get().Fatalf("Unable to start application")
	}

	// Create router for defining the routes.
	//do not allow any routes unless defined.
	router := mux.NewRouter().StrictSlash(true)

	//Load the autheticated routes
	if err := application.SetRoutes(router); err != nil {
		logger.Get().Fatalf("Unable to create http server endpoints. error: %v", err)
	}

	// Use negroni to add middleware.  Here we add the standard
	// middlewares: Recovery, Logger and static file serve which come with
	// Negroni
	n := negroni.New(
		negroni.NewRecovery(),
		negroni.NewLogger(),
		negroni.NewStatic(http.Dir(staticFileDir)),
	)
	//Add the logging context middleware
	n.Use(negroni.HandlerFunc(application.LoggingContext))

	n.UseHandler(router)

	//Initialize the application, db, auth etc
	if err := application.InitializeApplication(conf.SystemConfig, configDir); err != nil {
		logger.Get().Fatalf("Unable to initialize the application. err: %v", err)
	}

	logger.Get().Info("Starting the providers")
	//Load providers and routes
	//Load all the files present in the config path
	if err := application.StartProviders(configDir, providersDir); err != nil {
		logger.Get().Fatalf("Unable to initialize the Providers. err: %v", err)
	}

	logger.Get().Info("Starting event listener")
	go event.StartListener(SkyringEventSocketFile)

	// Starting the WebSocket server
	event.StartBroadcaster(
		websocketPort,
		conf.SystemConfig.Config.SSLEnabled,
		map[string]string{
			"cert": conf.SystemConfig.Config.SslCert,
			"key":  conf.SystemConfig.Config.SslKey,
		})

	runHTTP(
		fmt.Sprintf("%s:%d", conf.SystemConfig.Config.Host, httpPort),
		conf.SystemConfig.Config.SSLEnabled,
		fmt.Sprintf("%s:%d", conf.SystemConfig.Config.Host, sslPort),
		map[string]string{
			"cert": conf.SystemConfig.Config.SslCert,
			"key":  conf.SystemConfig.Config.SslKey,
		},
		n)

	if err := application.PostInitApplication(conf.SystemConfig); err != nil {
		logger.Get().Fatalf("Unable to run Post init. err: %v", err)
	}

	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP)
	go func() {
		sig := <-sigs
		logger.Get().Info("Signal:", sig)
		done <- true
	}()
	<-done
	os.Remove(SkyringEventSocketFile)
}
<commit_msg>skyring: Disable negroni logging<commit_after>/*Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"github.com/skyrings/skyring-common/conf"
	"github.com/skyrings/skyring-common/tools/logger"
	"github.com/skyrings/skyring/apps"
	"github.com/skyrings/skyring/apps/skyring"
	"github.com/skyrings/skyring/event"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

const (
	// ConfigFile default configuration file
	ConfigFile = "skyring.conf"
	// DefaultLogLevel default log level
	DefaultLogLevel = logging.DEBUG
	//SkyringEventSocketFile skyring event socket file
	SkyringEventSocketFile = "/var/run/.skyring-event"
)

var (
	configDir     string
	logFile       string
	logToStderr   bool
	logLevel      string
	providersDir  string
	staticFileDir string
	websocketPort string
	httpPort      int
	sslPort       int
)

func main() {
	app := cli.NewApp()
	app.EnableBashCompletion = true
	app.Name = "skyring"
	app.Version = VERSION + "-" + RELEASE
	app.Authors = []cli.Author{{Name: "SkyRing", Email: "skyring@redhat.com"}}
	app.Usage = "Unified storage management service"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "config-dir, d",
			Value:  "/etc/skyring",
			Usage:  "Override default configuration directory",
			EnvVar: "SKYRING_CONFIGDIR",
		},
		cli.StringFlag{
			Name:   "providers-dir, b",
			Value:  "/var/lib/skyring",
			Usage:  "Override default bin directory",
			EnvVar: "SKYRING_PROVIDERSDIR",
		},
		cli.BoolFlag{
			Name:  "log-to-stderr",
			Usage: "log to console's standard error",
		},
		cli.StringFlag{
			Name:  "log-file, l",
			Value: "/var/log/skyring/skyring.log",
			Usage: "Override default log file",
		},
		cli.StringFlag{
			Name:  "log-level",
			Value: DefaultLogLevel.String(),
			Usage: "Set log level",
		},
		cli.StringFlag{
			Name:   "static-file-dir, s",
			Value:  "/usr/share/skyring/webapp",
			Usage:  "Override default static file serve directory",
			EnvVar: "SKYRING_STATICFILEDIR",
		},
		cli.StringFlag{
			Name:  "websocket-port",
			Value: "8081",
			Usage: "Websocket server port",
		},
		cli.IntFlag{
			Name:  "http-port",
			Value: 8080,
			Usage: "Http server port",
		},
		cli.IntFlag{
			Name:  "ssl-port",
			Value: 10443,
			Usage: "SSL server port",
		},
	}

	app.Before = func(c *cli.Context) error {
		// Set global configuration values
		configDir = c.String("config-dir")
		logToStderr = c.Bool("log-to-stderr")
		logFile = c.String("log-file")
		logLevel = c.String("log-level")
		providersDir = c.String("providers-dir")
		staticFileDir = c.String("static-file-dir")
		websocketPort = c.String("websocket-port")
		httpPort = c.Int("http-port")
		sslPort = c.Int("ssl-port")
		return nil
	}

	app.Action = func(c *cli.Context) {
		args := c.Args()
		if len(args) != 0 {
			fmt.Fprintf(os.Stderr, "invalid arguments: [%s]\n", strings.Join(args, " "))
			os.Exit(-1)
		}
		start()
	}

	app.Run(os.Args)
}

func runHTTP(addr string, ssl bool, sslAddr string, sslParams map[string]string, n *negroni.Negroni) {
	// Start ssl server
	if ssl {
		go func() {
			logger.Get().Info("Started listening on %s", sslAddr)
			if err := http.ListenAndServeTLS(sslAddr, sslParams["cert"], sslParams["key"], n); err != nil {
				logger.Get().Fatalf("Unable to start the webserver. err: %v", err)
			}
		}()
	} else {
		// Start http server
		go func() {
			logger.Get().Info("Started listening on %s", addr)
			if err := http.ListenAndServe(addr, n); err != nil {
				logger.Get().Fatalf("Unable to start the webserver. err: %v", err)
			}
		}()
	}
}

func start() {
	var (
		application app.Application
	)
	var level logging.Level
	var err error
	if level, err = logging.LogLevel(logLevel); err != nil {
		fmt.Fprintf(os.Stderr, "log level error. %s\n", err)
		fmt.Fprintf(os.Stderr, "Using default log level %s\n", DefaultLogLevel)
		level = DefaultLogLevel
	}
	// TODO: use logToStderr when deamonizing.
	if err := logger.Init("skyring", logFile, true, level); err != nil {
		panic(fmt.Sprintf("log init failed. %s", err))
	}

	conf.LoadAppConfiguration(configDir, ConfigFile)
	conf.SystemConfig.Logging.LogToStderr = logToStderr
	conf.SystemConfig.Logging.Filename = logFile
	conf.SystemConfig.Logging.Level = level
	conf.SystemConfig.Config.HttpPort = httpPort
	conf.SystemConfig.Config.SslPort = sslPort

	application = skyring.NewApp(configDir, providersDir)
	if application == nil {
		logger.Get().Fatalf("Unable to start application")
	}

	// Create router for defining the routes.
	//do not allow any routes unless defined.
	router := mux.NewRouter().StrictSlash(true)

	//Load the autheticated routes
	if err := application.SetRoutes(router); err != nil {
		logger.Get().Fatalf("Unable to create http server endpoints. error: %v", err)
	}

	// Use negroni to add middleware.  Here we add the standard
	// middlewares: Recovery, Logger and static file serve which come with
	// Negroni
	n := negroni.New(
		negroni.NewRecovery(),
		negroni.NewStatic(http.Dir(staticFileDir)),
	)
	//Add the logging context middleware
	n.Use(negroni.HandlerFunc(application.LoggingContext))

	n.UseHandler(router)

	//Initialize the application, db, auth etc
	if err := application.InitializeApplication(conf.SystemConfig, configDir); err != nil {
		logger.Get().Fatalf("Unable to initialize the application. err: %v", err)
	}

	logger.Get().Info("Starting the providers")
	//Load providers and routes
	//Load all the files present in the config path
	if err := application.StartProviders(configDir, providersDir); err != nil {
		logger.Get().Fatalf("Unable to initialize the Providers. err: %v", err)
	}

	logger.Get().Info("Starting event listener")
	go event.StartListener(SkyringEventSocketFile)

	// Starting the WebSocket server
	event.StartBroadcaster(
		websocketPort,
		conf.SystemConfig.Config.SSLEnabled,
		map[string]string{
			"cert": conf.SystemConfig.Config.SslCert,
			"key":  conf.SystemConfig.Config.SslKey,
		})

	runHTTP(
		fmt.Sprintf("%s:%d", conf.SystemConfig.Config.Host, httpPort),
		conf.SystemConfig.Config.SSLEnabled,
		fmt.Sprintf("%s:%d", conf.SystemConfig.Config.Host, sslPort),
		map[string]string{
			"cert": conf.SystemConfig.Config.SslCert,
			"key":  conf.SystemConfig.Config.SslKey,
		},
		n)

	if err := application.PostInitApplication(conf.SystemConfig); err != nil {
		logger.Get().Fatalf("Unable to run Post init. err: %v", err)
	}

	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP)
	go func() {
		sig := <-sigs
		logger.Get().Info("Signal:", sig)
		done <- true
	}()
	<-done
	os.Remove(SkyringEventSocketFile)
}
<|endoftext|>


<commit_before>// Copyright 2015 Florian Hülsmann <fh@cbix.de>

package main

import (
	"fmt"
	"github.com/mediocregopher/radix.v2/pool"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv6"
	"net"
	"time"
)

type routerSolicitation struct {
	ip  net.Addr
	mac net.HardwareAddr
}

var (
	pc     *ipv6.PacketConn
	db     *pool.Pool
	rschan chan routerSolicitation
	// default config, might be overwritten by redis hash key fahrrad/config
	AssignedPrefixLength     uint8          = 64
	OnLinkPrefixLength       uint8          = 48
	DefaultValidLifetime     uint32         = 86400
	DefaultPreferredLifetime uint32         = 14400
	TickerDelay              time.Duration  = 5 * time.Minute
	defaultConfig            map[string]int = map[string]int{
		"AssignedPrefixLength":     int(AssignedPrefixLength),
		"OnLinkPrefixLength":       int(OnLinkPrefixLength),
		"DefaultValidLifetime":     int(DefaultValidLifetime),
		"DefaultPreferredLifetime": int(DefaultPreferredLifetime),
		"TickerDelay":              int(TickerDelay / time.Second),
	}
)

func main() {
	var err error
	// create redis connection pool
	if db, err = pool.New("tcp", "localhost:6379", 10); err != nil {
		panic(err)
	}
	defer db.Empty()
	dbc, err := db.Get()
	if err != nil {
		fmt.Println(err)
	}
	for k, v := range defaultConfig {
		dbc.PipeAppend("HSETNX", "fahrrad/config", k, v)
	}
	for k, _ := range defaultConfig {
		dbc.PipeAppend("HGET", "fahrrad/config", k)
	}
	for _, _ = range defaultConfig {
		dbc.PipeResp()
	}
	var v int
	v, err = dbc.PipeResp().Int()
	if err == nil {
		AssignedPrefixLength = uint8(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		OnLinkPrefixLength = uint8(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		DefaultValidLifetime = uint32(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		DefaultPreferredLifetime = uint32(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		TickerDelay = time.Duration(v) * time.Second
	}
	defer db.Put(dbc)

	// open listening connection
	conn, err := net.ListenIP("ip6:ipv6-icmp", &net.IPAddr{net.IPv6unspecified, ""})
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	pc = ipv6.NewPacketConn(conn)
	// RFC4861 requires the hop limit set to 255, but the default value in golang is 64
	pc.SetHopLimit(255)

	// only accept neighbor discovery messages
	filter := new(ipv6.ICMPFilter)
	filter.SetAll(true)
	filter.Accept(ipv6.ICMPTypeRouterSolicitation)
	filter.Accept(ipv6.ICMPTypeRouterAdvertisement)
	filter.Accept(ipv6.ICMPTypeNeighborSolicitation)
	filter.Accept(ipv6.ICMPTypeNeighborAdvertisement)
	if err = pc.SetICMPFilter(filter); err != nil {
		panic(err)
	}

	rschan = make(chan routerSolicitation)
	go hostManager()

	// read from socket
	buf := make([]byte, 512)
	for {
		n, _, srcAddr, err := pc.ReadFrom(buf)
		if err != nil {
			panic(err)
		}
		go handleND(srcAddr, buf[:n])
	}
}

// method hostManager holds a list of hosts that get RAs periodically
func hostManager() {
	var activeRAs map[string]routerSolicitation = make(map[string]routerSolicitation)
	var tick *time.Ticker = time.NewTicker(TickerDelay)
	for {
		select {
		case rs := <-rschan:
			k := string(rs.ip.(*net.IPAddr).IP)
			activeRAs[k] = rs
			success := handleRS(rs)
			if !success {
				delete(activeRAs, k)
			}
		case <-tick.C:
			for _, rs := range activeRAs {
				handleRS(rs)
			}
		}
	}
}

// method handleND parses arbitrary ICMPv6 messages, currently only router solicitations
func handleND(src net.Addr, body []byte) {
	t := ipv6.ICMPType(body[0])
	fmt.Printf("%v from %v\n", t, src)
	switch t {
	case ipv6.ICMPTypeRouterSolicitation:
		// parse ND options
		options, err := parseOptions(body[8:])
		if err != nil {
			fmt.Println(err)
		}

		// check if any of the options is a source LLA
		var lla *NDOptionLLA = nil
		for _, o := range options {
			if o == nil {
				continue
			}
			llaopt, ok := (*o).(*NDOptionLLA)
			if !ok {
				continue
			}
			lla = llaopt
			if int(lla.OptionType) != 1 {
				continue
			}
		}
		if lla == nil {
			fmt.Println("no source LLA option given")
			return
		}
		lladdr := make(net.HardwareAddr, len(lla.Addr))
		copy(lladdr, lla.Addr)
		rschan <- routerSolicitation{src, lladdr}
	default:
		return
	}
}

// method handleRS takes a router solicitation and eventually replies with a unicasted router advertisement
func handleRS(rs routerSolicitation) bool {
	// lookup prefix from redis
	dbc, err := db.Get()
	if err != nil {
		fmt.Println(err)
	}
	defer db.Put(dbc)
	fmt.Printf("looking up prefix for %v ... ", net.HardwareAddr(rs.mac))
	mackey := append([]byte("fahrrad/mac/"), rs.mac...)
	prefix, err := dbc.Cmd("GET", mackey).Bytes()
	if err != nil {
		//fmt.Println(err)
		// i.e. key doesn't exist
		fmt.Printf("not found: %v\n", err)
		return false
	}
	if len(prefix) != 16 {
		fmt.Printf("invalid length: %x\n", prefix)
		return false
	}
	fmt.Printf("found %v/%d\n", net.IP(prefix), AssignedPrefixLength)
	// ICMPv6 RA header:
	msgbody := []byte{0x40, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}

	/*
	   According to RFC 5942, the announced prefixes for on-link usage and autoconfiguration
	   can be separate from each other (there can be an arbitrary number of advertised on-link
	   and autoconf prefixes, respectively).
	   This allows us to use /48 for the link but tell the clients to use /64 for autoconf.
	*/
	// Prefix options:
	// autoconf (assigned prefix) option
	apopt := &NDOptionPrefix{
		PrefixLength:      AssignedPrefixLength,
		OnLink:            false,
		AutoConf:          true,
		ValidLifetime:     DefaultValidLifetime,
		PreferredLifetime: DefaultPreferredLifetime,
		Prefix:            net.IP(prefix).Mask(net.CIDRMask(int(AssignedPrefixLength), 128)),
	}
	// onlink prefix option
	olopt := &NDOptionPrefix{
		PrefixLength:      OnLinkPrefixLength,
		OnLink:            true,
		AutoConf:          false,
		ValidLifetime:     DefaultValidLifetime,
		PreferredLifetime: DefaultPreferredLifetime,
		Prefix:            net.IP(prefix).Mask(net.CIDRMask(int(OnLinkPrefixLength), 128)),
	}
	apoptbytes, err := apopt.Marshal()
	if err != nil {
		// this should never happen
		fmt.Println(err)
		return false
	}
	msgbody = append(msgbody, apoptbytes...)
	oloptbytes, err := olopt.Marshal()
	if err != nil {
		// this should never happen
		fmt.Println(err)
		return false
	}
	msgbody = append(msgbody, oloptbytes...)

	// at this point we could include a source LLA option, but RFC 4861 doesn't require that
	// this would work by taking net.InterfaceByName(src.(*net.IPAddr).Zone) and its HardwareAddr

	// code and checksum are 0, the latter is calculated by the kernel
	// TODO: data structure for RA/ND message body
	msg := &icmp.Message{ipv6.ICMPTypeRouterAdvertisement, 0, 0, &icmp.DefaultMessageBody{msgbody}}
	mb, err := msg.Marshal(nil)
	if err != nil {
		panic(err)
	}
	// send package
	_, err = pc.WriteTo(mb, nil, rs.ip)
	if err != nil {
		panic(err)
	}
	return true
}
<commit_msg>online conf, fixes #1<commit_after>// Copyright 2015 Florian Hülsmann <fh@cbix.de>

package main

import (
	"fmt"
	"github.com/mediocregopher/radix.v2/pool"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv6"
	"net"
	"time"
)

type routerSolicitation struct {
	ip  net.Addr
	mac net.HardwareAddr
}

var (
	pc     *ipv6.PacketConn
	db     *pool.Pool
	rschan chan routerSolicitation
	// default config, might be overwritten by redis hash key fahrrad/config
	AssignedPrefixLength     uint8          = 64
	OnLinkPrefixLength       uint8          = 48
	DefaultValidLifetime     uint32         = 86400
	DefaultPreferredLifetime uint32         = 14400
	TickerDelay              time.Duration  = 5 * time.Minute
	defaultConfig            map[string]int = map[string]int{
		"AssignedPrefixLength":     int(AssignedPrefixLength),
		"OnLinkPrefixLength":       int(OnLinkPrefixLength),
		"DefaultValidLifetime":     int(DefaultValidLifetime),
		"DefaultPreferredLifetime": int(DefaultPreferredLifetime),
		"TickerDelay":              int(TickerDelay / time.Second),
	}
)

func main() {
	var err error
	// create redis connection pool
	if db, err = pool.New("tcp", "localhost:6379", 10); err != nil {
		panic(err)
	}
	defer db.Empty()
	dbc, err := db.Get()
	if err != nil {
		fmt.Println(err)
	}
	for k, v := range defaultConfig {
		dbc.PipeAppend("HSETNX", "fahrrad/config", k, v)
	}
	for k, _ := range defaultConfig {
		dbc.PipeAppend("HGET", "fahrrad/config", k)
	}
	for _, _ = range defaultConfig {
		dbc.PipeResp()
	}
	var v int
	v, err = dbc.PipeResp().Int()
	if err == nil {
		AssignedPrefixLength = uint8(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		OnLinkPrefixLength = uint8(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		DefaultValidLifetime = uint32(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		DefaultPreferredLifetime = uint32(v)
	}
	v, err = dbc.PipeResp().Int()
	if err == nil {
		TickerDelay = time.Duration(v) * time.Second
	}
	defer db.Put(dbc)

	// open listening connection
	conn, err := net.ListenIP("ip6:ipv6-icmp", &net.IPAddr{net.IPv6unspecified, ""})
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	pc = ipv6.NewPacketConn(conn)
	// RFC4861 requires the hop limit set to 255, but the default value in golang is 64
	pc.SetHopLimit(255)

	// only accept neighbor discovery messages
	filter := new(ipv6.ICMPFilter)
	filter.SetAll(true)
	filter.Accept(ipv6.ICMPTypeRouterSolicitation)
	filter.Accept(ipv6.ICMPTypeRouterAdvertisement)
	filter.Accept(ipv6.ICMPTypeNeighborSolicitation)
	filter.Accept(ipv6.ICMPTypeNeighborAdvertisement)
	if err = pc.SetICMPFilter(filter); err != nil {
		panic(err)
	}

	rschan = make(chan routerSolicitation)
	go hostManager()

	// read from socket
	buf := make([]byte, 512)
	for {
		n, _, srcAddr, err := pc.ReadFrom(buf)
		if err != nil {
			panic(err)
		}
		go handleND(srcAddr, buf[:n])
	}
}

// method hostManager holds a list of hosts that get RAs periodically
func hostManager() {
	var activeRAs map[string]routerSolicitation = make(map[string]routerSolicitation)
	var tick *time.Ticker = time.NewTicker(TickerDelay)
	for {
		select {
		case rs := <-rschan:
			k := string(rs.ip.(*net.IPAddr).IP)
			activeRAs[k] = rs
			success := handleRS(rs)
			if !success {
				delete(activeRAs, k)
			}
		case <-tick.C:
			for _, rs := range activeRAs {
				handleRS(rs)
			}
		}
	}
}

// method handleND parses arbitrary ICMPv6 messages, currently only router solicitations
func handleND(src net.Addr, body []byte) {
	t := ipv6.ICMPType(body[0])
	fmt.Printf("%v from %v\n", t, src)
	switch t {
	case ipv6.ICMPTypeRouterSolicitation:
		// parse ND options
		options, err := parseOptions(body[8:])
		if err != nil {
			fmt.Println(err)
		}

		// check if any of the options is a source LLA
		var lla *NDOptionLLA = nil
		for _, o := range options {
			if o == nil {
				continue
			}
			llaopt, ok := (*o).(*NDOptionLLA)
			if !ok {
				continue
			}
			lla = llaopt
			if int(lla.OptionType) != 1 {
				continue
			}
		}
		if lla == nil {
			fmt.Println("no source LLA option given")
			return
		}
		lladdr := make(net.HardwareAddr, len(lla.Addr))
		copy(lladdr, lla.Addr)
		rschan <- routerSolicitation{src, lladdr}
	default:
		return
	}
}

// method handleRS takes a router solicitation and eventually replies with a unicasted router advertisement
func handleRS(rs routerSolicitation) bool {
	// lookup prefix from redis
	dbc, err := db.Get()
	if err != nil {
		fmt.Println(err)
	}
	defer db.Put(dbc)
	fmt.Printf("looking up prefix for %v ... ", net.HardwareAddr(rs.mac))
	mackey := append([]byte("fahrrad/mac/"), rs.mac...)
	prefix, err := dbc.Cmd("GET", mackey).Bytes()
	if err != nil {
		//fmt.Println(err)
		// i.e. key doesn't exist
		fmt.Printf("not found: %v\n", err)
		return false
	}
	if len(prefix) != 16 {
		fmt.Printf("invalid length: %x\n", prefix)
		return false
	}
    // read config from db
    var v int
    var config map[string]int
	for k, dv := range defaultConfig {
        v, err = dbc.Cmd("HGET", "fahrrad/config", k).Int()
        if err != nil {
            v = dv
        }
        config[k] = v
	}

	fmt.Printf("found %v/%d\n", net.IP(prefix), config["AssignedPrefixLength"])
	// ICMPv6 RA header:
	msgbody := []byte{0x40, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}

	/*
	   According to RFC 5942, the announced prefixes for on-link usage and autoconfiguration
	   can be separate from each other (there can be an arbitrary number of advertised on-link
	   and autoconf prefixes, respectively).
	   This allows us to use /48 for the link but tell the clients to use /64 for autoconf.
	*/
	// Prefix options:
	// autoconf (assigned prefix) option
	apopt := &NDOptionPrefix{
		PrefixLength:      uint8(config["AssignedPrefixLength"]),
		OnLink:            false,
		AutoConf:          true,
		ValidLifetime:     uint32(config["DefaultValidLifetime"]),
		PreferredLifetime: uint32(config["DefaultPreferredLifetime"]),
		Prefix:            net.IP(prefix).Mask(net.CIDRMask(config["AssignedPrefixLength"], 128)),
	}
	// onlink prefix option
	olopt := &NDOptionPrefix{
		PrefixLength:      uint8(config["OnLinkPrefixLength"]),
		OnLink:            true,
		AutoConf:          false,
		ValidLifetime:     uint32(config["DefaultValidLifetime"]),
		PreferredLifetime: uint32(config["DefaultPreferredLifetime"]),
		Prefix:            net.IP(prefix).Mask(net.CIDRMask(int(OnLinkPrefixLength), 128)),
	}
	apoptbytes, err := apopt.Marshal()
	if err != nil {
		// this should never happen
		fmt.Println(err)
		return false
	}
	msgbody = append(msgbody, apoptbytes...)
	oloptbytes, err := olopt.Marshal()
	if err != nil {
		// this should never happen
		fmt.Println(err)
		return false
	}
	msgbody = append(msgbody, oloptbytes...)

	// at this point we could include a source LLA option, but RFC 4861 doesn't require that
	// this would work by taking net.InterfaceByName(src.(*net.IPAddr).Zone) and its HardwareAddr

	// code and checksum are 0, the latter is calculated by the kernel
	// TODO: data structure for RA/ND message body
	msg := &icmp.Message{ipv6.ICMPTypeRouterAdvertisement, 0, 0, &icmp.DefaultMessageBody{msgbody}}
	mb, err := msg.Marshal(nil)
	if err != nil {
		panic(err)
	}
	// send package
	_, err = pc.WriteTo(mb, nil, rs.ip)
	if err != nil {
		panic(err)
	}
	return true
}
<|endoftext|>


<commit_before>// Copyright 2017 The tcp-srv Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"flag"
	"log"
	"math/rand"
	"net"
	"time"

	"github.com/go-daq/smbus"
	"github.com/go-daq/smbus/sensor/sht3x"
	"github.com/go-daq/smbus/sensor/tsl2591"
)

var (
	addr    = flag.String("addr", ":10000", "[ip]:port for TCP server")
	busID   = flag.Int("bus-id", 0x1, "SMBus ID number (/dev/i2c-[ID]")
	busAddr = flag.Int("bus-addr", 0x70, "SMBus address to read/write")
	debug   = flag.Bool("dbg", false, "(debugging only)")
)

func main() {
	flag.Parse()

	log.SetFlags(0)
	log.SetPrefix("tcp-srv ")

	log.Printf("starting up server on: %v\n", *addr)

	srv, err := net.Listen("tcp", *addr)
	if err != nil {
		log.Fatal(err)
	}

	if *debug {
		go runClient(*addr)
	}

	for {
		conn, err := srv.Accept()
		if err != nil {
			log.Printf("error accepting connection: %v\n", err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn) {
	defer conn.Close()

	log.Printf("connection from: %v\n", conn.RemoteAddr())

	data, err := fetchData()
	if err != nil {
		log.Printf("error fetching data: %v\n", err)
		return
	}

	err = json.NewEncoder(conn).Encode(data)
	if err != nil {
		log.Printf("error sending json data: %v\n", err)
		return
	}
}

func fetchData() (Sensors, error) {
	if *debug {
		return genData()
	}
	bus, err := smbus.Open(*busID, uint8(*busAddr))
	if err != nil {
		return Sensors{}, err
	}
	defer bus.Close()

	data := Sensors{Timestamp: time.Now().UTC()}
	err = data.read(bus, uint8(*busAddr))
	if err != nil {
		return data, err
	}

	return data, nil
}

type Sensors struct {
	Timestamp time.Time `json:"timestamp"`
	Tsl       Tsl       `json:"tsl"`
	Sht31     Sht31     `json:"sht31"`
	Si7021    [2]Si7021 `json:"si7021"`
	Bme       Bme       `json:"bme280"`
}

func (s *Sensors) read(bus *smbus.Conn, addr uint8) error {
	var err error
	err = s.Tsl.read(bus, addr, 0x01)
	if err != nil {
		return err
	}
	err = s.Sht31.read(bus, addr, 0x02)
	if err != nil {
		return err
	}
	err = s.Si7021[0].read(bus, addr, 0x04)
	if err != nil {
		return err
	}
	err = s.Si7021[1].read(bus, addr, 0x08)
	if err != nil {
		return err
	}
	err = s.Bme.read(bus, addr, 0x10)
	if err != nil {
		return err
	}
	return err
}

type Tsl struct {
	Lux  float64 `json:"lux"`
	Full uint16  `json:"full"`
	IR   uint16  `json:"ir"`
}

func (tsl *Tsl) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	dev, err := tsl2591.Open(bus, tsl2591.Addr, tsl2591.IntegTime100ms, tsl2591.GainLow)
	if err != nil {
		return err
	}

	full, ir, err := dev.FullLuminosity()
	if err != nil {
		return err
	}

	tsl.Lux = dev.Lux(full, ir)
	tsl.Full = full
	tsl.IR = ir

	return err
}

type Sht31 struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"hum"`
}

func (sht *Sht31) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	dev, err := sht3x.Open(bus, sht3x.I2CAddr)
	if err != nil {
		return err
	}

	t, rh, err := dev.Sample()
	if err != nil {
		return err
	}

	sht.Temp = t
	sht.Hum = rh

	err = dev.ClearStatus()
	if err != nil {
		return err
	}

	return nil
}

type Si7021 struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"hum"`
}

func (si *Si7021) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	return err
}

type Bme struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"hum"`
	Pres float64 `json:"pres"`
}

func (bme *Bme) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	return err
}

func runClient(addr string) {
	tick := time.NewTicker(3 * time.Second)
	defer tick.Stop()

	for range tick.C {

		conn, err := net.Dial("tcp", addr)
		if err != nil {
			log.Fatalf("client error: %v\n", err)
		}
		var data Sensors
		err = json.NewDecoder(conn).Decode(&data)
		conn.Close()
		if err != nil {
			log.Fatalf("client decoding error: %v\n", err)
		}
		log.Printf("--> recv: %v\n", data)
	}
}

func genData() (Sensors, error) {
	var err error
	data := Sensors{
		Timestamp: time.Now().UTC(),
		Tsl: Tsl{
			Lux:  rand.Float64(),
			Full: uint16(rand.Uint32()),
			IR:   uint16(rand.Uint32()),
		},
		Sht31: Sht31{
			Temp: rand.Float64(),
			Hum:  rand.Float64(),
		},
		Si7021: [2]Si7021{
			{
				Temp: rand.Float64(),
				Hum:  rand.Float64(),
			},
			{
				Temp: rand.Float64(),
				Hum:  rand.Float64(),
			},
		},
		Bme: Bme{
			Temp: rand.Float64(),
			Hum:  rand.Float64(),
			Pres: rand.Float64(),
		},
	}
	return data, err
}
<commit_msg>tcp-srv: add support for si7021<commit_after>// Copyright 2017 The tcp-srv Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"flag"
	"log"
	"math/rand"
	"net"
	"time"

	"github.com/go-daq/smbus"
	"github.com/go-daq/smbus/sensor/sht3x"
	"github.com/go-daq/smbus/sensor/si7021"
	"github.com/go-daq/smbus/sensor/tsl2591"
)

var (
	addr    = flag.String("addr", ":10000", "[ip]:port for TCP server")
	busID   = flag.Int("bus-id", 0x1, "SMBus ID number (/dev/i2c-[ID]")
	busAddr = flag.Int("bus-addr", 0x70, "SMBus address to read/write")
	debug   = flag.Bool("dbg", false, "(debugging only)")
)

func main() {
	flag.Parse()

	log.SetFlags(0)
	log.SetPrefix("tcp-srv ")

	log.Printf("starting up server on: %v\n", *addr)

	srv, err := net.Listen("tcp", *addr)
	if err != nil {
		log.Fatal(err)
	}

	if *debug {
		go runClient(*addr)
	}

	for {
		conn, err := srv.Accept()
		if err != nil {
			log.Printf("error accepting connection: %v\n", err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn) {
	defer conn.Close()

	log.Printf("connection from: %v\n", conn.RemoteAddr())

	data, err := fetchData()
	if err != nil {
		log.Printf("error fetching data: %v\n", err)
		return
	}

	err = json.NewEncoder(conn).Encode(data)
	if err != nil {
		log.Printf("error sending json data: %v\n", err)
		return
	}
}

func fetchData() (Sensors, error) {
	if *debug {
		return genData()
	}
	bus, err := smbus.Open(*busID, uint8(*busAddr))
	if err != nil {
		return Sensors{}, err
	}
	defer bus.Close()

	data := Sensors{Timestamp: time.Now().UTC()}
	err = data.read(bus, uint8(*busAddr))
	if err != nil {
		return data, err
	}

	return data, nil
}

type Sensors struct {
	Timestamp time.Time `json:"timestamp"`
	Tsl       Tsl       `json:"tsl"`
	Sht31     Sht31     `json:"sht31"`
	Si7021    [2]Si7021 `json:"si7021"`
	Bme       Bme       `json:"bme280"`
}

func (s *Sensors) read(bus *smbus.Conn, addr uint8) error {
	var err error
	err = s.Tsl.read(bus, addr, 0x01)
	if err != nil {
		return err
	}
	err = s.Sht31.read(bus, addr, 0x02)
	if err != nil {
		return err
	}
	err = s.Si7021[0].read(bus, addr, 0x04)
	if err != nil {
		return err
	}
	err = s.Si7021[1].read(bus, addr, 0x08)
	if err != nil {
		return err
	}
	err = s.Bme.read(bus, addr, 0x10)
	if err != nil {
		return err
	}
	return err
}

type Tsl struct {
	Lux  float64 `json:"lux"`
	Full uint16  `json:"full"`
	IR   uint16  `json:"ir"`
}

func (tsl *Tsl) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	dev, err := tsl2591.Open(bus, tsl2591.Addr, tsl2591.IntegTime100ms, tsl2591.GainLow)
	if err != nil {
		return err
	}

	full, ir, err := dev.FullLuminosity()
	if err != nil {
		return err
	}

	tsl.Lux = dev.Lux(full, ir)
	tsl.Full = full
	tsl.IR = ir

	return err
}

type Sht31 struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"hum"`
}

func (sht *Sht31) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	dev, err := sht3x.Open(bus, sht3x.I2CAddr)
	if err != nil {
		return err
	}

	t, rh, err := dev.Sample()
	if err != nil {
		return err
	}

	sht.Temp = t
	sht.Hum = rh

	err = dev.ClearStatus()
	if err != nil {
		return err
	}

	return nil
}

type Si7021 struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"hum"`
}

func (si *Si7021) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	dev, err := si7021.Open(bus, 0x40)
	if err != nil {
		return err
	}

	h, err := dev.Humidity()
	if err != nil {
		return err
	}

	t, err := dev.Temperature()
	if err != nil {
		return err
	}

	si.Temp = t
	si.Hum = h
	return err
}

type Bme struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"hum"`
	Pres float64 `json:"pres"`
}

func (bme *Bme) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		return err
	}

	return err
}

func runClient(addr string) {
	tick := time.NewTicker(3 * time.Second)
	defer tick.Stop()

	for range tick.C {

		conn, err := net.Dial("tcp", addr)
		if err != nil {
			log.Fatalf("client error: %v\n", err)
		}
		var data Sensors
		err = json.NewDecoder(conn).Decode(&data)
		conn.Close()
		if err != nil {
			log.Fatalf("client decoding error: %v\n", err)
		}
		log.Printf("--> recv: %v\n", data)
	}
}

func genData() (Sensors, error) {
	var err error
	data := Sensors{
		Timestamp: time.Now().UTC(),
		Tsl: Tsl{
			Lux:  rand.Float64(),
			Full: uint16(rand.Uint32()),
			IR:   uint16(rand.Uint32()),
		},
		Sht31: Sht31{
			Temp: rand.Float64(),
			Hum:  rand.Float64(),
		},
		Si7021: [2]Si7021{
			{
				Temp: rand.Float64(),
				Hum:  rand.Float64(),
			},
			{
				Temp: rand.Float64(),
				Hum:  rand.Float64(),
			},
		},
		Bme: Bme{
			Temp: rand.Float64(),
			Hum:  rand.Float64(),
			Pres: rand.Float64(),
		},
	}
	return data, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"runtime"

	"code.google.com/p/gopacket/layers"
	"code.google.com/p/gopacket/pcap"
	//"code.google.com/p/gopacket/pcapgo"
)
import "code.google.com/p/gopacket"

import (
	"github.com/zionist/gossip/base"
	"github.com/zionist/gossip/parser"
)

func doParse(inPackages chan gopacket.Packet, outPackages chan base.SipMessage, done chan int) {
	for packet := range inPackages {
		if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
			//fmt.Println(packet.Metadata().Timestamp)
			udp, _ := udpLayer.(*layers.UDP)
			msg, err := parser.ParseMessage(udp.Payload)
			if err != nil {
				fmt.Println(err)
				//panic("Err in thread")
			} else {
				outPackages <- msg
				fmt.Printf("msg %s, in goroutine %d \n", msg.Short(), runtime.NumGoroutine())
			}
		}
	}
	done <- runtime.NumGoroutine()
}

func getParse(packages chan base.SipMessage) {
	for msg := range packages {
		fmt.Println(msg.Short())
	}
	//for range packages {
	//
	//	}
}

func main() {
	// here we go
	runtime.GOMAXPROCS(runtime.NumCPU())

	if handle, err := pcap.OpenOffline("/home/slaviann/work/teligent/multifone/samples/big.pcap"); err != nil {
		panic(err)
	} else {
		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
		packages := make(chan base.SipMessage)
		//udpPackages := make(chan *layers.UDP)

		done := make(chan int, runtime.NumCPU())
		for i := 0; i < runtime.NumCPU(); i++ {
			go doParse(packetSource.Packets(), packages, done)
			go getParse(packages)
		}
		//wait all doParse gouroutines finished
		for i := 0; i < runtime.NumCPU(); i++ {
			<-done
		}
		close(packages)
	}

}
<commit_msg>correct goroutine end waiting<commit_after>package main

import (
	"fmt"
	"runtime"

	"code.google.com/p/go-uuid/uuid"
	"code.google.com/p/gopacket/layers"
	"code.google.com/p/gopacket/pcap"
	//"code.google.com/p/gopacket/pcapgo"
)
import "code.google.com/p/gopacket"

import (
	"github.com/zionist/gossip/base"
	"github.com/zionist/gossip/parser"
)

func doParse(inPackages chan gopacket.Packet, outPackages chan base.SipMessage, done chan string) {
	uuid := uuid.NewRandom()
	for packet := range inPackages {
		if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
			//fmt.Println(packet.Metadata().Timestamp)
			udp, _ := udpLayer.(*layers.UDP)
			msg, err := parser.ParseMessage(udp.Payload)
			if err != nil {
				fmt.Println(err)
				//panic("Err in thread")
			} else {
				outPackages <- msg
				//fmt.Printf("msg %s, in goroutine %s \n", msg.Short(), uuid.String())
			}
		}
	}
	done <- uuid.String()
	//fmt.Printf("Goroutine %s finished\n", uuid.String())
}

func getParse(packages chan base.SipMessage, done chan string) {
	uuid := uuid.NewRandom()
	for msg := range packages {
		fmt.Println(msg.Short())
	}
	done <- uuid.String()
	//for range packages {
	//
	//	}
}

func main() {
	// here we go
	runtime.GOMAXPROCS(runtime.NumCPU())

	if handle, err := pcap.OpenOffline("/home/slaviann/work/teligent/multifone/samples/small.pcap"); err != nil {
		panic(err)
	} else {
		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
		packages := make(chan base.SipMessage)
		//udpPackages := make(chan *layers.UDP)

		doneParse := make(chan string, runtime.NumCPU())
		doneGet := make(chan string, runtime.NumCPU())
		for i := 0; i < runtime.NumCPU(); i++ {
			go doParse(packetSource.Packets(), packages, doneParse)
			go getParse(packages, doneGet)
		}

		for i := 0; i < runtime.NumCPU(); i++ {
			<-doneParse
			//fmt.Printf("goroutine %s finished \n", <-done)
		}
		close(packages)
		for i := 0; i < runtime.NumCPU(); i++ {
			<-doneGet
			//fmt.Printf("goroutine %s finished \n", <-done2)
		}
	}

}
<|endoftext|>


<commit_before>package gen

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"time"
)

var (
	flagOut = flag.String("out", "", "output dir")
	flagN   = flag.Int("n", 1000, "number of inputs to generate")
	seq     = 0
)

func init() {
	flag.Parse()
	if *flagOut == "" {
		fmt.Fprintf(os.Stderr, "output directory is not set\n")
		os.Exit(1)
	}
	if err := os.MkdirAll(*flagOut, 0760); err != nil {
		fmt.Fprintf(os.Stderr, "mkdir failed: %v\n", err)
		os.Exit(1)
	}
	rand.Seed(time.Now().UnixNano())
}

func Rand(n int) int {
	return rand.Intn(n)
}

func Emit(data, hint []byte, valid bool) {
	filePath := filepath.Join(*flagOut, fmt.Sprintf("%d", seq))
	f, err := os.Create(filePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create file: %s\n", err.Error())
		os.Exit(1)
	}
	defer f.Close()

	n, err := f.Write(data)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create file: %s\n", err.Error())
		os.Exit(1)
	} else if n != len(data) {
		fmt.Fprint(os.Stderr, "failed to write data to file\n")
		os.Exit(1)
	}

	if seq++; seq == *flagN {
		os.Exit(0)
	}
}
<commit_msg>Fix formatting<commit_after>package gen

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"time"
)

var (
	flagOut = flag.String("out", "", "output dir")
	flagN   = flag.Int("n", 1000, "number of inputs to generate")
	seq     = 0
)

func init() {
	flag.Parse()
	if *flagOut == "" {
		fmt.Fprintf(os.Stderr, "output directory is not set\n")
		os.Exit(1)
	}
	if err := os.MkdirAll(*flagOut, 0760); err != nil {
		fmt.Fprintf(os.Stderr, "mkdir failed: %v\n", err)
		os.Exit(1)
	}
	rand.Seed(time.Now().UnixNano())
}

func Rand(n int) int {
	return rand.Intn(n)
}

func Emit(data, hint []byte, valid bool) {
	f, err := os.Create(filepath.Join(*flagOut, fmt.Sprintf("%d", seq)))
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create file: %v\n", err)
		os.Exit(1)
	}
	defer f.Close()

	n, err := f.Write(data)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to write to file: %v\n", err)
		os.Exit(1)
	} else if n != len(data) {
		fmt.Fprint(os.Stderr, "failed to write data to file\n")
		os.Exit(1)
	}

	if seq++; seq == *flagN {
		os.Exit(0)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/lox/parfait/cmd"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	Version string
)

func main() {
	run(os.Args[1:], os.Exit)
}

func run(args []string, exit func(code int)) {
	app := kingpin.New("parfait",
		`A tool for creating and monitoring AWS CloudFormation stacks`)

	app.Version(Version)
	app.Writer(os.Stdout)
	app.DefaultEnvars()
	app.Terminate(exit)

	sess, err := session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
	})
	if err != nil {
		log.Fatal(err)
	}

	cmd.ConfigureWatchStack(app, sess)
	cmd.ConfigureListStacks(app, sess)
	cmd.ConfigureListStackOutputs(app, sess)
	cmd.ConfigureCreateStack(app, sess)
	cmd.ConfigureUpdateStack(app, sess)
	cmd.ConfigureDeleteStack(app, sess)
	cmd.ConfigureFollowLogs(app, sess)

	kingpin.MustParse(app.Parse(args))
}
<commit_msg>Read version from version/version.go<commit_after>package main

import (
	"log"
	"os"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/lox/parfait/cmd"
	"github.com/lox/parfait/version"
	"gopkg.in/alecthomas/kingpin.v2"
)

func main() {
	run(os.Args[1:], os.Exit)
}

func run(args []string, exit func(code int)) {
	app := kingpin.New("parfait",
		`A tool for creating and monitoring AWS CloudFormation stacks`)

	app.Version(version.Version)
	app.Writer(os.Stdout)
	app.DefaultEnvars()
	app.Terminate(exit)

	sess, err := session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
	})
	if err != nil {
		log.Fatal(err)
	}

	cmd.ConfigureWatchStack(app, sess)
	cmd.ConfigureListStacks(app, sess)
	cmd.ConfigureListStackOutputs(app, sess)
	cmd.ConfigureCreateStack(app, sess)
	cmd.ConfigureUpdateStack(app, sess)
	cmd.ConfigureDeleteStack(app, sess)
	cmd.ConfigureFollowLogs(app, sess)

	kingpin.MustParse(app.Parse(args))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sort"

	"github.com/jagregory/cfval/parse"
	"github.com/jagregory/cfval/reporting"
	"github.com/jagregory/cfval/resources"
	"github.com/jagregory/cfval/schema"
	"github.com/mitchellh/cli"
)

type ByPath reporting.Reports

func (a ByPath) Len() int           { return len(a) }
func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPath) Less(i, j int) bool { return a[i].PathReadable < a[j].PathReadable }

var ui = &cli.ColoredUi{
	InfoColor:  cli.UiColorNone,
	ErrorColor: cli.UiColorRed,
	WarnColor:  cli.UiColorYellow,
	Ui: &cli.BasicUi{
		Reader:      os.Stdin,
		Writer:      os.Stdout,
		ErrorWriter: os.Stderr,
	},
}

func printReports(reports reporting.Reports) {
	sort.Sort(ByPath(reports))

	maxLength := 0
	for _, report := range reports {
		path := report.PathReadable
		if len(path) > maxLength {
			maxLength = len(path)
		}
	}

	for _, report := range reports {
		path := report.PathReadable

		str := path
		str += " "
		for i := 0; i < maxLength-len(path); i++ {
			str += "."
		}
		str += "... "
		str += report.Message

		if report.Level == reporting.Failure {
			ui.Error(str)
		} else if report.Level == reporting.Warning {
			ui.Warn(str)
		} else {
			ui.Info(str)
		}

		fmt.Println()
	}
}

func getReadStream(args []string) (io.Reader, error) {
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		return os.Stdin, nil
	} else if len(args) > 0 {
		return os.Open(args[0])
	} else {
		return nil, fmt.Errorf("Provide either a filename or pipe something")
	}
}

type ValidateCommand struct{}

func (ValidateCommand) Help() string {
	return `
Usage: cfval validate [filename]
       cat filename | cfval validate

  Given a CloudFormation JSON template, validate will parse and execute various
  tests against the template. Any problems will be printed and a non-zero exit
  code reported.

Options:

  -forgiving             Ignore unrecognised resource types
  -warnings-as-errors    Treat warnings as errors
`
}

func (ValidateCommand) Synopsis() string {
	return "Validate a CloudFormation template"
}

func (c ValidateCommand) Run(args []string) int {
	var warningsAsErrors bool
	var forgiving bool

	cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError)
	cmdFlags.BoolVar(&warningsAsErrors, "warnings-as-errors", false, "warnings-as-errors")
	cmdFlags.BoolVar(&forgiving, "forgiving", false, "forgiving")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()
	if len(args) == 0 {
		return cli.RunResultHelp
	}

	stream, err := getReadStream(args)
	if err != nil {
		fmt.Println(err)
		return 1
	}

	bytes, err := ioutil.ReadAll(stream)
	if err != nil {
		fmt.Println("Error reading JSON from Stdin")
		return 1
	}

	template, err := parse.ParseTemplateJSON(bytes, forgiving)
	if err != nil {
		fmt.Println("Error parsing JSON:", err)
		return 1
	}

	_, reports := schema.TemplateValidate(template, schema.NewResourceDefinitions(resources.AwsTypes))
	stats := reports.Stats()

	printReports(reports)

	if warningsAsErrors || stats.Failures > 0 {
		fmt.Printf("Fail: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
		return 1
	}

	fmt.Printf("Pass: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
	return 0
}

func main() {
	app := cli.NewCLI("cfval", "0.1.0")
	app.Args = os.Args[1:]
	app.Commands = map[string]cli.CommandFactory{
		"validate": func() (cli.Command, error) {
			return ValidateCommand{}, nil
		},
	}

	exitStatus, err := app.Run()
	if err != nil {
		log.Println(err)
	}

	os.Exit(exitStatus)
}
<commit_msg>Fix whitespace<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sort"

	"github.com/jagregory/cfval/parse"
	"github.com/jagregory/cfval/reporting"
	"github.com/jagregory/cfval/resources"
	"github.com/jagregory/cfval/schema"
	"github.com/mitchellh/cli"
)

type ByPath reporting.Reports

func (a ByPath) Len() int           { return len(a) }
func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPath) Less(i, j int) bool { return a[i].PathReadable < a[j].PathReadable }

var ui = &cli.ColoredUi{
	InfoColor:  cli.UiColorNone,
	ErrorColor: cli.UiColorRed,
	WarnColor:  cli.UiColorYellow,
	Ui: &cli.BasicUi{
		Reader:      os.Stdin,
		Writer:      os.Stdout,
		ErrorWriter: os.Stderr,
	},
}

func printReports(reports reporting.Reports) {
	sort.Sort(ByPath(reports))

	maxLength := 0
	for _, report := range reports {
		path := report.PathReadable
		if len(path) > maxLength {
			maxLength = len(path)
		}
	}

	for _, report := range reports {
		path := report.PathReadable

		str := path
		str += " "
		for i := 0; i < maxLength-len(path); i++ {
			str += "."
		}
		str += "... "
		str += report.Message

		if report.Level == reporting.Failure {
			ui.Error(str)
		} else if report.Level == reporting.Warning {
			ui.Warn(str)
		} else {
			ui.Info(str)
		}
	}

	if len(reports) > 0 {
		fmt.Println()
	}
}

func getReadStream(args []string) (io.Reader, error) {
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		return os.Stdin, nil
	} else if len(args) > 0 {
		return os.Open(args[0])
	} else {
		return nil, fmt.Errorf("Provide either a filename or pipe something")
	}
}

type ValidateCommand struct{}

func (ValidateCommand) Help() string {
	return `
Usage: cfval validate [filename]
       cat filename | cfval validate

  Given a CloudFormation JSON template, validate will parse and execute various
  tests against the template. Any problems will be printed and a non-zero exit
  code reported.

Options:

  -forgiving             Ignore unrecognised resource types
  -warnings-as-errors    Treat warnings as errors
`
}

func (ValidateCommand) Synopsis() string {
	return "Validate a CloudFormation template"
}

func (c ValidateCommand) Run(args []string) int {
	var warningsAsErrors bool
	var forgiving bool

	cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError)
	cmdFlags.BoolVar(&warningsAsErrors, "warnings-as-errors", false, "warnings-as-errors")
	cmdFlags.BoolVar(&forgiving, "forgiving", false, "forgiving")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()
	if len(args) == 0 {
		return cli.RunResultHelp
	}

	stream, err := getReadStream(args)
	if err != nil {
		fmt.Println(err)
		return 1
	}

	bytes, err := ioutil.ReadAll(stream)
	if err != nil {
		fmt.Println("Error reading JSON from Stdin")
		return 1
	}

	template, err := parse.ParseTemplateJSON(bytes, forgiving)
	if err != nil {
		fmt.Println("Error parsing JSON:", err)
		return 1
	}

	_, reports := schema.TemplateValidate(template, schema.NewResourceDefinitions(resources.AwsTypes))
	stats := reports.Stats()

	printReports(reports)

	if warningsAsErrors || stats.Failures > 0 {
		fmt.Printf("Fail: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
		return 1
	}

	fmt.Printf("Pass: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
	return 0
}

func main() {
	app := cli.NewCLI("cfval", "0.1.0")
	app.Args = os.Args[1:]
	app.Commands = map[string]cli.CommandFactory{
		"validate": func() (cli.Command, error) {
			return ValidateCommand{}, nil
		},
	}

	exitStatus, err := app.Run()
	if err != nil {
		log.Println(err)
	}

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


<commit_before>package main

import (
	"keyvalue/client"
	"keyvalue/server"

	"github.com/jessevdk/go-flags"

	"log"
	"os"
	"runtime"
	"strconv"
	"strings"
)

type service interface {
	Get(key string) (int, string)
	Set(key string, value string) (int, string)
	Close()
}

type operation struct {
	key   string
	value string
}

var (
	opts struct {
		// Callbacks called each time the option is found.
		Get func(string) `short:"g" long:"get" description:"Get a key from the server"`
		Set func(string) `short:"s" long:"set" description:"Set a key on the server (key=value)"`

		// Boolean for whether this should act as a server or client
		Client bool `short:"c" long:"client" description:"Acts as a client when specified"`
	}
	args       []string
	operations = make(chan operation, len(os.Args))
)

func init() {
	opts.Get = func(key string) {
		operations <- operation{key: key}
	}

	opts.Set = func(keyvalue string) {
		split := strings.Split(keyvalue, "=")
		if len(split) < 2 {
			log.Fatalf("Set operation '-s %s' must be in the form '-s key=value'\n", keyvalue)
		}
		operations <- operation{key: split[0], value: strings.Join(split[1:], "=")}
	}

	var err error
	args, err = flags.Parse(&opts)
	if err != nil {
		log.Fatalf("Error parsing options: &v\n", err)
	}

	if len(args) < 1 {
		if opts.Client {
			log.Fatalln("Need to specify address for client to connect")
		} else {
			log.Fatalln("Need to specify port for server to open")
		}

	}

	// Set runtime GOMAXPROCS
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	var service service
	if opts.Client {
		_, service = client.Init(args[0])
	} else {
		port, err := strconv.Atoi(args[0])
		if err == nil {
			_, service = server.Init(uint16(port))
		} else {
			split := strings.Split(args[0], ":")
			port, err := strconv.Atoi(split[len(split)-1])
			if err == nil {
				_, service = server.Init(uint16(port))
			} else {
				log.Fatalf("Could not parse port from '%s': %v", args[0], err)
			}
		}
	}

	for oper := range operations {
		go func(oper operation) {
			if oper.value == "" {
				result, value := service.Get(oper.key)
				log.Printf("Called Get(key=%s) Received(result=%d, value=%s)\n", oper.key, result, value)
			} else {
				result, old := service.Set(oper.key, oper.value)
				log.Printf("Called Set(key=%s, value=%s) Received(result=%d, value=%s)\n", oper.key, oper.value, result, old)
			}
		}(oper)
	}
}
<commit_msg>Guarantee client calls are done in order by writing them as blocking<commit_after>package main

import (
	"keyvalue/client"
	"keyvalue/server"

	"github.com/jessevdk/go-flags"

	"log"
	"os"
	"runtime"
	"strconv"
	"strings"
)

type service interface {
	Get(key string) (int, string)
	Set(key string, value string) (int, string)
	Close()
}

type operation struct {
	key   string
	value string
}

var (
	opts struct {
		// Callbacks called each time the option is found.
		Get func(string) `short:"g" long:"get" description:"Get a key from the server"`
		Set func(string) `short:"s" long:"set" description:"Set a key on the server (key=value)"`

		// Boolean for whether this should act as a server or client
		Client bool `short:"c" long:"client" description:"Acts as a client when specified"`
	}
	args       []string
	operations = make(chan operation, len(os.Args))
)

func init() {
	opts.Get = func(key string) {
		operations <- operation{key: key}
	}

	opts.Set = func(keyvalue string) {
		split := strings.Split(keyvalue, "=")
		if len(split) < 2 {
			log.Fatalf("Set operation '-s %s' must be in the form '-s key=value'\n", keyvalue)
		}
		operations <- operation{key: split[0], value: strings.Join(split[1:], "=")}
	}

	var err error
	args, err = flags.Parse(&opts)
	if err != nil {
		log.Fatalf("Error parsing options: &v\n", err)
	}

	if len(args) < 1 {
		if opts.Client {
			log.Fatalln("Need to specify address for client to connect")
		} else {
			log.Fatalln("Need to specify port for server to open")
		}

	}

	// Set runtime GOMAXPROCS
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	var service service
	if opts.Client {
		_, service = client.Init(args[0])
	} else {
		port, err := strconv.Atoi(args[0])
		if err == nil {
			_, service = server.Init(uint16(port))
		} else {
			split := strings.Split(args[0], ":")
			port, err := strconv.Atoi(split[len(split)-1])
			if err == nil {
				_, service = server.Init(uint16(port))
			} else {
				log.Fatalf("Could not parse port from '%s': %v", args[0], err)
			}
		}
	}

	for oper := range operations {
		if oper.value == "" {
			result, value := service.Get(oper.key)
			log.Printf("Called Get(key=%s) Received(result=%d, value=%s)\n", oper.key, result, value)
		} else {
			result, old := service.Set(oper.key, oper.value)
			log.Printf("Called Set(key=%s, value=%s) Received(result=%d, value=%s)\n", oper.key, oper.value, result, old)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
)

type symbols []string

type StockInfo struct {
	price      float64
	changePerc float64
	symbol     string
}

type JsonResp struct {
	List JsonRespList
}

type JsonRespMeta struct {
	Count int
}

type JsonRespList struct {
	Resources []JsonRespResourceCont
	Meta      JsonRespMeta
}

type JsonRespResourceCont struct {
	Resource JsonRespResource
}

type JsonRespResource struct {
	Fields JsonRespFields
}

type JsonRespFields struct {
	Price       float64 `json:",string"`
	Chg_percent float64 `json:",string"`
}

func (s *symbols) String() string {
	return fmt.Sprint(*s)
}

// https://golang.org/src/flag/example_test.go
func (s *symbols) Set(value string) error {
	if len(*s) > 0 {
		return errors.New("symbols value already set")
	}
	for _, sym := range strings.Split(value, ",") {

		sym = strings.ToUpper(sym)

		if sym == "BTC" {
			sym = "BTCUSD=X"
		}

		*s = append(*s, sym)
	}
	return nil
}

var inputSymbols symbols
var inputFile string
var verboseFlag bool

func main() {
	flag.Parse()
	stocks := make(map[string]StockInfo)
	ch := make(chan StockInfo)

	if len(inputFile) > 0 {
		f, err := os.Open(inputFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "mq: Usage of %s:\n", os.Args[0])
			flag.PrintDefaults()
			return
		}
		appendSymbolsFromFile(f, &inputSymbols)
		f.Close()
	}

	if len(inputSymbols) == 0 {
		fmt.Fprintf(os.Stderr, "mq: Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		return
	}

	for _, sym := range inputSymbols {
		go fetch(sym, ch)
	}

	for range inputSymbols {
		info := <-ch
		stocks[info.symbol] = info
	}

	fmt.Println("         |Price     |% Change today")
	for sym, info := range stocks {
		printStock(sym, info)
	}
}

func printStock(sym string, i StockInfo) {
	start := "\033[32m+"

	if i.changePerc < 0.0 {
		start = "\033[31m"
	} else {

	}
	fmt.Printf("%-9s|%-10.2f|%s%.2f%%\033[0m\n", sym, i.price, start, i.changePerc)
	// fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
}

func getUrl(sym string) string {
	const yahooUrl string = "http://finance.yahoo.com/webservice/v1/symbols/%s/quote?format=json&view=detail"
	return fmt.Sprintf(yahooUrl, sym)
}

func fetch(sym string, ch chan<- StockInfo) {

	var errorResponse StockInfo = StockInfo{price: 0.0, changePerc: 0.0, symbol: sym}

	url := getUrl(sym)
	resp, err := http.Get(url)
	if err != nil {
		if verboseFlag {
			fmt.Println(err)
		}
		ch <- errorResponse
		return
	}
	defer resp.Body.Close()

	// Debug
	// contents, err := ioutil.ReadAll(resp.Body)
	// fmt.Println(string(contents))

	decoder := json.NewDecoder(resp.Body)
	var data JsonResp
	err = decoder.Decode(&data)

	if err != nil {
		ch <- errorResponse
		return
	}

	if data.List.Meta.Count == 0 {
		ch <- errorResponse
		return
	}
	ch <- StockInfo{price: data.List.Resources[0].Resource.Fields.Price,
		changePerc: data.List.Resources[0].Resource.Fields.Chg_percent,
		symbol:     sym}
}

func appendSymbolsFromFile(f *os.File, syms *symbols) {
	input := bufio.NewScanner(f)
	for input.Scan() {
		*syms = append(*syms, input.Text())
	}
}

func init() {
	flag.Var(&inputSymbols, "s", "space separated symbols")
	flag.StringVar(&inputFile, "f", "", "newline separated file of symbols")
	flag.BoolVar(&verboseFlag, "v", false, "verbose")
}
<commit_msg>Use tabwriter for formatting output.<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
	"text/tabwriter"
)

type symbols []string

type StockInfo struct {
	price      float64
	changePerc float64
	symbol     string
}

type JsonResp struct {
	List JsonRespList
}

type JsonRespMeta struct {
	Count int
}

type JsonRespList struct {
	Resources []JsonRespResourceCont
	Meta      JsonRespMeta
}

type JsonRespResourceCont struct {
	Resource JsonRespResource
}

type JsonRespResource struct {
	Fields JsonRespFields
}

type JsonRespFields struct {
	Price       float64 `json:",string"`
	Chg_percent float64 `json:",string"`
}

func (s *symbols) String() string {
	return fmt.Sprint(*s)
}

// https://golang.org/src/flag/example_test.go
func (s *symbols) Set(value string) error {
	if len(*s) > 0 {
		return errors.New("symbols value already set")
	}
	for _, sym := range strings.Split(value, ",") {

		sym = strings.ToUpper(sym)

		if sym == "BTC" {
			sym = "BTCUSD=X"
		}

		*s = append(*s, sym)
	}
	return nil
}

var inputSymbols symbols
var inputFile string
var verboseFlag bool

func main() {
	flag.Parse()
	stocks := make(map[string]StockInfo)
	ch := make(chan StockInfo)

	if len(inputFile) > 0 {
		f, err := os.Open(inputFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "mq: Usage of %s:\n", os.Args[0])
			flag.PrintDefaults()
			return
		}
		appendSymbolsFromFile(f, &inputSymbols)
		f.Close()
	}

	if len(inputSymbols) == 0 {
		fmt.Fprintf(os.Stderr, "mq: Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		return
	}

	for _, sym := range inputSymbols {
		go fetch(sym, ch)
	}

	for range inputSymbols {
		info := <-ch
		stocks[info.symbol] = info
	}
	fmt.Println("")
	printStocks(stocks)
	fmt.Println("")
}

func printStocks(stocks map[string]StockInfo) {
	const headerFmt = "%v\t%v\t%v\t\n"
	const rowFmt = "%s\t%.2f\t%s%.2f%%%s\t\n"
	const colorEnd = "\033[0m"
	tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
	fmt.Fprintf(tw, headerFmt, "Symbol", "Price ($)", "Change today (%)")
	fmt.Fprintf(tw, headerFmt, "------", "---------", "----------------")
	for sym, info := range stocks {
		colorStart := "\033[32m+"
		if info.changePerc < 0.0 {
			colorStart = "\033[31m"
		}
		fmt.Fprintf(tw, rowFmt, sym, info.price, colorStart, info.changePerc, colorEnd)
	}
	tw.Flush()
}

func getUrl(sym string) string {
	const yahooUrl string = "http://finance.yahoo.com/webservice/v1/symbols/%s/quote?format=json&view=detail"
	return fmt.Sprintf(yahooUrl, sym)
}

func fetch(sym string, ch chan<- StockInfo) {

	var errorResponse StockInfo = StockInfo{price: 0.0, changePerc: 0.0, symbol: sym}

	url := getUrl(sym)
	resp, err := http.Get(url)
	if err != nil {
		if verboseFlag {
			fmt.Println(err)
		}
		ch <- errorResponse
		return
	}
	defer resp.Body.Close()

	// Debug
	// contents, err := ioutil.ReadAll(resp.Body)
	// fmt.Println(string(contents))

	decoder := json.NewDecoder(resp.Body)
	var data JsonResp
	err = decoder.Decode(&data)

	if err != nil {
		ch <- errorResponse
		return
	}

	if data.List.Meta.Count == 0 {
		ch <- errorResponse
		return
	}
	ch <- StockInfo{price: data.List.Resources[0].Resource.Fields.Price,
		changePerc: data.List.Resources[0].Resource.Fields.Chg_percent,
		symbol:     sym}
}

func appendSymbolsFromFile(f *os.File, syms *symbols) {
	input := bufio.NewScanner(f)
	for input.Scan() {
		*syms = append(*syms, input.Text())
	}
}

func init() {
	flag.Var(&inputSymbols, "s", "space separated symbols")
	flag.StringVar(&inputFile, "f", "", "newline separated file of symbols")
	flag.BoolVar(&verboseFlag, "v", false, "verbose")
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "time"
    "github.com/carbocation/forum.git/forum"
    "math/rand"
)

var N int = 10

func main() {
    makeEntries()
    closureTable()
}

func closureTable() {
    // Create the closure table with a single progenitor
    ct := forum.ClosureTable{forum.Relationship{Ancestor: 0, Descendant: 0, Depth: 0}}

    for i := 1; i < N; i++ {
        // Create a place for entry #i, making it the child of a random entry j<i
        err := ct.AddChild(forum.Child{Parent: rand.Int63n(int64(i)), Child: int64(i)})
        if err != nil {
            fmt.Println(err)

            return
        }
    }

    fmt.Printf("%v\n", ct)
    fmt.Println("Success")
}

func makeEntries() {
    // Make 10 entries based on a skeleton; the Id's will be appropriately distinct.
    skeleton := forum.Entry{ Id: 1, Title: "Hello, world", Body: "This is a body.", Created: time.Now(), AuthorId: 1}
    var entries = make([]forum.Entry,N)
    for i := 0; i < N; i++ {
        entries[i], entries[i].Id, entries[i].Created = skeleton, int64(i+1), time.Now()
    }

    fmt.Printf("Created new entries with the following data:\n%#v\n",entries)
}<commit_msg>Testing closuretree.<commit_after>package main

import (
    "fmt"
    "time"
    "github.com/carbocation/forum.git/forum"
    "math/rand"
)

var N int = 4

func main() {
    //makeEntries()
    //closureTable()
    closureTree()
}

func closureTree() {
    ct := buildClosureTable(N)

    //Now we have a closure table. Feed it to the ClosureTree to build a recursive structure.
    tree := forum.ClosureTree{}
    err := tree.Populate(ct)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("%v\n", ct)
    fmt.Printf("%v\n", tree)
}

func closureTable() {
    // Create the closure table with a single progenitor
    ct := buildClosureTable(N)

    fmt.Printf("%v\n", ct)
}

func makeEntries() {
    // Make 10 entries based on a skeleton; the Id's will be appropriately distinct.
    skeleton := forum.Entry{ Id: 1, Title: "Hello, world", Body: "This is a body.", Created: time.Now(), AuthorId: 1}
    var entries = make([]forum.Entry,N)
    for i := 0; i < N; i++ {
        entries[i], entries[i].Id, entries[i].Created = skeleton, int64(i+1), time.Now()
    }

    fmt.Printf("Created new entries with the following data:\n%#v\n",entries)
}

func buildClosureTable(N int) forum.ClosureTable {
    // Create the closure table with a single progenitor
    ct := forum.ClosureTable{forum.Relationship{Ancestor: 0, Descendant: 0, Depth: 0}}

    for i := 1; i < N; i++ {
        // Create a place for entry #i, making it the child of a random entry j<i
        err := ct.AddChild(forum.Child{Parent: rand.Int63n(int64(i)), Child: int64(i)})
        if err != nil {
            fmt.Println(err)
            break
        }
    }

    return ct
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"runtime"

	"github.com/docopt/docopt-go"
	"github.com/fatih/color"
	"github.com/hashicorp/logutils"
)

//go:generate echo Let's go bananas!
//go:generate go run misc/gen_schemas.go
//go:generate ./misc/gen_meta.sh

const (
	binName    = "monkey"
	binTitle   = binName + "/" + binVersion
	envAPIKey  = "FUZZYMONKEY_API_KEY"
	githubSlug = "FuzzyMonkeyCo/" + binName
)

var (
	clientUtils = &http.Client{}

	colorERR *color.Color
	colorWRN *color.Color
	colorNFO *color.Color
)

func init() {
	log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.LUTC)

	colorERR = color.New(color.FgRed)
	colorWRN = color.New(color.FgYellow)
	colorNFO = color.New(color.FgWhite, color.Bold)

	loadSchemas()
}

func main() {
	os.Exit(actualMain())
}

func usage() (docopt.Opts, error) {
	usage := binName + "\tv" + binVersion + "\t" + binDescribe + "\t" + runtime.Version() + `

Usage:
  ` + binName + ` [-vvv] fuzz
  ` + binName + ` [-vvv] lint
  ` + binName + ` [-vvv] exec (start | reset | stop)
  ` + binName + ` [-vvv] -h | --help
  ` + binName + ` [-vvv] -U | --update
  ` + binName + ` [-vvv] -V | --version

Options:
  -v, -vv, -vvv  Debug verbosity level
  -h, --help     Show this screen
  -U, --update   Ensures ` + binName + ` is latest
  -V, --version  Show version

Try:
     export FUZZYMONKEY_API_KEY=42
  ` + binName + ` --update
  ` + binName + ` fuzz`

	parser := &docopt.Parser{
		HelpHandler:  docopt.PrintHelpOnly,
		OptionsFirst: true,
	}
	return parser.ParseArgs(usage, os.Args[1:], binTitle)
}

func actualMain() int {
	args, err := usage()
	if err != nil {
		// Usage shown: bad args
		return 1
	}
	if len(args) == 0 {
		// Help or version shown
		return 0
	}

	if err := makePwdID(); err != nil {
		return retryOrReport()
	}

	logCatchall, err := os.OpenFile(logID(), os.O_WRONLY|os.O_CREATE, 0640)
	if err != nil {
		log.Println(err)
		return retryOrReport()
	}
	defer logCatchall.Close()
	logFiltered := &logutils.LevelFilter{
		Levels:   []logutils.LogLevel{"DBG", "NFO", "ERR", "NOP"},
		MinLevel: logLevel(args["-v"].(int)),
		Writer:   os.Stderr,
	}
	log.SetOutput(io.MultiWriter(logCatchall, logFiltered))
	log.Println("[ERR] (not an error)", binTitle, logID(), args)

	if args["--update"].(bool) {
		return doUpdate()
	}

	yml, err := readYML()
	if err != nil {
		return retryOrReport()
	}
	cfg, err := newCfg(yml)
	if err != nil || cfg == nil {
		return retryOrReport()
	}

	if args["exec"].(bool) {
		switch {
		case args["start"].(bool):
			return doExec(cfg, kindStart)
		case args["reset"].(bool):
			return doExec(cfg, kindReset)
		}
		return doExec(cfg, kindStop)
	}

	apiKey := os.Getenv(envAPIKey)
	// Always lint before fuzzing
	validSpec, err := lintDocs(cfg, apiKey)
	if err != nil {
		return 2
	}
	log.Println("[NFO] No validation errors found.")
	colorNFO.Println("No validation errors found.")
	if args["lint"].(bool) {
		return 0
	}

	return doFuzz(cfg, apiKey, validSpec)
}

func ensureDeleted(path string) {
	if err := os.Remove(path); err != nil && os.IsExist(err) {
		fmt.Println(err)
		log.Panic("[ERR] ", err)
	}
}

func logLevel(verbosity int) logutils.LogLevel {
	lvl := map[int]string{
		0: "NOP",
		1: "ERR",
		2: "NFO",
		3: "DBG",
	}[verbosity]
	return logutils.LogLevel(lvl)
}

func doUpdate() int {
	latest, err := peekLatestRelease()
	if err != nil {
		return retryOrReport()
	}

	// assumes not v-prefixed
	// assumes never patching old-minor releases
	if latest != binVersion {
		fmt.Printf("A newer version of %s is out: %s (you have %s)\n",
			binName, latest, binVersion)
		if err := replaceCurrentRelease(latest); err != nil {
			fmt.Println("The update failed 🙈 please try again")
			return 3
		}
	}
	return 0
}

func doExec(cfg *ymlCfg, kind cmdKind) int {
	if _, err := os.Stat(shell()); os.IsNotExist(err) {
		log.Printf("%s is required\n", shell())
		return 5
	}
	if err := snapEnv(envID()); err != nil {
		return retryOrReport()
	}

	if cmdRep := executeScript(cfg, kind); cmdRep.Failed {
		return 7
	}
	return 0
}

func doFuzz(cfg *ymlCfg, apiKey string, spec []byte) int {
	if _, err := os.Stat(shell()); os.IsNotExist(err) {
		log.Printf("%s is required\n", shell())
		return 5
	}

	if err := snapEnv(envID()); err != nil {
		return retryOrReport()
	}

	if apiKey == "" {
		log.Printf("$%s is unset\n", envAPIKey)
		return 4
	}

	cmd, err := newFuzz(cfg, apiKey, spec)
	if err != nil {
		return retryOrReportThenCleanup(cfg, err)
	}

	for {
		if cmd.Kind() == kindDone {
			maybePostStop(cfg)
			ensureDeleted(envID())
			return fuzzOutcome(cmd.(*doneCmd))
		}

		if cmd, err = next(cfg, cmd); err != nil {
			return retryOrReportThenCleanup(cfg, err)
		}
	}
}

func retryOrReportThenCleanup(cfg *ymlCfg, err error) int {
	defer maybePostStop(cfg)
	if hadExecError {
		return 7
	}
	return retryOrReport()
}

func retryOrReport() int {
	issues := "https://github.com/" + githubSlug + "/issues"
	email := "ook@fuzzymonkey.co"
	fmt.Println("\nLooks like something went wrong... Maybe try again with -v?")
	fmt.Printf("\nYou may want to take a look at %s\n", logID())
	fmt.Printf("or come by %s\n", issues)
	fmt.Printf("or drop us a line at %s\n", email)
	fmt.Println("\nThank you for your patience & sorry about this :)")
	return 1
}
<commit_msg>recook: also make vvv global<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"runtime"

	"github.com/docopt/docopt-go"
	"github.com/fatih/color"
	"github.com/hashicorp/logutils"
)

//go:generate echo Let's go bananas!
//go:generate go run misc/gen_schemas.go
//go:generate ./misc/gen_meta.sh

const (
	binName    = "monkey"
	binTitle   = binName + "/" + binVersion
	envAPIKey  = "FUZZYMONKEY_API_KEY"
	githubSlug = "FuzzyMonkeyCo/" + binName
)

var (
	clientUtils = &http.Client{}

	colorERR *color.Color
	colorWRN *color.Color
	colorNFO *color.Color
)

func init() {
	log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.LUTC)

	colorERR = color.New(color.FgRed)
	colorWRN = color.New(color.FgYellow)
	colorNFO = color.New(color.FgWhite, color.Bold)

	loadSchemas()
}

func main() {
	os.Exit(actualMain())
}

func usage() (docopt.Opts, error) {
	usage := binName + "\tv" + binVersion + "\t" + binDescribe + "\t" + runtime.Version() + `

Usage:
  ` + binName + ` [-vvv] init [--with-magic]
  ` + binName + ` [-vvv] login [--user user]
  ` + binName + ` [-vvv] fuzz [N] [--tag tag]
  ` + binName + ` [-vvv] shrink
  ` + binName + ` [-vvv] lint
  ` + binName + ` [-vvv] exec (start | reset | stop)
  ` + binName + ` [-vvv] -h | --help
  ` + binName + ` [-vvv]      --update
  ` + binName + ` [-vvv] -V | --version

Options:
  -v, -vv, -vvv  Debug verbosity level
  -h, --help     Show this screen
  -U, --update   Ensures ` + binName + ` is latest
  -V, --version  Show version
  -u, --user     User to login as
  --with-magic   Auto fill in schemas from random API calls

Try:
     export FUZZYMONKEY_API_KEY=42
  ` + binName + ` --update
  ` + binName + ` init --with-magic
  ` + binName + ` fuzz`

	parser := &docopt.Parser{
		HelpHandler:  docopt.PrintHelpOnly,
		OptionsFirst: true,
	}
	return parser.ParseArgs(usage, os.Args[1:], binTitle)
}

func actualMain() int {
	args, err := usage()
	if err != nil {
		// Usage shown: bad args
		return 1
	}
	if len(args) == 0 {
		// Help or version shown
		return 0
	}

	if err := makePwdID(); err != nil {
		return retryOrReport()
	}

	logCatchall, err := os.OpenFile(logID(), os.O_WRONLY|os.O_CREATE, 0640)
	if err != nil {
		log.Println(err)
		return retryOrReport()
	}
	defer logCatchall.Close()
	logFiltered := &logutils.LevelFilter{
		Levels:   []logutils.LogLevel{"DBG", "NFO", "ERR", "NOP"},
		MinLevel: logLevel(args["-v"].(int)),
		Writer:   os.Stderr,
	}
	log.SetOutput(io.MultiWriter(logCatchall, logFiltered))
	log.Println("[ERR] (not an error)", binTitle, logID(), args)

	if args["--update"].(bool) {
		return doUpdate()
	}

	yml, err := readYML()
	if err != nil {
		return retryOrReport()
	}
	cfg, err := newCfg(yml)
	if err != nil || cfg == nil {
		return retryOrReport()
	}

	if args["exec"].(bool) {
		switch {
		case args["start"].(bool):
			return doExec(cfg, kindStart)
		case args["reset"].(bool):
			return doExec(cfg, kindReset)
		}
		return doExec(cfg, kindStop)
	}

	apiKey := os.Getenv(envAPIKey)
	// Always lint before fuzzing
	validSpec, err := lintDocs(cfg, apiKey)
	if err != nil {
		return 2
	}
	log.Println("[NFO] No validation errors found.")
	colorNFO.Println("No validation errors found.")
	if args["lint"].(bool) {
		return 0
	}

	return doFuzz(cfg, apiKey, validSpec)
}

func ensureDeleted(path string) {
	if err := os.Remove(path); err != nil && os.IsExist(err) {
		fmt.Println(err)
		log.Panic("[ERR] ", err)
	}
}

func logLevel(verbosity int) logutils.LogLevel {
	lvl := map[int]string{
		0: "NOP",
		1: "ERR",
		2: "NFO",
		3: "DBG",
	}[verbosity]
	return logutils.LogLevel(lvl)
}

func doUpdate() int {
	latest, err := peekLatestRelease()
	if err != nil {
		return retryOrReport()
	}

	// assumes not v-prefixed
	// assumes never patching old-minor releases
	if latest != binVersion {
		fmt.Printf("A newer version of %s is out: %s (you have %s)\n",
			binName, latest, binVersion)
		if err := replaceCurrentRelease(latest); err != nil {
			fmt.Println("The update failed 🙈 please try again")
			return 3
		}
	}
	return 0
}

func doExec(cfg *ymlCfg, kind cmdKind) int {
	if _, err := os.Stat(shell()); os.IsNotExist(err) {
		log.Printf("%s is required\n", shell())
		return 5
	}
	if err := snapEnv(envID()); err != nil {
		return retryOrReport()
	}

	if cmdRep := executeScript(cfg, kind); cmdRep.Failed {
		return 7
	}
	return 0
}

func doFuzz(cfg *ymlCfg, apiKey string, spec []byte) int {
	if _, err := os.Stat(shell()); os.IsNotExist(err) {
		log.Printf("%s is required\n", shell())
		return 5
	}

	if err := snapEnv(envID()); err != nil {
		return retryOrReport()
	}

	if apiKey == "" {
		log.Printf("$%s is unset\n", envAPIKey)
		return 4
	}

	cmd, err := newFuzz(cfg, apiKey, spec)
	if err != nil {
		return retryOrReportThenCleanup(cfg, err)
	}

	for {
		if cmd.Kind() == kindDone {
			maybePostStop(cfg)
			ensureDeleted(envID())
			return fuzzOutcome(cmd.(*doneCmd))
		}

		if cmd, err = next(cfg, cmd); err != nil {
			return retryOrReportThenCleanup(cfg, err)
		}
	}
}

func retryOrReportThenCleanup(cfg *ymlCfg, err error) int {
	defer maybePostStop(cfg)
	if hadExecError {
		return 7
	}
	return retryOrReport()
}

func retryOrReport() int {
	issues := "https://github.com/" + githubSlug + "/issues"
	email := "ook@fuzzymonkey.co"
	fmt.Println("\nLooks like something went wrong... Maybe try again with -v?")
	fmt.Printf("\nYou may want to take a look at %s\n", logID())
	fmt.Printf("or come by %s\n", issues)
	fmt.Printf("or drop us a line at %s\n", email)
	fmt.Println("\nThank you for your patience & sorry about this :)")
	return 1
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/vampirewalk/slackbot/Godeps/_workspace/src/github.com/nlopes/slack"
	"log"
	"net/url"
	"os"
	"strings"
)

func main() {
	token := os.Getenv("SLACK_TOKEN")
	if token == "" {
		log.Fatal("SLACK_TOKEN is required")
	}

	api := slack.New(token)
	bot := Bot{}

	rtm := api.NewRTM()
	go rtm.ManageConnection()

Loop:
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.ConnectedEvent:
				bot.SetMe(ev.Info.User)

			case *slack.MessageEvent:
				// if msg contains github url
				repoURL := bot.ExtractGithubRepoURL(ev.Msg.Text)
				log.Printf(repoURL)
				if repoURL != "" && !bot.IsMe(ev.Msg.User) {
					log.Printf("Received message \"%s\" from %s", ev.Msg.Text, ev.Msg.User)
					u, err := url.Parse(repoURL)
					if err != nil {
						log.Printf("URL error")
						rtm.SendMessage(rtm.NewOutgoingMessage("URL error", ev.Channel))
					}
					results := strings.Split(u.Path, "/")
					state, err := bot.GetRepoState(results[1], results[2])
					if err != nil {
						log.Printf("Parse error")
						rtm.SendMessage(rtm.NewOutgoingMessage("Parse error", ev.Channel))
					}
					report := bot.ReportRepoState(state)
					log.Printf(report)
					rtm.SendMessage(rtm.NewOutgoingMessage(report, ev.Channel))
				}

			case *slack.RTMError:
				log.Printf("Error: %s\n", ev.Error())

			case *slack.InvalidAuthEvent:
				log.Fatal("Invalid credentials")
				break Loop
			}
		}
	}
}
<commit_msg>Join all channels at first<commit_after>package main

import (
	"github.com/vampirewalk/slackbot/Godeps/_workspace/src/github.com/nlopes/slack"
	"log"
	"net/url"
	"os"
	"strings"
)

func main() {
	token := os.Getenv("SLACK_TOKEN")
	if token == "" {
		log.Fatal("SLACK_TOKEN is required")
	}

	api := slack.New(token)
	channels, err := api.GetChannels(true)
	if err != nil {
		log.Fatal("Failed to get channels")
	}
	for _, channel := range channels {
		if !channel.IsMember {
			api.JoinChannel(channel.ID)
		}
	}
	bot := Bot{}

	rtm := api.NewRTM()
	go rtm.ManageConnection()

Loop:
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.ConnectedEvent:
				bot.SetMe(ev.Info.User)

			case *slack.MessageEvent:
				// if msg contains github url
				repoURL := bot.ExtractGithubRepoURL(ev.Msg.Text)
				log.Printf(repoURL)
				if repoURL != "" && !bot.IsMe(ev.Msg.User) {
					log.Printf("Received message \"%s\" from %s", ev.Msg.Text, ev.Msg.User)
					u, err := url.Parse(repoURL)
					if err != nil {
						log.Printf("URL error")
						rtm.SendMessage(rtm.NewOutgoingMessage("URL error", ev.Channel))
					}
					results := strings.Split(u.Path, "/")
					state, err := bot.GetRepoState(results[1], results[2])
					if err != nil {
						log.Printf("Parse error")
						rtm.SendMessage(rtm.NewOutgoingMessage("Parse error", ev.Channel))
					}
					report := bot.ReportRepoState(state)
					log.Printf(report)
					rtm.SendMessage(rtm.NewOutgoingMessage(report, ev.Channel))
				}

			case *slack.RTMError:
				log.Printf("Error: %s\n", ev.Error())

			case *slack.InvalidAuthEvent:
				log.Fatal("Invalid credentials")
				break Loop
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"io/ioutil"
	"encoding/json"
	"strings"
	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/go-redis/redis"
	"strconv"
	// "time"
)

type device struct {
	// Gps_num float32 `json:"gps_num"`
	// App string `json:"app"`
	// Gps_alt float32 `json:"gps_alt"`
	// Fmt_opt int `json:"fmt_opt"`
	// Device string `json:"device"`
	// S_d2 float32 `json:"s_d2"`
	S_d0 float32 `json:"s_d0"`
	// S_d1 float32 `json:"s_d1"`
	S_h0 float32 `json:"s_h0"`
	SiteName string `json:"SiteName"`
	// Gps_fix float32 `json:"gps_fix"`
	// Ver_app string `json:"ver_app"`
	Gps_lat float32 `json:"gps_lat"`
	S_t0 float32 `json:"s_t0"`
	Timestamp string `json:"timestamp"`
	Gps_lon float32 `json:"gps_lon"`
	// Date string `json:"date"`
	// Tick float32 `json:"tick"`
	Device_id string `json:"device_id"`
	// S_1 float32 `json:"s_1"`
	// S_0 float32 `json:"s_0"`
	// S_3 float32 `json:"s_3"`
	// S_2 float32 `json:"s_2"`
	// Ver_format string `json:"ver_format"`
	// Time string `json:"time"`
}

type airbox struct {
	Source string `json:"source"`
	Feeds []device `json:"feeds"`
	Version string `json:"version"`
	Num_of_records int `json:"num_of_records"`
}

var bot *linebot.Client
var airbox_json airbox
var	client=redis.NewClient(&redis.Options{
		Addr:"hipposerver.ddns.net:6379",
		Password:"",
		DB:0,
	})

func main() {
	url := "https://data.lass-net.org/data/last-all-airbox.json"
	req, _ := http.NewRequest("GET", url, nil)
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	errs := json.Unmarshal(body, &airbox_json)
	if errs != nil {
		fmt.Println(errs)
	}
	// pushmessage()
	// fmt.Println(airbox_json)
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
	_,_=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()

}
// func pushmessage(){
// 	_,err:=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
// 	if err!=nil{
// 		panic(err)
// 	}
// }
func callbackHandler(w http.ResponseWriter, r *http.Request) {
	// t:=time.Now()
	// _, min, _:=t.Clock()
	// if min==0{
	// 	pushmessage()
	// }
	// _,_=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				var txtmessage string
				inText := strings.ToLower(message.Text)
				if strings.Contains(inText,"訂閱"){
					userID:=event.Source.UserID
					// pong, _ := client.Ping().Result()
					// txtmessage=pong
					for i:=0; i<len(airbox_json.Feeds); i++ {
						if strings.Contains(inText,strings.ToLower(airbox_json.Feeds[i].Device_id)) {
							val, err:=client.Get(airbox_json.Feeds[i].Device_id).Result()
							if err!=nil{
								client.Set(airbox_json.Feeds[i].Device_id,userID,0)
								txtmessage="訂閱成功!"
								break
							}
							stringSlice:=strings.Split(val,",")
							if stringInSlice(userID,stringSlice){
								txtmessage="您已訂閱過此ID!"
								break
							} else{
								val=val+","+userID
								client.Set(airbox_json.Feeds[i].Device_id,val,0)
								txtmessage="訂閱成功!"
								break
							}
						}
					}
				} else{
					for i:=0; i<len(airbox_json.Feeds); i++ {
						if strings.Contains(inText,strings.ToLower(airbox_json.Feeds[i].Device_id)) {
							txtmessage="Device_id: "+airbox_json.Feeds[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+airbox_json.Feeds[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(airbox_json.Feeds[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(airbox_json.Feeds[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+airbox_json.Feeds[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(airbox_json.Feeds[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(airbox_json.Feeds[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(airbox_json.Feeds[i].S_t0),'f',0,64)
							break
						}
					}
				}
				if len(txtmessage)==0{
					txtmessage="Sorry! No this device ID, please check again."
				}
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(txtmessage)).Do(); err != nil {
					log.Print(err)
				}
			}
		}
	}
}

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}
<commit_msg>test push<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"io/ioutil"
	"encoding/json"
	"strings"
	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/go-redis/redis"
	"strconv"
	// "time"
)

type device struct {
	// Gps_num float32 `json:"gps_num"`
	// App string `json:"app"`
	// Gps_alt float32 `json:"gps_alt"`
	// Fmt_opt int `json:"fmt_opt"`
	// Device string `json:"device"`
	// S_d2 float32 `json:"s_d2"`
	S_d0 float32 `json:"s_d0"`
	// S_d1 float32 `json:"s_d1"`
	S_h0 float32 `json:"s_h0"`
	SiteName string `json:"SiteName"`
	// Gps_fix float32 `json:"gps_fix"`
	// Ver_app string `json:"ver_app"`
	Gps_lat float32 `json:"gps_lat"`
	S_t0 float32 `json:"s_t0"`
	Timestamp string `json:"timestamp"`
	Gps_lon float32 `json:"gps_lon"`
	// Date string `json:"date"`
	// Tick float32 `json:"tick"`
	Device_id string `json:"device_id"`
	// S_1 float32 `json:"s_1"`
	// S_0 float32 `json:"s_0"`
	// S_3 float32 `json:"s_3"`
	// S_2 float32 `json:"s_2"`
	// Ver_format string `json:"ver_format"`
	// Time string `json:"time"`
}

type airbox struct {
	Source string `json:"source"`
	Feeds []device `json:"feeds"`
	Version string `json:"version"`
	Num_of_records int `json:"num_of_records"`
}

var bot *linebot.Client
var airbox_json airbox
var	client=redis.NewClient(&redis.Options{
		Addr:"hipposerver.ddns.net:6379",
		Password:"",
		DB:0,
	})

func main() {
	url := "https://data.lass-net.org/data/last-all-airbox.json"
	req, _ := http.NewRequest("GET", url, nil)
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	errs := json.Unmarshal(body, &airbox_json)
	if errs != nil {
		fmt.Println(errs)
	}
	// pushmessage()
	// fmt.Println(airbox_json)
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
		_,_=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)

}
// func pushmessage(){
// 	_,err:=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
// 	if err!=nil{
// 		panic(err)
// 	}
// }
func callbackHandler(w http.ResponseWriter, r *http.Request) {
	// t:=time.Now()
	// _, min, _:=t.Clock()
	// if min==0{
	// 	pushmessage()
	// }
	// _,_=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				var txtmessage string
				inText := strings.ToLower(message.Text)
				if strings.Contains(inText,"訂閱"){
					userID:=event.Source.UserID
					// pong, _ := client.Ping().Result()
					// txtmessage=pong
					for i:=0; i<len(airbox_json.Feeds); i++ {
						if strings.Contains(inText,strings.ToLower(airbox_json.Feeds[i].Device_id)) {
							val, err:=client.Get(airbox_json.Feeds[i].Device_id).Result()
							if err!=nil{
								client.Set(airbox_json.Feeds[i].Device_id,userID,0)
								txtmessage="訂閱成功!"
								break
							}
							stringSlice:=strings.Split(val,",")
							if stringInSlice(userID,stringSlice){
								txtmessage="您已訂閱過此ID!"
								break
							} else{
								val=val+","+userID
								client.Set(airbox_json.Feeds[i].Device_id,val,0)
								txtmessage="訂閱成功!"
								break
							}
						}
					}
				} else{
					for i:=0; i<len(airbox_json.Feeds); i++ {
						if strings.Contains(inText,strings.ToLower(airbox_json.Feeds[i].Device_id)) {
							txtmessage="Device_id: "+airbox_json.Feeds[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+airbox_json.Feeds[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(airbox_json.Feeds[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(airbox_json.Feeds[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+airbox_json.Feeds[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(airbox_json.Feeds[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(airbox_json.Feeds[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(airbox_json.Feeds[i].S_t0),'f',0,64)
							break
						}
					}
				}
				if len(txtmessage)==0{
					txtmessage="Sorry! No this device ID, please check again."
				}
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(txtmessage)).Do(); err != nil {
					log.Print(err)
				}
			}
		}
	}
}

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}
<|endoftext|>