<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/adamdecaf/cert-manage/cmd"
)

const (
	version = "0.0.1-dev"
)

var (
	fs = flag.NewFlagSet("flags", flag.ExitOnError)

	// Commands
	backup    = fs.Bool("backup", false, "Make a backup")
	list      = fs.Bool("list", false, "List certificates (by default on the system, see -app)")
	restore   = fs.Bool("restore", false, "Restore from a given backup, if it exists")
	whitelist = fs.String("whitelist", "", "Filter certificates according to the provided whitelist")

	// Filters
	app = fs.String("app", "", "Specify an application (see -list)")

	// Output
	// -count: Just output the count of trusted certs
	format = fs.String("format", "table", "Specify the output format (options: raw, table)")
)

func main() {
	fs.Parse(os.Args[1:])

	// Perform a restore
	// Note: This always needs to happen before -whitelist and before -backup
	if restore != nil && *restore {
		err := appChoice(app,
			func(a string) error {
				return cmd.RestoreForApp(a)
			},
			func() error {
				return cmd.RestoreForPlatform()
			})
		exit("Restore completed successfully", err)
	}

	// Take a backup
	// Note: This always needs to be done first
	if backup != nil && *backup {
		err := appChoice(app,
			func(a string) error {
				return cmd.BackupForApp(a)
			},
			func() error {
				return cmd.BackupForPlatform()
			})
		exit("Backup completed successfully", err)
	}

	// Whitelist
	wh := strings.TrimSpace(*whitelist)
	if whitelist != nil && wh != "" {
		err := appChoice(app,
			func(a string) error {
				return cmd.WhitelistForApp(a, *whitelist, *format)
			},
			func() error {
				return cmd.WhitelistForPlatform(*whitelist, *format)
			})
		exit("Whitelist completed successfully", err)
	}

	// List
	if list != nil && *list {
		err := appChoice(app,
			func(a string) error {
				return cmd.ListCertsForApp(*app, *format)
			},
			func() error {
				return cmd.ListCertsForPlatform(*format)
			})
		exit("", err)
	}
}

type fn func() error
type appfn func(string) error

// appChoice decides between `appfn` and `fn` given the presence of `app`
// being non-nil and a non-empty string.
func appChoice(app *string, appfn appfn, fn fn) error {
	if app != nil && *app != "" {
		return appfn(*app)
	}
	return fn()
}

func exit(msg string, err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if msg != "" {
		fmt.Println(msg)
	}
}
<commit_msg>comment cleanup<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/adamdecaf/cert-manage/cmd"
)

const (
	version = "0.0.1-dev"
)

var (
	fs = flag.NewFlagSet("flags", flag.ExitOnError)

	// Commands
	backup    = fs.Bool("backup", false, "Make a backup")
	list      = fs.Bool("list", false, "List certificates (by default on the system, see -app)")
	restore   = fs.Bool("restore", false, "Restore from a given backup, if it exists")
	whitelist = fs.String("whitelist", "", "Filter certificates according to the provided whitelist")

	// Filters
	app = fs.String("app", "", "Specify an application (see -list)")

	// Output
	// -count: Just output the count of trusted certs
	format = fs.String("format", "table", "Specify the output format (options: raw, table)")
)

func main() {
	fs.Parse(os.Args[1:])

	// Perform a restore
	// Note: This always needs to happen before -whitelist and before -backup
	if restore != nil && *restore {
		err := appChoice(app,
			func(a string) error {
				return cmd.RestoreForApp(a)
			},
			func() error {
				return cmd.RestoreForPlatform()
			})
		exit("Restore completed successfully", err)
	}

	// Take a backup
	// Note: This always needs to be done before -whitelist
	if backup != nil && *backup {
		err := appChoice(app,
			func(a string) error {
				return cmd.BackupForApp(a)
			},
			func() error {
				return cmd.BackupForPlatform()
			})
		exit("Backup completed successfully", err)
	}

	// Whitelist
	wh := strings.TrimSpace(*whitelist)
	if whitelist != nil && wh != "" {
		err := appChoice(app,
			func(a string) error {
				return cmd.WhitelistForApp(a, *whitelist, *format)
			},
			func() error {
				return cmd.WhitelistForPlatform(*whitelist, *format)
			})
		exit("Whitelist completed successfully", err)
	}

	// List
	if list != nil && *list {
		err := appChoice(app,
			func(a string) error {
				return cmd.ListCertsForApp(*app, *format)
			},
			func() error {
				return cmd.ListCertsForPlatform(*format)
			})
		exit("", err)
	}
}

type fn func() error
type appfn func(string) error

// appChoice decides between `appfn` and `fn` given the presence of `app`
// being non-nil and a non-empty string.
func appChoice(app *string, appfn appfn, fn fn) error {
	if app != nil && *app != "" {
		return appfn(*app)
	}
	return fn()
}

func exit(msg string, err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if msg != "" {
		fmt.Println(msg)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func handleBurndown(res http.ResponseWriter, req *http.Request) {
	vars, err := ioutil.ReadAll(req.Body)
	if err != nil {
		panic("Cannot parse JSON from request body.")
	}

	trello := NewTrello(vars)
	burndown := NewBurndown(trello, vars)
	burndown.calculate()

	res.Header().Set("Content-Type", "application/json")
	res.Header().Set("Access-Control-Allow-Origin", "*")
	data, error := json.Marshal(burndown)
	if error != nil {
		log.Println(error.Error())
		http.Error(res, error.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(res, string(data))
}

func main() {
	router := mux.NewRouter()
	router.Handle("/", http.FileServer(http.Dir("static"))).Methods("GET")
	router.HandleFunc("/burndown", handleBurndown).Methods("POST")
	http.ListenAndServe(":8080", router)
}
<commit_msg>use static dir<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func handleBurndown(res http.ResponseWriter, req *http.Request) {
	vars, err := ioutil.ReadAll(req.Body)
	if err != nil {
		panic("Cannot parse JSON from request body.")
	}

	trello := NewTrello(vars)
	burndown := NewBurndown(trello, vars)
	burndown.calculate()

	res.Header().Set("Content-Type", "application/json")
	res.Header().Set("Access-Control-Allow-Origin", "*")
	data, error := json.Marshal(burndown)
	if error != nil {
		log.Println(error.Error())
		http.Error(res, error.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(res, string(data))
}

func main() {
	router := mux.NewRouter()
	staticHandler := http.StripPrefix("/static/", http.FileServer(http.Dir("./static/")))
	router.PathPrefix("/static/").Handler(staticHandler).Methods("GET")
	http.Handle("/", router)
	router.HandleFunc("/burndown", handleBurndown).Methods("POST")
	http.ListenAndServe(":8080", router)
}
<|endoftext|>


<commit_before>package jimmifyweb

import "os/exec"

//BuildSite build the handlebars templates
func BuildSite() {
	exec.Command("npm install")
	exec.Command("grunt build")
}
<commit_msg>finished go module to build automatically<commit_after>package jimmifyweb

import (
	"errors"
	"os"
	"os/exec"
)

//BuildSite build the handlebars templates
func BuildSite() (string, error) {
	working, _ := os.Getwd() // Get os working directory
	//get path to static site
	path := os.Getenv("GOPATH") + "/src/github.com/jimmified/jimmify-web/"
	os.Chdir(path) // change dir to static site
	//run npm
	npm := exec.Command("npm", "install")
	err := npm.Run()
	if err != nil {
		os.Chdir(working) // change dir back to working directory
		return path, errors.New("Jimmify Web: could not run npm install")
	}
	grunt := exec.Command("grunt", "build")
	err = grunt.Run()
	if err != nil {
		os.Chdir(working) // change dir back to working directory
		return path, errors.New("Jimmify Web: could not run grunt build")
	}
	os.Chdir(working) // change dir back to working directory
	return path, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/reportportal/commons-go/commons"
	"github.com/reportportal/commons-go/conf"
	"github.com/reportportal/commons-go/server"
	"github.com/unrolled/secure"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	currDir, e := os.Getwd()
	if nil != e {
		log.Fatalf("Cannot get workdir: %s", e.Error())
	}
	cfg := conf.EmptyConfig()
	rpConf := struct {
		Cfg         *conf.ServerConfig
		StaticsPath string `env:"RP_STATICS_PATH"`
	}{
		Cfg:         cfg,
		StaticsPath: currDir,
	}
	err := conf.LoadConfig(&rpConf)
	if nil != err {
		log.Fatalf("Cannot log app config")
	}
	info := commons.GetBuildInfo()
	info.Name = "Service UI"
	srv := server.New(rpConf.Cfg, info)
	configureRouter(srv, rpConf)
	srv.StartServer()
}

func configureRouter(srv *server.RpServer, rpConf struct {
	Cfg         *conf.ServerConfig
	StaticsPath string `env:"RP_STATICS_PATH"`
}) {
	srv.WithRouter(func(router *chi.Mux) {
		//apply compression
		router.Use(middleware.DefaultCompress)
		router.Use(middleware.Logger)
		//content security policy
		csp := map[string][]string{
			"default-src": {"'self'", "data:", "'unsafe-inline'", "*.uservoice.com"},
			"script-src": {
				"'self'",
				"'unsafe-inline'",
				"'unsafe-eval'",
				"status.reportportal.io",
				"www.google-analytics.com",
				"stats.g.doubleclick.net",
				"*.saucelabs.com",
				"*.epam.com",
				"*.uservoice.com",
				"*.rawgit.com",
			},
			"worker-src":     {"'self'", "blob:"},
			"font-src":       {"'self'", "data:", "fonts.googleapis.com", "fonts.gstatic.com", "*.rawgit.com"},
			"style-src-elem": {"'self'", "data:", "'unsafe-inline'", "*.googleapis.com", "*.rawgit.com"},
			"media-src":      {"'self'", "*.saucelabs.com"},
			"img-src":        {"*", "'self'", "data:", "blob:"},
			"object-src":     {"'self'"},
		}
		//apply content security policies
		router.Use(func(next http.Handler) http.Handler {
			return secure.New(secure.Options{
				ContentTypeNosniff:    true,
				BrowserXssFilter:      true,
				ContentSecurityPolicy: buildCSP(csp),
				STSSeconds:            315360000,
				STSIncludeSubdomains:  true,
				STSPreload:            true,
			}).Handler(next)
		})
		err := os.Chdir(rpConf.StaticsPath)
		if nil != err {
			log.Fatalf("Dir %s not found", rpConf.StaticsPath)
		}
		router.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			//trim query params
			ext := filepath.Ext(trimQuery(r.URL.String(), "?"))
			// never cache html
			if "/" == r.URL.String() || ".html" == ext {
				w.Header().Add("Cache-Control", "no-cache")
			}
			http.FileServer(http.Dir(rpConf.StaticsPath)).ServeHTTP(&redirectingRW{ResponseWriter: w, Request: r}, r)
		}))
	})
}

func trimQuery(s string, sep string) string {
	sepIndex := strings.Index(s, sep)
	if -1 != sepIndex {
		return s[:sepIndex]
	}
	return s
}

func buildCSP(csp map[string][]string) string {
	var instr []string
	for k, v := range csp {
		instr = append(instr, k+" "+strings.Join(v, " "))
	}
	return strings.Join(instr, "; ")
}

type redirectingRW struct {
	*http.Request
	http.ResponseWriter
	ignore bool
}

func (hrw *redirectingRW) Header() http.Header {
	return hrw.ResponseWriter.Header()
}

func (hrw *redirectingRW) WriteHeader(status int) {
	var notFoundStatusCode int = 404
	if notFoundStatusCode == status {
		hrw.ignore = true
		http.Redirect(hrw.ResponseWriter, hrw.Request, "/ui/#notfound", http.StatusTemporaryRedirect)
	} else {
		hrw.ResponseWriter.WriteHeader(status)
	}
}

func (hrw *redirectingRW) Write(p []byte) (int, error) {
	if hrw.ignore {
		return len(p), nil
	}
	return hrw.ResponseWriter.Write(p)
}
<commit_msg>Remove magic number from main.go file<commit_after>package main

import (
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/reportportal/commons-go/commons"
	"github.com/reportportal/commons-go/conf"
	"github.com/reportportal/commons-go/server"
	"github.com/unrolled/secure"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	currDir, e := os.Getwd()
	if nil != e {
		log.Fatalf("Cannot get workdir: %s", e.Error())
	}
	cfg := conf.EmptyConfig()
	rpConf := struct {
		Cfg         *conf.ServerConfig
		StaticsPath string `env:"RP_STATICS_PATH"`
	}{
		Cfg:         cfg,
		StaticsPath: currDir,
	}
	err := conf.LoadConfig(&rpConf)
	if nil != err {
		log.Fatalf("Cannot log app config")
	}
	info := commons.GetBuildInfo()
	info.Name = "Service UI"
	srv := server.New(rpConf.Cfg, info)
	configureRouter(srv, rpConf)
	srv.StartServer()
}

func configureRouter(srv *server.RpServer, rpConf struct {
	Cfg         *conf.ServerConfig
	StaticsPath string `env:"RP_STATICS_PATH"`
}) {
	srv.WithRouter(func(router *chi.Mux) {
		//apply compression
		router.Use(middleware.DefaultCompress)
		router.Use(middleware.Logger)
		//content security policy
		csp := map[string][]string{
			"default-src": {"'self'", "data:", "'unsafe-inline'", "*.uservoice.com"},
			"script-src": {
				"'self'",
				"'unsafe-inline'",
				"'unsafe-eval'",
				"status.reportportal.io",
				"www.google-analytics.com",
				"stats.g.doubleclick.net",
				"*.saucelabs.com",
				"*.epam.com",
				"*.uservoice.com",
				"*.rawgit.com",
			},
			"worker-src":     {"'self'", "blob:"},
			"font-src":       {"'self'", "data:", "fonts.googleapis.com", "fonts.gstatic.com", "*.rawgit.com"},
			"style-src-elem": {"'self'", "data:", "'unsafe-inline'", "*.googleapis.com", "*.rawgit.com"},
			"media-src":      {"'self'", "*.saucelabs.com"},
			"img-src":        {"*", "'self'", "data:", "blob:"},
			"object-src":     {"'self'"},
		}
		//apply content security policies
		var STSSeconds int64 = 315360000
		router.Use(func(next http.Handler) http.Handler {
			return secure.New(secure.Options{
				ContentTypeNosniff:    true,
				BrowserXssFilter:      true,
				ContentSecurityPolicy: buildCSP(csp),
				STSSeconds:            STSSeconds,
				STSIncludeSubdomains:  true,
				STSPreload:            true,
			}).Handler(next)
		})
		err := os.Chdir(rpConf.StaticsPath)
		if nil != err {
			log.Fatalf("Dir %s not found", rpConf.StaticsPath)
		}
		router.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			//trim query params
			ext := filepath.Ext(trimQuery(r.URL.String(), "?"))
			// never cache html
			if "/" == r.URL.String() || ".html" == ext {
				w.Header().Add("Cache-Control", "no-cache")
			}
			http.FileServer(http.Dir(rpConf.StaticsPath)).ServeHTTP(&redirectingRW{ResponseWriter: w, Request: r}, r)
		}))
	})
}

func trimQuery(s string, sep string) string {
	sepIndex := strings.Index(s, sep)
	if -1 != sepIndex {
		return s[:sepIndex]
	}
	return s
}

func buildCSP(csp map[string][]string) string {
	var instr []string
	for k, v := range csp {
		instr = append(instr, k+" "+strings.Join(v, " "))
	}
	return strings.Join(instr, "; ")
}

type redirectingRW struct {
	*http.Request
	http.ResponseWriter
	ignore bool
}

func (hrw *redirectingRW) Header() http.Header {
	return hrw.ResponseWriter.Header()
}

func (hrw *redirectingRW) WriteHeader(status int) {
	var notFoundStatusCode int = 404
	if notFoundStatusCode == status {
		hrw.ignore = true
		http.Redirect(hrw.ResponseWriter, hrw.Request, "/ui/#notfound", http.StatusTemporaryRedirect)
	} else {
		hrw.ResponseWriter.WriteHeader(status)
	}
}

func (hrw *redirectingRW) Write(p []byte) (int, error) {
	if hrw.ignore {
		return len(p), nil
	}
	return hrw.ResponseWriter.Write(p)
}
<|endoftext|>


<commit_before>/*
Package relay implments a simple client to report errors using the Mailgun mailing service
*/
package relay

import (
	"encoding/json"
	"errors"
	"net/http"
	//"net/url"
	"os"
	//"time"
)

var ErrBadConfig = errors.New("The config does not contain the necessary information")

// Relay is a client to send error messages with
type Relay struct {
	c    *http.Client
	to   string
	from string
	key  string
}

// Config contains the information used to initialize a Relay
type Config struct {
	To   string `json:"to"`
	From string `json:"from"`
	Key  string `json:"api_key"`
}

// New is used to generate a new Relay. If called with argument nil, it
// reads from config.json
func New(c *Config) (*Relay, error) {
	// make a new relay
	r := &Relay{
		c: &http.Client{},
	}

	// if they gave us a config, use it
	if c != nil {
		r.to = c.To
		r.from = c.From
		r.key = c.Key
	} else { // otherwise read config.json
		infile, err := os.Open("config.json")
		defer infile.Close()
		if err != nil {
			return nil, ErrBadConfig
		}

		dec := json.NewDecoder(infile)

		config := new(Config)
		err = dec.Decode(config)
		if err != nil {
			return nil, ErrBadConfig
		}

		r.to = config.To
		r.from = config.From
		r.key = config.Key
	}

	// ensure all necessary fields are set
	if r.to == "" || r.from == "" || r.key == "" {
		return nil, ErrBadConfig
	} else {
		return r, nil
	}
}
<commit_msg>Added the send method, ready to begin testing<commit_after>/*
Package relay implments a simple client to report errors using the Mailgun mailing service
*/
package relay

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

var ErrBadConfig = errors.New("the config does not contain the necessary information")
var ErrBadRequest = errors.New("the request generated by relay is invalid")
var ErrMailgunDown = errors.New("something seems to be wrong with Mailgun servers")
var ErrUnknown = errors.New("Something undefined happened")

// Relay is a client to send error messages with
type Relay struct {
	c      *http.Client
	domain string
	to     string
	from   string
	key    string
}

// Config contains the information used to initialize a Relay
type Config struct {
	Domain string `json:"domain"`
	To     string `json:"to"`
	From   string `json:"from"`
	Key    string `json:"api_key"`
}

// New is used to generate a new Relay. If called with argument nil, it
// reads from config.json
func New(c *Config) (*Relay, error) {
	// make a new relay
	r := &Relay{
		c: &http.Client{},
	}

	// if they gave us a config, use it
	if c != nil {
		r.to = c.To
		r.from = c.From
		r.key = c.Key
		r.domain = c.Domain
	} else { // otherwise read config.json
		infile, err := os.Open("config.json")
		defer infile.Close()
		if err != nil {
			return nil, ErrBadConfig
		}

		dec := json.NewDecoder(infile)

		config := new(Config)
		err = dec.Decode(config)
		if err != nil {
			return nil, ErrBadConfig
		}

		r.to = config.To
		r.from = config.From
		r.key = config.Key
		r.domain = config.Domain
	}

	// ensure all necessary fields are set
	if r.to == "" || r.from == "" || r.key == "" || r.domain == "" {
		return nil, ErrBadConfig
	} else {
		return r, nil
	}
}

// Send sends the error 'err' with a timestamp the supplied subject
func (r *Relay) Send(subject string, err error) error {
	// Collect the information we want to send
	text := time.Now().Format(time.RFC1123) + ":\n" + err.Error()
	val := make(url.Values)
	val.Add("from", r.from)
	val.Add("to", r.to)
	val.Add("subject", subject)
	val.Add("text", text)

	// Set up the request
	req, err := http.NewRequest("POST",
		"https://api.mailgun.net/v2/"+r.domain+"/messages",
		strings.NewReader(val.Encode()))

	if err != nil {
		return ErrBadRequest
	}

	req.SetBasicAuth("api", r.key)

	// Do the request
	res, err := r.c.Do(req)

	// Handle the errors
	if res.StatusCode == 200 {
		return nil
	}
	if res.StatusCode >= 400 && res.StatusCode < 500 {
		return ErrBadRequest
	}
	if res.StatusCode >= 500 {
		return ErrMailgunDown
	}

	return ErrUnknown
}
<|endoftext|>


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

package main

import (
	"flag"

	"github.com/google/gxui"
	"github.com/google/gxui/drivers/gl"
	"github.com/google/gxui/math"
	"github.com/google/gxui/themes/dark"

	"image"
	"log"
	"os"

	"github.com/kyokomi/gomajan/pai"
	"github.com/kyokomi/gomajan/player"
	"github.com/kyokomi/gomajan/taku"
)

type MainWindow struct {
	gxui.Window
	theme  gxui.Theme
	driver gxui.Driver
}

func NewMainWindow(driver gxui.Driver) MainWindow {
	var mainWindow MainWindow
	theme := dark.CreateTheme(driver)
	window := theme.CreateWindow(800, 800, "gxjan")

	mainWindow.Window = window
	mainWindow.theme = theme
	mainWindow.driver = driver

	return mainWindow
}

var _taku *taku.Taku

func appMain(driver gxui.Driver) {

	window := NewMainWindow(driver)

	_taku = taku.NewTaku()

	// TODO: Splitterにしてみた
	rootLayer := window.Theme().CreateSplitterLayout()
//	rootLayer.SetOrientation(gxui.TopToBottom)
//	rootLayer.SetVerticalAlignment(gxui.AlignTop)

	// 手牌
	{
		tehaiLayer := window.Theme().CreateLinearLayout()
		tehaiLayer.SetDirection(gxui.TopToBottom)
		tehaiLayer.SetVerticalAlignment(gxui.AlignTop)
		tehaiLayer.SetMargin(math.CreateSpacing(20))

		for _, player := range _taku.Players {
			container := window.Theme().CreateLinearLayout()
			container.SetDirection(gxui.LeftToRight)
			container.SetHorizontalAlignment(gxui.AlignCenter)

			for _, hai := range player.Tiles() {
				if hai.Pai.Type() == pai.NoneType || hai.Val <= 0 {
					continue
				}

				for i := 0; i < hai.Val; i++ {
					window.nextPaiImage(container, &player, hai.Pai)
				}
			}
			container.SetMargin(math.CreateSpacing(10))
			tehaiLayer.AddChild(container)
		}
		rootLayer.AddChild(tehaiLayer)
		rootLayer.SetChildWeight(tehaiLayer, 0.4)
	}

	// 山
	{
		yamaLayer := window.Theme().CreateLinearLayout()
		yamaLayer.SetDirection(gxui.TopToBottom)
		yamaLayer.SetVerticalAlignment(gxui.AlignTop)
		yamaLayer.SetMargin(math.CreateSpacing(20))

		for i, 山2 := range _taku.Yama {
			container := window.Theme().CreateLinearLayout()
			container.SetDirection(gxui.TopToBottom)
			container.SetVerticalAlignment(gxui.AlignTop)
			for j, 山 := range 山2 {
				container2 := window.Theme().CreateLinearLayout()
				container2.SetDirection(gxui.LeftToRight)
				container2.SetHorizontalAlignment(gxui.AlignCenter)
				for k, 牌 := range 山 {
					if 牌.Type() == pai.NoneType {
						continue
					}

					imagePai := window.createPaiImage(牌)
					if _taku.YamaMask[i][j][k] != 0 {
						imagePai.SetVisible(false)
					}
					container2.AddChild(imagePai)
				}
				container2.SetMargin(math.CreateSpacing(1))
				container.AddChild(container2)
			}
			container.SetMargin(math.CreateSpacing(4))
			yamaLayer.AddChild(container)
		}
		rootLayer.AddChild(yamaLayer)
		rootLayer.SetChildWeight(yamaLayer, 0.6)
	}

	//	label := window.Theme().CreateLabel()
	//	label.SetText("hogehogehoge")
	//	label.SetMargin(math.CreateSpacing(200))
	//	container.AddChild(label)
	window.AddChild(rootLayer)
	window.OnClose(driver.Terminate)
	gxui.EventLoop(driver)
}

func (m MainWindow) Theme() gxui.Theme {
	return m.theme
}

type PaiImage struct {
	gxui.Image
	Pai pai.MJP
}

func (m MainWindow) createPaiImage(p pai.MJP) PaiImage {
	f, err := os.Open("data/pai-images/" + imageMappings[p])
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()
	im, _, err := image.Decode(f)
	if err != nil {
		log.Fatalln(err)
	}

	pict := m.theme.CreateImage()
	texture := m.driver.CreateTexture(im, 96)
	pict.SetTexture(texture)
	pict.SetExplicitSize(math.Size{32, 45})
	//	pict.SetMargin(math.CreateSpacing(4))

	return PaiImage{Image: pict, Pai: p}
}

var ueshita = 1 // ちょんちょん後の下段から

func nextFunc(playerID int) pai.MJP {
	if ueshita == 2 {
		_taku.Add列And山越しFunc(1)
		ueshita = 0
	}

	p := _taku.Next(ueshita, playerID)
	ueshita++
	return p
}

func (m MainWindow) nextPaiImage(container gxui.LinearLayout, player *player.Player, p pai.MJP) {
	paiImage := m.createPaiImage(p)
	paiImage.OnClick(func(e gxui.MouseEvent) {
		player.PaiDec(paiImage.Pai)
		container.RemoveChild(paiImage)

		nextPai := nextFunc(player.PlayerID())
		player.PaiInc(paiImage.Pai)
		m.nextPaiImage(container, player, nextPai)
	})

	for i := 0; i < container.ChildCount(); i++ {
		if container.ChildAt(i).(PaiImage).Pai > paiImage.Pai {
			container.AddChildAt(i, paiImage)
			return
		}
	}
	container.AddChild(paiImage)
}

func main() {
	flag.Parse()
	gl.StartDriver(appMain)
}
<commit_msg>引いた牌を山から消す対応<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"

	"github.com/google/gxui"
	"github.com/google/gxui/drivers/gl"
	"github.com/google/gxui/math"
	"github.com/google/gxui/themes/dark"

	"image"
	"log"
	"os"

	"github.com/kyokomi/gomajan/pai"
	"github.com/kyokomi/gomajan/player"
	"github.com/kyokomi/gomajan/taku"
)

type MainWindow struct {
	gxui.Window
	theme  gxui.Theme
	driver gxui.Driver
}

func NewMainWindow(driver gxui.Driver) MainWindow {
	var mainWindow MainWindow
	theme := dark.CreateTheme(driver)
	window := theme.CreateWindow(800, 800, "gxjan")

	mainWindow.Window = window
	mainWindow.theme = theme
	mainWindow.driver = driver

	return mainWindow
}

var _taku *taku.Taku
var _yamaImage [4][2][17]*PaiImage

func appMain(driver gxui.Driver) {

	window := NewMainWindow(driver)

	_taku = taku.NewTaku()

	// TODO: Splitterにしてみた
	rootLayer := window.Theme().CreateSplitterLayout()
//	rootLayer.SetOrientation(gxui.TopToBottom)
//	rootLayer.SetVerticalAlignment(gxui.AlignTop)

	// 手牌
	{
		tehaiLayer := window.Theme().CreateLinearLayout()
		tehaiLayer.SetDirection(gxui.TopToBottom)
		tehaiLayer.SetVerticalAlignment(gxui.AlignTop)
		tehaiLayer.SetMargin(math.CreateSpacing(20))

		for _, player := range _taku.Players {
			container := window.Theme().CreateLinearLayout()
			container.SetDirection(gxui.LeftToRight)
			container.SetHorizontalAlignment(gxui.AlignCenter)

			for _, hai := range player.Tiles() {
				if hai.Pai.Type() == pai.NoneType || hai.Val <= 0 {
					continue
				}

				for i := 0; i < hai.Val; i++ {
					window.nextPaiImage(container, &player, hai.Pai)
				}
			}
			container.SetMargin(math.CreateSpacing(10))
			tehaiLayer.AddChild(container)
		}
		rootLayer.AddChild(tehaiLayer)
		rootLayer.SetChildWeight(tehaiLayer, 0.4)
	}

	// 山
	{
		yamaLayer := window.Theme().CreateLinearLayout()
		yamaLayer.SetDirection(gxui.TopToBottom)
		yamaLayer.SetVerticalAlignment(gxui.AlignTop)
		yamaLayer.SetMargin(math.CreateSpacing(20))

		for i, 山2 := range _taku.Yama {
			container := window.Theme().CreateLinearLayout()
			container.SetDirection(gxui.TopToBottom)
			container.SetVerticalAlignment(gxui.AlignTop)
			for j, 山 := range 山2 {
				container2 := window.Theme().CreateLinearLayout()
				container2.SetDirection(gxui.LeftToRight)
				container2.SetHorizontalAlignment(gxui.AlignCenter)
				for k, 牌 := range 山 {
					if 牌.Type() == pai.NoneType {
						continue
					}

					imagePai := window.createPaiImage(牌)
					if _taku.YamaMask[i][j][k] != 0 {
						imagePai.SetVisible(false)
					}
					_yamaImage[i][j][k] = &imagePai
					container2.AddChild(imagePai)
				}
				container2.SetMargin(math.CreateSpacing(1))
				container.AddChild(container2)
			}
			container.SetMargin(math.CreateSpacing(4))
			yamaLayer.AddChild(container)
		}
		rootLayer.AddChild(yamaLayer)
		rootLayer.SetChildWeight(yamaLayer, 0.6)
	}

	//	label := window.Theme().CreateLabel()
	//	label.SetText("hogehogehoge")
	//	label.SetMargin(math.CreateSpacing(200))
	//	container.AddChild(label)
	window.AddChild(rootLayer)
	window.OnClose(driver.Terminate)
	gxui.EventLoop(driver)
}

func (m MainWindow) Theme() gxui.Theme {
	return m.theme
}

type PaiImage struct {
	gxui.Image
	Pai pai.MJP
}

func (m MainWindow) createPaiImage(p pai.MJP) PaiImage {
	f, err := os.Open("data/pai-images/" + imageMappings[p])
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()
	im, _, err := image.Decode(f)
	if err != nil {
		log.Fatalln(err)
	}

	pict := m.theme.CreateImage()
	texture := m.driver.CreateTexture(im, 96)
	pict.SetTexture(texture)
	pict.SetExplicitSize(math.Size{32, 45})
	//	pict.SetMargin(math.CreateSpacing(4))

	return PaiImage{Image: pict, Pai: p}
}

var ueshita = 1 // ちょんちょん後の下段からなので1

func nextFunc(playerID int) pai.MJP {
	if ueshita == 2 {
		_taku.Add列And山越しFunc(1)
		ueshita = 0
	}

	p := _taku.Next(ueshita, playerID)

	// 引いた牌を非表示
	_yamaImage[_taku.PlayYamaIndex][ueshita][_taku.PlayRetuIndex].SetVisible(false)

	ueshita++
	return p
}

func (m MainWindow) nextPaiImage(container gxui.LinearLayout, player *player.Player, p pai.MJP) {
	paiImage := m.createPaiImage(p)
	paiImage.OnClick(func(e gxui.MouseEvent) {
		player.PaiDec(paiImage.Pai)
		container.RemoveChild(paiImage)

		nextPai := nextFunc(player.PlayerID())
		player.PaiInc(paiImage.Pai)
		m.nextPaiImage(container, player, nextPai)
	})

	for i := 0; i < container.ChildCount(); i++ {
		if container.ChildAt(i).(PaiImage).Pai > paiImage.Pai {
			container.AddChildAt(i, paiImage)
			return
		}
	}
	container.AddChild(paiImage)
}

func main() {
	flag.Parse()
	gl.StartDriver(appMain)
}
<|endoftext|>


<commit_before>package main

import (
	"babelweb2/parser"
	"babelweb2/ws"
	"bufio"
	"container/list"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"reflect"
	"sync"
	"time"
)

type connectslice []string

func (i *connectslice) String() string {
	return fmt.Sprintf("%s", *i)
}
func (i *connectslice) Set(value string) error {
	fmt.Printf("%s\n", value)
	*i = append(*i, value)
	return nil
}

var myconnectlist connectslice

func flagsInit(bwPort *string) int {

	flag.Var(&myconnectlist, "hp", "list of hostnames and portnums (shorthand)")
	flag.Var(&myconnectlist, "hostport", "liste of hostnames and portnums")

	flag.StringVar(bwPort, "b", ":8080", "babelweb Port (shorthand)")
	flag.StringVar(bwPort, "bwport", ":8080", "babelweb Port")
	flag.Parse()

	if flag.NFlag() == 0 {
		log.Println("connection to local node:")
	} else {
		fmt.Println("Here are the values")
		for i := 0; i < len(myconnectlist); i++ {
			fmt.Printf("-%s\n", myconnectlist[i])
		}
	}
	return flag.NFlag()
}

func connection(updates chan parser.BabelUpdate, wg *sync.WaitGroup, bwPort *string) {
	var node string
	node = "[::1]:33123"
	var lenArg = flagsInit(bwPort)
	log.Println("lenghth %d", lenArg)
	if lenArg == 0 {
		log.Println("to connect  to %s", node)
		go ConnectionNode(updates, node, wg)
	} else {
		go connectGroup(updates, node, wg)
	}
}

func ConnectionNode(updates chan parser.BabelUpdate, node string,
	wg *sync.WaitGroup) {
	var conn net.Conn
	var err error
	wg.Add(1)
	for {
		log.Println("	Trying ", node)
		for {
			conn, err = net.Dial("tcp6", node)
			if err != nil {
				log.Println(err)
				time.Sleep(time.Second * 5)
			} else {
				break
			}
		}
		log.Println("	Connected to", node)
		fmt.Fprintf(conn, "monitor\n")
		r := bufio.NewReader(conn)
		s := parser.NewScanner(r)
		err = ws.Db.Bd.Listen(s, updates)
		conn.Close()
		log.Println("Connection closed")
		if err != nil {
			log.Println(err)
			wg.Done()
			return
		}
		ws.Db.Lock()
		err = ws.Db.Bd.Clean(updates)
		ws.Db.Unlock()
		if err != nil {
			log.Println(err)
			wg.Done()
			return
		}
	}
	wg.Done()
}

var Listconduct = list.New()

func connectGroup(updates chan parser.BabelUpdate, node string, wg *sync.WaitGroup) {

	var wg2 sync.WaitGroup
	output := func(c chan parser.BabelUpdate) {
		for n := range c {
			updates <- n
		}
		//wg2.Done()
	}
	if Listconduct.Len() > 0 {
		wg2.Add(Listconduct.Len())
	}

	for i := 0; i < len(myconnectlist); i++ {
		conduct := make(chan parser.BabelUpdate, ws.ChanelSize)
		Listconduct.PushBack(conduct)
		go ConnectionNode(conduct, myconnectlist[i], wg)
	}

	cases := make([]reflect.SelectCase, Listconduct.Len())
	var i = 0
	for e := Listconduct.Front(); e != nil; e = e.Next() {
		cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(e.Value)}
		i++
	}

	remaining := len(cases)
	for remaining > 0 {
		chosen, _, ok := reflect.Select(cases)
		if !ok {
			// The chosen channel has been closed
			cases[chosen].Chan = reflect.ValueOf(nil)
			remaining -= 1
			continue
		}
		if find(chosen) != nil {
			go output(find(chosen))
		} else {
			log.Println("null")
		}
	}
	wg2.Wait()
}

func find(index int) chan parser.BabelUpdate {
	var i = 0
	for e := Listconduct.Front(); e != nil; e = e.Next() {
		if i == (index) {
			return e.Value.(chan parser.BabelUpdate)
		} else {
			i++
		}
	}
	return nil
}

func main() {
	log.Println("	--------launching server--------")
	var bwPort string
	var wg sync.WaitGroup
	updates := make(chan parser.BabelUpdate, ws.ChanelSize)
	connection(updates, &wg, &bwPort)
	log.Println("valeur bxport%d", bwPort)

	ws.Db.Bd = parser.NewBabelDesc()
	bcastGrp := ws.NewListenerGroupe()
	go ws.MCUpdates(updates, bcastGrp, &wg)
	ws := ws.Handler(bcastGrp)
	http.Handle("/", http.FileServer(http.Dir("static/")))
	http.Handle("/style.css", http.FileServer(http.Dir("static/css/")))
	http.Handle("/initialize.js", http.FileServer(http.Dir("static/js")))
	http.Handle("/d3/d3.js", http.FileServer(http.Dir("static/js")))
	http.Handle("/ws", ws)
	err := http.ListenAndServe(bwPort, nil)
	if err != nil {
		log.Println(err)
		return
	}

	wg.Wait()
}
<commit_msg>connection to multiple routers v2.<commit_after>package main

import (
	"babelweb2/parser"
	"babelweb2/ws"
	"bufio"
	"container/list"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"reflect"
	"sync"
	"time"
)

type connectslice []string

var myconnectlist connectslice
var Listconduct = list.New()
var Quitmain = make(chan struct{}, 2)

func (i *connectslice) String() string {
	return fmt.Sprintf("%s", *i)
}
func (i *connectslice) Set(value string) error {
	fmt.Printf("%s\n", value)
	*i = append(*i, value)
	return nil
}

func flagsInit(bwPort *string) int {

	flag.Var(&myconnectlist, "hp", "list of hostnames and portnums (shorthand)")
	flag.Var(&myconnectlist, "hostport", "liste of hostnames and portnums")

	flag.StringVar(bwPort, "b", ":8080", "babelweb Port (shorthand)")
	flag.StringVar(bwPort, "bwport", ":8080", "babelweb Port")
	flag.Parse()

	if len(myconnectlist) == 0 {
		log.Println("connection to local node:")
	} else {
		fmt.Println("Here are the values")
		for i := 0; i < len(myconnectlist); i++ {
			fmt.Printf("-%s\n", myconnectlist[i])
		}
	}
	return flag.NFlag()
}

func connection(updates chan parser.BabelUpdate, wg *sync.WaitGroup, bwPort *string) {
	var node string
	node = "[::1]:33123"
	var lenArg = flagsInit(bwPort)
	log.Println("lenghth %d", lenArg)
	if lenArg == 0 {
		log.Println("to connect  to %s", node)
		go ConnectionNode(updates, node, wg, Quitmain)
	} else {
		go connectGroup(updates, node, wg)
	}
}

func ConnectionNode(updates chan parser.BabelUpdate, node string,
	wg *sync.WaitGroup, quit chan struct{}) {
	var conn net.Conn
	var err error
	exit := true
	wg.Add(1)
	defer wg.Done()
	defer close(updates)

	for {
		select {
		case _, q := <-quit:
			if !q {
				return
			}
		default:
			log.Println("	Trying ", node)
			for exit {
				select {
				case _, q := <-quit:
					if !q {
						return
					}
				default:
					conn, err = net.Dial("tcp6", node)
					if err != nil {
						log.Println(err)
						time.Sleep(time.Second * 5)
					} else {
						log.Println("test")
						exit = false
					}
				}
			}
			log.Println("	Connected to", node)
			fmt.Fprintf(conn, "monitor\n")
			r := bufio.NewReader(conn)
			s := parser.NewScanner(r)
			err = ws.Db.Bd.Listen(s, updates)
			conn.Close()
			log.Println("Connection closed")
			if err != nil {
				log.Println(err)
				return
			}
			ws.Db.Lock()
			err = ws.Db.Bd.Clean(updates)
			ws.Db.Unlock()
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func connectGroup(updates chan parser.BabelUpdate, node string, wg *sync.WaitGroup) {
	var quitgroup = make(chan struct{}, 2)
	var wgGroup sync.WaitGroup

	wg.Add(1)
	defer close(updates)
	defer wg.Done()
	defer wgGroup.Wait()
	defer close(quitgroup)

	output := func(c chan parser.BabelUpdate) {
		for n := range c {
			updates <- n
		}
	}

	for i := 0; i < len(myconnectlist); i++ {
		conduct := make(chan parser.BabelUpdate, ws.ChanelSize)
		Listconduct.PushBack(conduct)
		go ConnectionNode(conduct, myconnectlist[i], &wgGroup, quitgroup)
	}

	cases := make([]reflect.SelectCase, Listconduct.Len())
	var i = 0
	for e := Listconduct.Front(); e != nil; e = e.Next() {
		cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(e.Value)}
		i++
	}

	remaining := len(cases)
	for remaining > 0 {
		select {
		case _, q := <-Quitmain:
			if !q {
				return
			}
		default:
			chosen, _, ok := reflect.Select(cases)
			if !ok {
				cases[chosen].Chan = reflect.ValueOf(nil)
				remaining -= 1
				continue
			}
			if find(chosen) != nil {
				output(find(chosen))
			} else {
				log.Println("null")
			}
		}
	}
}

func find(index int) chan parser.BabelUpdate {
	var i = 0
	for e := Listconduct.Front(); e != nil; e = e.Next() {
		if i == (index) {
			return e.Value.(chan parser.BabelUpdate)
		} else {
			i++
		}
	}
	return nil
}

func main() {

	defer close(Quitmain)
	log.Println("	--------launching server--------")
	var bwPort string
	var wg sync.WaitGroup
	updates := make(chan parser.BabelUpdate, ws.ChanelSize)
	connection(updates, &wg, &bwPort)
	log.Println("valeur bxport%d", bwPort)

	ws.Db.Bd = parser.NewBabelDesc()
	bcastGrp := ws.NewListenerGroupe()
	go ws.MCUpdates(updates, bcastGrp, &wg)
	ws := ws.Handler(bcastGrp)
	http.Handle("/", http.FileServer(http.Dir("static/")))
	http.Handle("/style.css", http.FileServer(http.Dir("static/css/")))
	http.Handle("/initialize.js", http.FileServer(http.Dir("static/js")))
	http.Handle("/d3/d3.js", http.FileServer(http.Dir("static/js")))
	http.Handle("/ws", ws)
	err := http.ListenAndServe(bwPort, nil)
	if err != nil {
		log.Println(err)
		return
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"log/syslog"
	"os"
	"path/filepath"
	"time"

	"gopkg.in/alexzorin/libvirt-go.v2"
	"gopkg.in/yaml.v2"
)

func init() {
	flag.Parse()
}

var l *syslog.Writer
var viruri string
var master_iface string = "vlan1001"
var virconn libvirt.VirConnection
var first bool = true

func getVirConn() libvirt.VirConnection {
	if !first {
		if ok, err := virconn.IsAlive(); !ok || err != nil {
			for {
				vc, err := libvirt.NewVirConnection(viruri)
				if err == nil {
					virconn = vc
					return virconn
				}
				l.Info("failed to connect to libvirt:" + err.Error())
				time.Sleep(5 * time.Second)
			}
		}
	} else {
		for {
			vc, err := libvirt.NewVirConnection(viruri)
			if err == nil {
				first = false
				virconn = vc
				return virconn
			}
			l.Info("failed to connect to libvirt:" + err.Error())
			time.Sleep(5 * time.Second)
		}
	}
	return virconn
}

func main() {
	var vc libvirt.VirConnection
	var err error
	var buf []byte
	var data map[string]string
	l, err = syslog.Dial("", "", syslog.LOG_DAEMON|syslog.LOG_INFO, filepath.Base(os.Args[0]))
	if err != nil {
		log.Fatalf("Failed to connect to syslog: %s\n", err.Error())
		os.Exit(1)
	}
	defer l.Close()

	if buf, err = ioutil.ReadFile("/etc/svirtnet.yml"); err == nil {
		if err = yaml.Unmarshal(buf, &data); err == nil {
			master_iface = data["interface"]
		}
	}

	_, err = os.Stat("/sys/module/xenfs")
	if err == nil {
		viruri = "xen:///"
	} else {
		viruri = "qemu:///system"
	}

	l.Info("ListenAndServeTCPv4")
	go ListenAndServeTCPv4()

	callbackId := -1
	defer func() {
		vc := getVirConn()
		if callbackId >= 0 {
			vc.DomainEventDeregister(callbackId)
		}
		fmt.Printf("close libvirt conn\n")
		//	vc.CloseConnection()
		vc.UnrefAndCloseConnection()
	}()

	callback := libvirt.DomainEventCallback(
		func(c *libvirt.VirConnection, d *libvirt.VirDomain, eventDetails interface{}, f func()) int {
			if d == nil || eventDetails == nil || c == nil {
				return -1
			}

			if lifecycleEvent, ok := eventDetails.(libvirt.DomainLifecycleEvent); ok {
				domName, err := d.GetName()
				if err != nil {
					l.Info("failed to get domain name")
					return -1
				}
				switch lifecycleEvent.Event {
				case libvirt.VIR_DOMAIN_EVENT_STARTED:
					srvmutex.Lock()
					if _, ok := servers[domName]; !ok {
						servers[domName] = &Server{name: domName}
						go servers[domName].Start()
						l.Info(domName + " start serving")
						//					} else {
						//						if s.shutdown {
						//							time.Sleep(2 * time.Second)
						//							goto Loop
						//						}
					}
					srvmutex.Unlock()
				case libvirt.VIR_DOMAIN_EVENT_STOPPED, libvirt.VIR_DOMAIN_EVENT_SHUTDOWN, libvirt.VIR_DOMAIN_EVENT_CRASHED:
					srvmutex.Lock()
					if s, ok := servers[domName]; ok {
						s.Stop()
						l.Info(domName + " stop serving")
						delete(servers, domName)
					}
					srvmutex.Unlock()
				default:
					fmt.Printf("%#+v\n", eventDetails)
				}
			}
			//f()
			return 0
		},
	)

	libvirt.EventRegisterDefaultImpl()

	vc = getVirConn()
	//	defer vc.UnrefAndCloseConnection()

	callbackId = vc.DomainEventRegister(
		libvirt.VirDomain{},
		libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
		&callback,
		func() {
			fmt.Sprintf("catch")
		},
	)
	if callbackId < 0 {
		log.Fatalf("libvirt event registration failed")
		os.Exit(1)
	}

	if virdomains, err := vc.ListAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE | libvirt.VIR_CONNECT_LIST_DOMAINS_INACTIVE); err == nil {
		for _, d := range virdomains {
			domName, err := d.GetName()
			if err != nil {
				continue
			}
			srvmutex.Lock()
			if _, ok := servers[domName]; !ok {
				servers[domName] = &Server{name: domName}
				l.Info(domName + " start serving")
				go servers[domName].Start()
			}
			srvmutex.Unlock()
		}
	}

	for {
		libvirt.EventRunDefaultImpl()
	}

	// Deregister the event
	if ret := vc.DomainEventDeregister(callbackId); ret < 0 {
		l.Info("Event deregistration failed")
	}
	callbackId = -1 // Don't deregister twice

}
<commit_msg>fix locks<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"log/syslog"
	"os"
	"path/filepath"
	"time"

	"gopkg.in/alexzorin/libvirt-go.v2"
	"gopkg.in/yaml.v2"
)

func init() {
	flag.Parse()
}

var l *syslog.Writer
var viruri string
var master_iface string = "vlan1001"
var virconn libvirt.VirConnection
var first bool = true

func getVirConn() libvirt.VirConnection {
	if !first {
		if ok, err := virconn.IsAlive(); !ok || err != nil {
			for {
				vc, err := libvirt.NewVirConnection(viruri)
				if err == nil {
					virconn = vc
					return virconn
				}
				l.Info("failed to connect to libvirt:" + err.Error())
				time.Sleep(5 * time.Second)
			}
		}
	} else {
		for {
			vc, err := libvirt.NewVirConnection(viruri)
			if err == nil {
				first = false
				virconn = vc
				return virconn
			}
			l.Info("failed to connect to libvirt:" + err.Error())
			time.Sleep(5 * time.Second)
		}
	}
	return virconn
}

func main() {
	var vc libvirt.VirConnection
	var err error
	var buf []byte
	var data map[string]string
	l, err = syslog.Dial("", "", syslog.LOG_DAEMON|syslog.LOG_INFO, filepath.Base(os.Args[0]))
	if err != nil {
		log.Fatalf("Failed to connect to syslog: %s\n", err.Error())
		os.Exit(1)
	}
	defer l.Close()

	if buf, err = ioutil.ReadFile("/etc/svirtnet.yml"); err == nil {
		if err = yaml.Unmarshal(buf, &data); err == nil {
			master_iface = data["interface"]
		}
	}

	_, err = os.Stat("/sys/module/xenfs")
	if err == nil {
		viruri = "xen:///"
	} else {
		viruri = "qemu:///system"
	}

	l.Info("ListenAndServeTCPv4")
	go ListenAndServeTCPv4()

	callbackId := -1
	defer func() {
		vc := getVirConn()
		if callbackId >= 0 {
			vc.DomainEventDeregister(callbackId)
		}
		fmt.Printf("close libvirt conn\n")
		//	vc.CloseConnection()
		vc.UnrefAndCloseConnection()
	}()

	callback := libvirt.DomainEventCallback(
		func(c *libvirt.VirConnection, d *libvirt.VirDomain, eventDetails interface{}, f func()) int {
			if d == nil || eventDetails == nil || c == nil {
				return -1
			}

			if lifecycleEvent, ok := eventDetails.(libvirt.DomainLifecycleEvent); ok {
				switch lifecycleEvent.Event {
				case libvirt.VIR_DOMAIN_EVENT_STARTED:
					domName, err := d.GetName()
					if err != nil {
						l.Info("failed to get domain name")
						return -1
					}
					srvmutex.Lock()
					if _, ok := servers[domName]; !ok {
						servers[domName] = &Server{name: domName}
						go servers[domName].Start()
						l.Info(domName + " start serving")
						//					} else {
						//						if s.shutdown {
						//							time.Sleep(2 * time.Second)
						//							goto Loop
						//						}
					}
					srvmutex.Unlock()
				case libvirt.VIR_DOMAIN_EVENT_STOPPED, libvirt.VIR_DOMAIN_EVENT_SHUTDOWN, libvirt.VIR_DOMAIN_EVENT_CRASHED:
					domName, err := d.GetName()
					if err != nil {
						l.Info("failed to get domain name")
						return -1
					}
					srvmutex.Lock()
					if s, ok := servers[domName]; ok {
						s.Stop()
						l.Info(domName + " stop serving")
						delete(servers, domName)
					}
					srvmutex.Unlock()
				default:
					fmt.Printf("%#+v\n", eventDetails)
				}
			}
			//f()
			return 0
		},
	)

	libvirt.EventRegisterDefaultImpl()

	vc = getVirConn()
	//	defer vc.UnrefAndCloseConnection()

	callbackId = vc.DomainEventRegister(
		libvirt.VirDomain{},
		libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
		&callback,
		func() {
			fmt.Sprintf("catch")
		},
	)
	if callbackId < 0 {
		log.Fatalf("libvirt event registration failed")
		os.Exit(1)
	}

	if virdomains, err := vc.ListAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE | libvirt.VIR_CONNECT_LIST_DOMAINS_INACTIVE); err == nil {
		for _, d := range virdomains {
			domName, err := d.GetName()
			if err != nil {
				continue
			}
			srvmutex.Lock()
			if _, ok := servers[domName]; !ok {
				servers[domName] = &Server{name: domName}
				l.Info(domName + " start serving")
				go servers[domName].Start()
			}
			srvmutex.Unlock()
		}
	}

	for {
		libvirt.EventRunDefaultImpl()
	}

	// Deregister the event
	if ret := vc.DomainEventDeregister(callbackId); ret < 0 {
		l.Info("Event deregistration failed")
	}
	callbackId = -1 // Don't deregister twice

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"unicode/utf16"
)

type File struct {
	stringPool     *ResStringPool
	resourceMap    []uint32
	notPrecessedNS map[ResStringPoolRef]ResStringPoolRef
	namespaces     map[ResStringPoolRef]ResStringPoolRef
	XMLBuffer      bytes.Buffer
}

const (
	RES_NULL_TYPE        = 0x0000
	RES_STRING_POOL_TYPE = 0x0001
	RES_TABLE_TYPE       = 0x0002
	RES_XML_TYPE         = 0x0003

	// Chunk types in RES_XML_TYPE
	RES_XML_FIRST_CHUNK_TYPE     = 0x0100
	RES_XML_START_NAMESPACE_TYPE = 0x0100
	RES_XML_END_NAMESPACE_TYPE   = 0x0101
	RES_XML_START_ELEMENT_TYPE   = 0x0102
	RES_XML_END_ELEMENT_TYPE     = 0x0103
	RES_XML_CDATA_TYPE           = 0x0104
	RES_XML_LAST_CHUNK_TYPE      = 0x017f

	// This contains a uint32_t array mapping strings in the string
	// pool back to resource identifiers.  It is optional.
	RES_XML_RESOURCE_MAP_TYPE = 0x0180

	// Chunk types in RES_TABLE_TYPE
	RES_TABLE_PACKAGE_TYPE   = 0x0200
	RES_TABLE_TYPE_TYPE      = 0x0201
	RES_TABLE_TYPE_SPEC_TYPE = 0x0202
)

type ResChunkHeader struct {
	Type       uint16
	HeaderSize uint16
	Size       uint32
}

const SORTED_FLAG = 1 << 0
const UTF8_FLAG = 1 << 8

type ResStringPoolRef uint32

const NilResStringPoolRef = ResStringPoolRef(0xFFFFFFFF)

type ResStringPoolHeader struct {
	Header      ResChunkHeader
	StringCount uint32
	StyleCount  uint32
	Flags       uint32
	StringStart uint32
	StylesStart uint32
}

type ResStringPool struct {
	Header  ResStringPoolHeader
	Strings []string
	Styles  []string
}

type ResXMLTreeNode struct {
	Header     ResChunkHeader
	LineNumber uint32
	Comment    ResStringPoolRef
}

type ResXMLTreeNamespaceExt struct {
	Prefix ResStringPoolRef
	Uri    ResStringPoolRef
}

type ResXMLTreeAttrExt struct {
	NS             ResStringPoolRef
	Name           ResStringPoolRef
	AttributeStart uint16
	AttributeSize  uint16
	AttributeCount uint16
	IdIndex        uint16
	ClassIndex     uint16
	StyleIndex     uint16
}

type ResXMLTreeAttribute struct {
	NS         ResStringPoolRef
	Name       ResStringPoolRef
	RawValue   ResStringPoolRef
	TypedValue ResValue
}

const (
	TYPE_NULL            = 0x00
	TYPE_REFERENCE       = 0x01
	TYPE_ATTRIBUTE       = 0x02
	TYPE_STRING          = 0x03
	TYPE_FLOAT           = 0x04
	TYPE_DIMENSION       = 0x05
	TYPE_FRACTION        = 0x06
	TYPE_FIRST_INT       = 0x10
	TYPE_INT_DEC         = 0x10
	TYPE_INT_HEX         = 0x11
	TYPE_INT_BOOLEAN     = 0x12
	TYPE_FIRST_COLOR_INT = 0x1c
	TYPE_INT_COLOR_ARGB8 = 0x1c
	TYPE_INT_COLOR_RGB8  = 0x1d
	TYPE_INT_COLOR_ARGB4 = 0x1e
	TYPE_INT_COLOR_RGB4  = 0x1f
	TYPE_LAST_COLOR_INT  = 0x1f
	TYPE_LAST_INT        = 0x1f
)

type ResValue struct {
	Size     uint16
	Res0     uint8
	DataType uint8
	Data     uint32
}

type ResXMLTreeEndElementExt struct {
	NS   ResStringPoolRef
	Name ResStringPoolRef
}

func NewFile(r io.ReaderAt) (*File, error) {
	f := new(File)
	fmt.Fprintf(&f.XMLBuffer, "<?xml version=\"1.0\" encoding=\"utf-8\"?>")
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	header := new(ResChunkHeader)
	binary.Read(sr, binary.LittleEndian, header)
	offset := uint32(header.HeaderSize)

	for offset < header.Size {
		sr.Seek(int64(offset), os.SEEK_SET)
		chunkHeader := &ResChunkHeader{}
		binary.Read(sr, binary.LittleEndian, chunkHeader)

		var err error
		chunkReader := io.NewSectionReader(r, int64(offset), int64(chunkHeader.Size))
		switch chunkHeader.Type {
		case RES_STRING_POOL_TYPE:
			f.stringPool, err = ReadStringPool(chunkReader)
		case RES_XML_RESOURCE_MAP_TYPE:
			f.resourceMap, err = ReadResourceMap(chunkReader)
		case RES_XML_START_NAMESPACE_TYPE:
			err = f.ReadStartNamespace(chunkReader)
		case RES_XML_END_NAMESPACE_TYPE:
			err = f.ReadEndNamespace(chunkReader)
		case RES_XML_START_ELEMENT_TYPE:
			err = f.ReadStartElement(chunkReader)
		case RES_XML_END_ELEMENT_TYPE:
			err = f.ReadEndElement(chunkReader)
		}
		if err != nil {
			return nil, err
		}

		offset += chunkHeader.Size
	}
	return f, nil
}

func (f *File) GetString(ref ResStringPoolRef) string {
	if ref == NilResStringPoolRef {
		return ""
	}
	return f.stringPool.Strings[int(ref)]
}

func ReadStringPool(sr *io.SectionReader) (*ResStringPool, error) {
	sp := new(ResStringPool)
	binary.Read(sr, binary.LittleEndian, &sp.Header)

	stringStarts := make([]uint32, sp.Header.StringCount)
	binary.Read(sr, binary.LittleEndian, stringStarts)
	styleStarts := make([]uint32, sp.Header.StyleCount)
	binary.Read(sr, binary.LittleEndian, styleStarts)

	sp.Strings = make([]string, sp.Header.StringCount)
	for i, start := range stringStarts {
		var str string
		var err error
		if (sp.Header.Flags & UTF8_FLAG) == 0 {
			str, err = ReadUTF16(sr, int64(sp.Header.StringStart+start))
		} else {
			str, err = ReadUTF8(sr, int64(sp.Header.StringStart+start))
		}
		if err != nil {
			return nil, err
		}
		sp.Strings[i] = str
	}

	sp.Styles = make([]string, sp.Header.StyleCount)
	for i, start := range styleStarts {
		var str string
		var err error
		if (sp.Header.Flags & UTF8_FLAG) == 0 {
			str, err = ReadUTF16(sr, int64(sp.Header.StylesStart+start))
		} else {
			str, err = ReadUTF8(sr, int64(sp.Header.StylesStart+start))
		}
		if err != nil {
			return nil, err
		}
		sp.Styles[i] = str
	}

	return sp, nil
}

func ReadUTF16(sr *io.SectionReader, offset int64) (string, error) {
	var size uint16
	sr.Seek(offset, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &size); err != nil {
		return "", err
	}
	buf := make([]uint16, size)
	if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
		return "", err
	}
	return string(utf16.Decode(buf)), nil
}

func ReadUTF8(sr *io.SectionReader, offset int64) (string, error) {
	var size uint16
	sr.Seek(offset, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &size); err != nil {
		return "", err
	}
	buf := make([]uint8, size)
	if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
		return "", err
	}
	return string(buf), nil
}

func ReadResourceMap(sr *io.SectionReader) ([]uint32, error) {
	header := new(ResChunkHeader)
	binary.Read(sr, binary.LittleEndian, header)
	count := (header.Size - uint32(header.HeaderSize)) / 4
	resourceMap := make([]uint32, count)
	if err := binary.Read(sr, binary.LittleEndian, resourceMap); err != nil {
		return nil, err
	}
	return resourceMap, nil
}

func (f *File) ReadStartNamespace(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	binary.Read(sr, binary.LittleEndian, header)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	namespace := new(ResXMLTreeNamespaceExt)
	binary.Read(sr, binary.LittleEndian, namespace)

	if f.notPrecessedNS == nil {
		f.notPrecessedNS = make(map[ResStringPoolRef]ResStringPoolRef)
	}
	f.notPrecessedNS[namespace.Uri] = namespace.Prefix

	if f.namespaces == nil {
		f.namespaces = make(map[ResStringPoolRef]ResStringPoolRef)
	}
	f.namespaces[namespace.Uri] = namespace.Prefix

	return nil
}

func (f *File) ReadEndNamespace(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	binary.Read(sr, binary.LittleEndian, header)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	namespace := new(ResXMLTreeNamespaceExt)
	binary.Read(sr, binary.LittleEndian, namespace)
	delete(f.namespaces, namespace.Uri)
	return nil
}

func (f *File) ReadStartElement(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	binary.Read(sr, binary.LittleEndian, header)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	ext := new(ResXMLTreeAttrExt)
	binary.Read(sr, binary.LittleEndian, ext)

	fmt.Fprintf(&f.XMLBuffer, "<%s", f.AddNamespace(ext.NS, ext.Name))

	// output XML namespaces
	if f.notPrecessedNS != nil {
		for uri, prefix := range f.notPrecessedNS {
			fmt.Fprintf(&f.XMLBuffer, " xmlns:%s=\"", f.GetString(prefix))
			xml.Escape(&f.XMLBuffer, []byte(f.GetString(uri)))
			fmt.Fprint(&f.XMLBuffer, "\"")
		}
		f.notPrecessedNS = nil
	}

	// process attributes
	offset := int64(ext.AttributeStart + header.Header.HeaderSize)
	for i := 0; i < int(ext.AttributeCount); i++ {
		sr.Seek(offset, os.SEEK_SET)
		attr := new(ResXMLTreeAttribute)
		binary.Read(sr, binary.LittleEndian, attr)

		var value string
		if attr.RawValue != NilResStringPoolRef {
			value = f.GetString(attr.RawValue)
		} else {
			data := attr.TypedValue.Data
			switch attr.TypedValue.DataType {
			case TYPE_NULL:
				value = ""
			case TYPE_REFERENCE:
				value = fmt.Sprintf("@0x%08X", data)
			case TYPE_INT_DEC:
				value = fmt.Sprintf("%d", data)
			case TYPE_INT_HEX:
				value = fmt.Sprintf("0x%08X", data)
			case TYPE_INT_BOOLEAN:
				if data != 0 {
					value = "true"
				} else {
					value = "false"
				}
			default:
				value = fmt.Sprintf("@0x%08X", data)
			}
		}

		fmt.Fprintf(&f.XMLBuffer, " %s=\"", f.AddNamespace(attr.NS, attr.Name))
		xml.Escape(&f.XMLBuffer, []byte(value))
		fmt.Fprint(&f.XMLBuffer, "\"")
		offset += int64(ext.AttributeSize)
	}
	fmt.Fprint(&f.XMLBuffer, ">")
	return nil
}

func (f *File) ReadEndElement(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return err
	}
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	ext := new(ResXMLTreeEndElementExt)
	if err := binary.Read(sr, binary.LittleEndian, ext); err != nil {
		return err
	}
	fmt.Fprintf(&f.XMLBuffer, "</%s>", f.AddNamespace(ext.NS, ext.Name))
	return nil
}

func (f *File) AddNamespace(ns, name ResStringPoolRef) string {
	if ns != NilResStringPoolRef {
		prefix := f.GetString(f.namespaces[ns])
		return fmt.Sprintf("%s:%s", prefix, f.GetString(name))
	} else {
		return f.GetString(name)
	}
}

func main() {
	f, _ := os.Open("AndroidManifest.xml")
	xml, _ := NewFile(f)
	fmt.Println(xml.XMLBuffer.String())
}
<commit_msg>長い文字列定数に対応<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"unicode/utf16"
)

type File struct {
	stringPool     *ResStringPool
	resourceMap    []uint32
	notPrecessedNS map[ResStringPoolRef]ResStringPoolRef
	namespaces     map[ResStringPoolRef]ResStringPoolRef
	XMLBuffer      bytes.Buffer
}

const (
	RES_NULL_TYPE        = 0x0000
	RES_STRING_POOL_TYPE = 0x0001
	RES_TABLE_TYPE       = 0x0002
	RES_XML_TYPE         = 0x0003

	// Chunk types in RES_XML_TYPE
	RES_XML_FIRST_CHUNK_TYPE     = 0x0100
	RES_XML_START_NAMESPACE_TYPE = 0x0100
	RES_XML_END_NAMESPACE_TYPE   = 0x0101
	RES_XML_START_ELEMENT_TYPE   = 0x0102
	RES_XML_END_ELEMENT_TYPE     = 0x0103
	RES_XML_CDATA_TYPE           = 0x0104
	RES_XML_LAST_CHUNK_TYPE      = 0x017f

	// This contains a uint32_t array mapping strings in the string
	// pool back to resource identifiers.  It is optional.
	RES_XML_RESOURCE_MAP_TYPE = 0x0180

	// Chunk types in RES_TABLE_TYPE
	RES_TABLE_PACKAGE_TYPE   = 0x0200
	RES_TABLE_TYPE_TYPE      = 0x0201
	RES_TABLE_TYPE_SPEC_TYPE = 0x0202
)

type ResChunkHeader struct {
	Type       uint16
	HeaderSize uint16
	Size       uint32
}

const SORTED_FLAG = 1 << 0
const UTF8_FLAG = 1 << 8

type ResStringPoolRef uint32

const NilResStringPoolRef = ResStringPoolRef(0xFFFFFFFF)

type ResStringPoolHeader struct {
	Header      ResChunkHeader
	StringCount uint32
	StyleCount  uint32
	Flags       uint32
	StringStart uint32
	StylesStart uint32
}

type ResStringPool struct {
	Header  ResStringPoolHeader
	Strings []string
	Styles  []string
}

type ResXMLTreeNode struct {
	Header     ResChunkHeader
	LineNumber uint32
	Comment    ResStringPoolRef
}

type ResXMLTreeNamespaceExt struct {
	Prefix ResStringPoolRef
	Uri    ResStringPoolRef
}

type ResXMLTreeAttrExt struct {
	NS             ResStringPoolRef
	Name           ResStringPoolRef
	AttributeStart uint16
	AttributeSize  uint16
	AttributeCount uint16
	IdIndex        uint16
	ClassIndex     uint16
	StyleIndex     uint16
}

type ResXMLTreeAttribute struct {
	NS         ResStringPoolRef
	Name       ResStringPoolRef
	RawValue   ResStringPoolRef
	TypedValue ResValue
}

const (
	TYPE_NULL            = 0x00
	TYPE_REFERENCE       = 0x01
	TYPE_ATTRIBUTE       = 0x02
	TYPE_STRING          = 0x03
	TYPE_FLOAT           = 0x04
	TYPE_DIMENSION       = 0x05
	TYPE_FRACTION        = 0x06
	TYPE_FIRST_INT       = 0x10
	TYPE_INT_DEC         = 0x10
	TYPE_INT_HEX         = 0x11
	TYPE_INT_BOOLEAN     = 0x12
	TYPE_FIRST_COLOR_INT = 0x1c
	TYPE_INT_COLOR_ARGB8 = 0x1c
	TYPE_INT_COLOR_RGB8  = 0x1d
	TYPE_INT_COLOR_ARGB4 = 0x1e
	TYPE_INT_COLOR_RGB4  = 0x1f
	TYPE_LAST_COLOR_INT  = 0x1f
	TYPE_LAST_INT        = 0x1f
)

type ResValue struct {
	Size     uint16
	Res0     uint8
	DataType uint8
	Data     uint32
}

type ResXMLTreeEndElementExt struct {
	NS   ResStringPoolRef
	Name ResStringPoolRef
}

func NewFile(r io.ReaderAt) (*File, error) {
	f := new(File)
	fmt.Fprintf(&f.XMLBuffer, "<?xml version=\"1.0\" encoding=\"utf-8\"?>")
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	header := new(ResChunkHeader)
	binary.Read(sr, binary.LittleEndian, header)
	offset := uint32(header.HeaderSize)

	for offset < header.Size {
		sr.Seek(int64(offset), os.SEEK_SET)
		chunkHeader := &ResChunkHeader{}
		binary.Read(sr, binary.LittleEndian, chunkHeader)

		var err error
		chunkReader := io.NewSectionReader(r, int64(offset), int64(chunkHeader.Size))
		switch chunkHeader.Type {
		case RES_STRING_POOL_TYPE:
			f.stringPool, err = ReadStringPool(chunkReader)
		case RES_XML_RESOURCE_MAP_TYPE:
			f.resourceMap, err = ReadResourceMap(chunkReader)
		case RES_XML_START_NAMESPACE_TYPE:
			err = f.ReadStartNamespace(chunkReader)
		case RES_XML_END_NAMESPACE_TYPE:
			err = f.ReadEndNamespace(chunkReader)
		case RES_XML_START_ELEMENT_TYPE:
			err = f.ReadStartElement(chunkReader)
		case RES_XML_END_ELEMENT_TYPE:
			err = f.ReadEndElement(chunkReader)
		}
		if err != nil {
			return nil, err
		}

		offset += chunkHeader.Size
	}
	return f, nil
}

func (f *File) GetString(ref ResStringPoolRef) string {
	if ref == NilResStringPoolRef {
		return ""
	}
	return f.stringPool.Strings[int(ref)]
}

func ReadStringPool(sr *io.SectionReader) (*ResStringPool, error) {
	sp := new(ResStringPool)
	binary.Read(sr, binary.LittleEndian, &sp.Header)

	stringStarts := make([]uint32, sp.Header.StringCount)
	binary.Read(sr, binary.LittleEndian, stringStarts)
	styleStarts := make([]uint32, sp.Header.StyleCount)
	binary.Read(sr, binary.LittleEndian, styleStarts)

	sp.Strings = make([]string, sp.Header.StringCount)
	for i, start := range stringStarts {
		var str string
		var err error
		if (sp.Header.Flags & UTF8_FLAG) == 0 {
			str, err = ReadUTF16(sr, int64(sp.Header.StringStart+start))
		} else {
			str, err = ReadUTF8(sr, int64(sp.Header.StringStart+start))
		}
		if err != nil {
			return nil, err
		}
		sp.Strings[i] = str
	}

	sp.Styles = make([]string, sp.Header.StyleCount)
	for i, start := range styleStarts {
		var str string
		var err error
		if (sp.Header.Flags & UTF8_FLAG) == 0 {
			str, err = ReadUTF16(sr, int64(sp.Header.StylesStart+start))
		} else {
			str, err = ReadUTF8(sr, int64(sp.Header.StylesStart+start))
		}
		if err != nil {
			return nil, err
		}
		sp.Styles[i] = str
	}

	return sp, nil
}

func ReadUTF16(sr *io.SectionReader, offset int64) (string, error) {
	// read lenth of string
	var size int
	var first, second uint16
	sr.Seek(offset, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &first); err != nil {
		return "", err
	}
	if (first & 0x8000) != 0 {
		if err := binary.Read(sr, binary.LittleEndian, &second); err != nil {
			return "", err
		}
		size = (int(first&0x7FFF) << 16) + int(second)
	} else {
		size = int(first)
	}

	// read string value
	buf := make([]uint16, size)
	if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
		return "", err
	}
	return string(utf16.Decode(buf)), nil
}

func ReadUTF8(sr *io.SectionReader, offset int64) (string, error) {
	// read lenth of string
	var size int
	var first, second uint8
	sr.Seek(offset, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &first); err != nil {
		return "", err
	}
	if (first & 0x80) != 0 {
		if err := binary.Read(sr, binary.LittleEndian, &second); err != nil {
			return "", err
		}
		size = (int(first&0x7F) << 8) + int(second)
	} else {
		size = int(first)
	}

	buf := make([]uint8, size)
	if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
		return "", err
	}
	return string(buf), nil
}

func ReadResourceMap(sr *io.SectionReader) ([]uint32, error) {
	header := new(ResChunkHeader)
	binary.Read(sr, binary.LittleEndian, header)
	count := (header.Size - uint32(header.HeaderSize)) / 4
	resourceMap := make([]uint32, count)
	if err := binary.Read(sr, binary.LittleEndian, resourceMap); err != nil {
		return nil, err
	}
	return resourceMap, nil
}

func (f *File) ReadStartNamespace(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	binary.Read(sr, binary.LittleEndian, header)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	namespace := new(ResXMLTreeNamespaceExt)
	binary.Read(sr, binary.LittleEndian, namespace)

	if f.notPrecessedNS == nil {
		f.notPrecessedNS = make(map[ResStringPoolRef]ResStringPoolRef)
	}
	f.notPrecessedNS[namespace.Uri] = namespace.Prefix

	if f.namespaces == nil {
		f.namespaces = make(map[ResStringPoolRef]ResStringPoolRef)
	}
	f.namespaces[namespace.Uri] = namespace.Prefix

	return nil
}

func (f *File) ReadEndNamespace(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	binary.Read(sr, binary.LittleEndian, header)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	namespace := new(ResXMLTreeNamespaceExt)
	binary.Read(sr, binary.LittleEndian, namespace)
	delete(f.namespaces, namespace.Uri)
	return nil
}

func (f *File) ReadStartElement(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	binary.Read(sr, binary.LittleEndian, header)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	ext := new(ResXMLTreeAttrExt)
	binary.Read(sr, binary.LittleEndian, ext)

	fmt.Fprintf(&f.XMLBuffer, "<%s", f.AddNamespace(ext.NS, ext.Name))

	// output XML namespaces
	if f.notPrecessedNS != nil {
		for uri, prefix := range f.notPrecessedNS {
			fmt.Fprintf(&f.XMLBuffer, " xmlns:%s=\"", f.GetString(prefix))
			xml.Escape(&f.XMLBuffer, []byte(f.GetString(uri)))
			fmt.Fprint(&f.XMLBuffer, "\"")
		}
		f.notPrecessedNS = nil
	}

	// process attributes
	offset := int64(ext.AttributeStart + header.Header.HeaderSize)
	for i := 0; i < int(ext.AttributeCount); i++ {
		sr.Seek(offset, os.SEEK_SET)
		attr := new(ResXMLTreeAttribute)
		binary.Read(sr, binary.LittleEndian, attr)

		var value string
		if attr.RawValue != NilResStringPoolRef {
			value = f.GetString(attr.RawValue)
		} else {
			data := attr.TypedValue.Data
			switch attr.TypedValue.DataType {
			case TYPE_NULL:
				value = ""
			case TYPE_REFERENCE:
				value = fmt.Sprintf("@0x%08X", data)
			case TYPE_INT_DEC:
				value = fmt.Sprintf("%d", data)
			case TYPE_INT_HEX:
				value = fmt.Sprintf("0x%08X", data)
			case TYPE_INT_BOOLEAN:
				if data != 0 {
					value = "true"
				} else {
					value = "false"
				}
			default:
				value = fmt.Sprintf("@0x%08X", data)
			}
		}

		fmt.Fprintf(&f.XMLBuffer, " %s=\"", f.AddNamespace(attr.NS, attr.Name))
		xml.Escape(&f.XMLBuffer, []byte(value))
		fmt.Fprint(&f.XMLBuffer, "\"")
		offset += int64(ext.AttributeSize)
	}
	fmt.Fprint(&f.XMLBuffer, ">")
	return nil
}

func (f *File) ReadEndElement(sr *io.SectionReader) error {
	header := new(ResXMLTreeNode)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return err
	}
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	ext := new(ResXMLTreeEndElementExt)
	if err := binary.Read(sr, binary.LittleEndian, ext); err != nil {
		return err
	}
	fmt.Fprintf(&f.XMLBuffer, "</%s>", f.AddNamespace(ext.NS, ext.Name))
	return nil
}

func (f *File) AddNamespace(ns, name ResStringPoolRef) string {
	if ns != NilResStringPoolRef {
		prefix := f.GetString(f.namespaces[ns])
		return fmt.Sprintf("%s:%s", prefix, f.GetString(name))
	} else {
		return f.GetString(name)
	}
}

func main() {
	f, _ := os.Open("AndroidManifest.xml")
	xml, _ := NewFile(f)
	fmt.Println(xml.XMLBuffer.String())
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net"
	"regexp"
	"strconv"
)

var endpoint string

type document struct {
	Client string `json:"client"`
	Metric string `json:"metric"`
	Count  int    `json:"count"`
	App    string `json:"app"`
	Value  int    `json:"value"`
}

func sendDocument(doc *document) error {
	addr, err := net.ResolveUDPAddr("udp", endpoint)
	if err != nil {
		return err
	}
	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return err
	}
	defer conn.Close()
	b, err := json.Marshal(doc)
	if err != nil {
		return err
	}
	_, err = conn.Write(b)
	return err
}

func handle(data []byte) (*document, error) {
	r := regexp.MustCompile(`galeb\.(?P<addr>[\w-_]+)\..*.requestTime:(?P<value>\d+)|ms.*`)
	d := r.FindStringSubmatch(string(data))
	value, err := strconv.Atoi(d[2])
	if err != nil {
		return nil, err
	}
	doc := &document{
		Client: "tsuru",
		Metric: "response_time",
		Count:  1,
		App:    d[1],
		Value:  value,
	}
	return doc, nil
}

func main() {
	addr, err := net.ResolveUDPAddr("udp", ":8125")
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	for {
		buf := make([]byte, 1600)
		n, _, err := conn.ReadFromUDP(buf)
		if err != nil {
			log.Print(err)
		}
		handle(buf[0:n])
	}
}
<commit_msg>add apps data<commit_after>package main

import (
	"encoding/json"
	"log"
	"net"
	"regexp"
	"strconv"
)

var (
	endpoint string
	apps     map[string]string
)

type document struct {
	Client string `json:"client"`
	Metric string `json:"metric"`
	Count  int    `json:"count"`
	App    string `json:"app"`
	Value  int    `json:"value"`
}

func sendDocument(doc *document) error {
	addr, err := net.ResolveUDPAddr("udp", endpoint)
	if err != nil {
		return err
	}
	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return err
	}
	defer conn.Close()
	b, err := json.Marshal(doc)
	if err != nil {
		return err
	}
	_, err = conn.Write(b)
	return err
}

func handle(data []byte) (*document, error) {
	r := regexp.MustCompile(`galeb\.(?P<addr>[\w-_]+)\..*.requestTime:(?P<value>\d+)|ms.*`)
	d := r.FindStringSubmatch(string(data))
	value, err := strconv.Atoi(d[2])
	if err != nil {
		return nil, err
	}
	doc := &document{
		Client: "tsuru",
		Metric: "response_time",
		Count:  1,
		App:    d[1],
		Value:  value,
	}
	return doc, nil
}

func main() {
	addr, err := net.ResolveUDPAddr("udp", ":8125")
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	for {
		buf := make([]byte, 1600)
		n, _, err := conn.ReadFromUDP(buf)
		if err != nil {
			log.Print(err)
		}
		handle(buf[0:n])
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/astaxie/beego/session/mysql" // used for all session stores
	_ "github.com/astaxie/beego/session/redis"
	_ "github.com/nhatvhm/asolution/models"
	_ "github.com/nhatvhm/asolution/routers"
	_ "github.com/lib/pq"
	"log"
	"time"
	"strconv"
    "os"
)

var (
   dbConnectString string = beego.AppConfig.String("dbConnectString")
   dbDriverName string = beego.AppConfig.String("dbDriverName")
   dbMaxIdle, dbMaxIdleErr = beego.AppConfig.Int("dbMaxIdle")
   dbMaxConn, dbMaxConnErr = beego.AppConfig.Int("dbMaxConn")
 )

func init() {
	// Development Settings, adjust for production
	// mysql / sqlite3 / postgres driver registered by default already
	orm.RegisterDriver(dbDriverName, orm.DRPostgres)

	if dbMaxIdleErr != nil {
      panic("Main Init: - Unable to start the server can't parse dbMaxIdleErr from configuration file must be int.")
    }

    if dbMaxConnErr != nil {
      panic("Main Init: - Unable to start the server can't parse dbMaxConnErr from configuration file must be int.")
    }

	//                    db alias  drivername
	orm.RegisterDataBase("default", dbDriverName, dbConnectString, dbMaxIdle, dbMaxConn)
	orm.DefaultTimeLoc = time.UTC

}

func main() {
	//beego.TemplateLeft = "<<<" // set to make internal template compatible with most front ends i.e. Angular, Polymer, etc
	//beego.TemplateRight = ">>>"

	name := "default"
	// Whether to drop table and re-create.
	force := true
	// Print log.
	verbose := true
	// Error.
	err := orm.RunSyncdb(name, force, verbose)
	if err != nil {
		log.Println(err)
	}
	// Reconfig port
	port, err := strconv.Atoi(os.Getenv("PORT"))
    if err == nil {
        beego.httpport = port
    }
	beego.Run()
}
<commit_msg>fix Web process failed to bind to  within 60 seconds of launch<commit_after>package main

import (
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/astaxie/beego/session/mysql" // used for all session stores
	_ "github.com/astaxie/beego/session/redis"
	_ "github.com/nhatvhm/asolution/models"
	_ "github.com/nhatvhm/asolution/routers"
	_ "github.com/lib/pq"
	"log"
	"time"
	"strconv"
    "os"
)

var (
   dbConnectString string = beego.AppConfig.String("dbConnectString")
   dbDriverName string = beego.AppConfig.String("dbDriverName")
   dbMaxIdle, dbMaxIdleErr = beego.AppConfig.Int("dbMaxIdle")
   dbMaxConn, dbMaxConnErr = beego.AppConfig.Int("dbMaxConn")
 )

func init() {
	// Development Settings, adjust for production
	// mysql / sqlite3 / postgres driver registered by default already
	orm.RegisterDriver(dbDriverName, orm.DRPostgres)

	if dbMaxIdleErr != nil {
      panic("Main Init: - Unable to start the server can't parse dbMaxIdleErr from configuration file must be int.")
    }

    if dbMaxConnErr != nil {
      panic("Main Init: - Unable to start the server can't parse dbMaxConnErr from configuration file must be int.")
    }

	//                    db alias  drivername
	orm.RegisterDataBase("default", dbDriverName, dbConnectString, dbMaxIdle, dbMaxConn)
	orm.DefaultTimeLoc = time.UTC

}

func main() {
	//beego.TemplateLeft = "<<<" // set to make internal template compatible with most front ends i.e. Angular, Polymer, etc
	//beego.TemplateRight = ">>>"

	name := "default"
	// Whether to drop table and re-create.
	force := true
	// Print log.
	verbose := true
	// Error.
	err := orm.RunSyncdb(name, force, verbose)
	if err != nil {
		log.Println(err)
	}
	// Reconfig port
	port, err := strconv.Atoi(os.Getenv("PORT"))
    if err == nil {
        beego.Run(port)
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The solid-mon-rpi 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 (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"sort"
	"time"

	"golang.org/x/net/websocket"

	"github.com/go-daq/smbus"
	"github.com/gonum/plot/palette/brewer"
	"github.com/sbinet-solid/solid-mon-rpi/sensors"
)

func main() {
	var (
		addr    = flag.String("addr", ":8080", "[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")
		freq    = flag.Duration("freq", 2*time.Second, "data polling interval")
		cfgFlag = flag.String("cfg", "", "path to an XML configuration file for sensors")
	)

	flag.Parse()

	log.SetFlags(0)
	log.SetPrefix("solid-mon-rpi ")

	log.Printf("starting up web-server on: %v\n", *addr)
	srv, err := newServer(*addr, *freq, *busID, *busAddr)
	if err != nil {
		log.Fatalf("error starting server: %v", err)
	}

	if *cfgFlag != "" {
		var cfg Config
		f, err := os.Open(*cfgFlag)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		err = xml.NewDecoder(f).Decode(&cfg)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("cfg: %+v\n", cfg.Sensors)
		srv.bus.descr = cfg.Sensors

		set := make(map[string]int)
		for _, descr := range srv.bus.descr {
			set[descr.Name] = 1
		}
		var labels []string
		for k := range set {
			labels = append(labels, k)
		}
		sort.Strings(labels)
		p, err := brewer.GetPalette(brewer.TypeAny, "Dark2", len(labels))
		if err != nil {
			log.Fatal(err)
		}
		colors := p.Colors()
		for i, label := range labels {
			col := colors[i%len(colors)]
			plotColors[label] = col
		}
	}

	http.Handle("/", srv)
	http.Handle("/data", websocket.Handler(srv.dataHandler))
	http.HandleFunc("/echo", srv.wrap(srv.echoHandler))

	err = http.ListenAndServe(srv.addr, nil)
	if err != nil {
		srv.quit <- 1
		log.Fatalf("error running server: %v", err)
	}
}

type Config struct {
	XMLName xml.Name        `xml:"data"`
	Sensors []sensors.Descr `xml:"sensor"`
	Freq    time.Duration
}

type server struct {
	addr string
	freq time.Duration
	quit chan int

	bus struct {
		id    int
		addr  uint8
		data  chan sensors.Sensors
		descr []sensors.Descr
	}

	tmpl    *template.Template
	dataReg registry // clients interested in sensors data
	plots   chan Plots
	echo    chan sensors.Sensors
}

func newServer(addr string, freq time.Duration, busID, busAddr int) (*server, error) {
	if addr == "" {
		addr = getHostIP() + ":80"
	}

	srv := &server{
		addr:    addr,
		freq:    freq,
		quit:    make(chan int),
		dataReg: newRegistry(),
		tmpl:    template.Must(template.New("fcs").Parse(indexTmpl)),
		plots:   make(chan Plots),
		echo:    make(chan sensors.Sensors),
	}
	srv.bus.id = busID
	srv.bus.addr = uint8(busAddr)
	srv.bus.data = make(chan sensors.Sensors)

	conn, err := smbus.Open(srv.bus.id, srv.bus.addr)
	if err != nil {
		return nil, fmt.Errorf(
			"error opening SMBus connection (id=%d addr=0x%x): %v",
			srv.bus.id,
			srv.bus.addr,
			err,
		)
	}
	go srv.run(conn)

	return srv, nil
}

func (srv *server) Freq() float64 {
	return 1 / srv.freq.Seconds()
}

func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	srv.wrap(srv.rootHandler)(w, r)
}

func (srv *server) rootHandler(w http.ResponseWriter, r *http.Request) error {
	return srv.tmpl.Execute(w, srv)
}

func (srv *server) wrap(f func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		err := f(w, r)
		if err != nil {
			log.Printf("error: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

func (srv *server) echoHandler(w http.ResponseWriter, r *http.Request) error {
	if r.Method != http.MethodGet {
		return fmt.Errorf("invalid HTTP request (got=%v, want=%v)", r.Method, http.MethodGet)
	}
	timeout := time.NewTimer(2 * srv.freq)
	defer timeout.Stop()
	select {
	case <-timeout.C:
		return fmt.Errorf("timeout retrieving data from board")
	case data := <-srv.echo:
		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(data)
		if err != nil {
			return err
		}
	}
	return nil
}

func (srv *server) dataHandler(ws *websocket.Conn) {
	log.Printf("new client...")
	c := &client{
		srv:   srv,
		reg:   &srv.dataReg,
		datac: make(chan []byte, 256),
		ws:    ws,
	}
	c.reg.register <- c
	defer c.Release()

	c.run()
}

func (srv *server) run(bus *smbus.Conn) {
	go srv.daq(bus)
	go srv.mon()
	for {
		select {
		case c := <-srv.dataReg.register:
			log.Printf("client registering [%v]...", c.ws.LocalAddr())
			srv.dataReg.clients[c] = true

		case c := <-srv.dataReg.unregister:
			if _, ok := srv.dataReg.clients[c]; ok {
				delete(srv.dataReg.clients, c)
				close(c.datac)
				log.Printf(
					"client disconnected [%v]\n",
					c.ws.LocalAddr(),
				)
			}

		case plots := <-srv.plots:
			if len(srv.dataReg.clients) == 0 {
				// no client connected
				continue
			}
			dataBuf := new(bytes.Buffer)
			err := json.NewEncoder(dataBuf).Encode(&plots)
			if err != nil {
				log.Printf("error marshalling data: %v\n", err)
				continue
			}
			for c := range srv.dataReg.clients {
				select {
				case c.datac <- dataBuf.Bytes():
				default:
					close(c.datac)
					delete(srv.dataReg.clients, c)
				}
			}
		}
	}
}

func (srv *server) daq(bus *smbus.Conn) {
	defer bus.Close()

	tick := time.NewTicker(srv.freq)
	defer tick.Stop()

	i := 0
	for range tick.C {
		data, err := srv.fetchData(bus)
		if err != nil {
			log.Printf("error fetching data: %v\n", err)
			continue
		}

		i++
		if i%10 == 0 {
			log.Printf("daq: %+v\n", data)
		}
		select {
		case srv.bus.data <- data:
		default:
			// nobody is listening
			// drop it on the floor
		}
	}
}

func (srv *server) mon() {
	trendTick := time.NewTicker(time.Minute)
	defer trendTick.Stop()

	table := newNtuple()
	trend := newNtuple()

	first := true
	var data sensors.Sensors
	for {
		select {
		case data = <-srv.bus.data:
			table.add(data)
			if first {
				first = false
				trend.add(data)
			}
			psFast, err := newControlPlots(table.data)
			if err != nil {
				log.Printf("error creating monitoring plots: %v", err)
				continue
			}
			psSlow, err := newControlPlots(trend.data)
			if err != nil {
				log.Printf("error creating (trend) monitoring plots: %v", err)
				continue
			}
			ps := Plots{
				update: time.Now().UTC(),
				plots:  psFast,
				trends: psSlow,
				data:   data,
			}
			select {
			case srv.plots <- ps:
			default:
				// nobody is listening
			}
		case <-trendTick.C:
			trend.add(data)

		case srv.echo <- data:
		}
	}
}

type ntuple struct {
	data []sensors.Sensors
}

func newNtuple() *ntuple {
	return &ntuple{
		data: make([]sensors.Sensors, 0, 2048),
	}
}

func (nt *ntuple) add(data sensors.Sensors) {
	if len(nt.data) == cap(nt.data) {
		i := len(nt.data)
		copy(nt.data[:i/2], nt.data[i/2:])
		nt.data = nt.data[:i/2]
	}
	nt.data = append(nt.data, data)
}

func (srv *server) fetchData(bus *smbus.Conn) (sensors.Sensors, error) {
	data, err := sensors.New(bus, srv.bus.addr, srv.bus.descr)
	if err != nil {
		return data, err
	}

	return data, nil
}

func getHostIP() string {
	host, err := os.Hostname()
	if err != nil {
		log.Fatalf("could not retrieve hostname: %v\n", err)
	}

	addrs, err := net.LookupIP(host)
	if err != nil {
		log.Fatalf("could not lookup hostname IP: %v\n", err)
	}

	for _, addr := range addrs {
		ipv4 := addr.To4()
		if ipv4 == nil {
			continue
		}
		return ipv4.String()
	}

	log.Fatalf("could not infer host IP")
	return ""
}
<commit_msg>solid-mon-rpi: handle stop server<commit_after>// Copyright 2017 The solid-mon-rpi 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 (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"sort"
	"time"

	"golang.org/x/net/websocket"

	"github.com/go-daq/smbus"
	"github.com/gonum/plot/palette/brewer"
	"github.com/sbinet-solid/solid-mon-rpi/sensors"
)

func main() {
	var (
		addr    = flag.String("addr", ":8080", "[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")
		freq    = flag.Duration("freq", 2*time.Second, "data polling interval")
		cfgFlag = flag.String("cfg", "", "path to an XML configuration file for sensors")
	)

	flag.Parse()

	log.SetFlags(0)
	log.SetPrefix("solid-mon-rpi ")

	log.Printf("starting up web-server on: %v\n", *addr)
	srv, err := newServer(*addr, *freq, *busID, *busAddr)
	if err != nil {
		log.Fatalf("error starting server: %v", err)
	}

	if *cfgFlag != "" {
		var cfg Config
		f, err := os.Open(*cfgFlag)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		err = xml.NewDecoder(f).Decode(&cfg)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("cfg: %+v\n", cfg.Sensors)
		srv.bus.descr = cfg.Sensors

		set := make(map[string]int)
		for _, descr := range srv.bus.descr {
			set[descr.Name] = 1
		}
		var labels []string
		for k := range set {
			labels = append(labels, k)
		}
		sort.Strings(labels)
		p, err := brewer.GetPalette(brewer.TypeAny, "Dark2", len(labels))
		if err != nil {
			log.Fatal(err)
		}
		colors := p.Colors()
		for i, label := range labels {
			col := colors[i%len(colors)]
			plotColors[label] = col
		}
	}

	http.Handle("/", srv)
	http.Handle("/data", websocket.Handler(srv.dataHandler))
	http.HandleFunc("/echo", srv.wrap(srv.echoHandler))

	err = http.ListenAndServe(srv.addr, nil)
	if err != nil {
		srv.quit <- 1
		log.Fatalf("error running server: %v", err)
	}
}

type Config struct {
	XMLName xml.Name        `xml:"data"`
	Sensors []sensors.Descr `xml:"sensor"`
	Freq    time.Duration
}

type server struct {
	addr string
	freq time.Duration
	quit chan int

	bus struct {
		id    int
		addr  uint8
		data  chan sensors.Sensors
		descr []sensors.Descr
	}

	tmpl    *template.Template
	dataReg registry // clients interested in sensors data
	plots   chan Plots
	echo    chan sensors.Sensors
}

func newServer(addr string, freq time.Duration, busID, busAddr int) (*server, error) {
	if addr == "" {
		addr = getHostIP() + ":80"
	}

	srv := &server{
		addr:    addr,
		freq:    freq,
		quit:    make(chan int),
		dataReg: newRegistry(),
		tmpl:    template.Must(template.New("fcs").Parse(indexTmpl)),
		plots:   make(chan Plots),
		echo:    make(chan sensors.Sensors),
	}
	srv.bus.id = busID
	srv.bus.addr = uint8(busAddr)
	srv.bus.data = make(chan sensors.Sensors)

	conn, err := smbus.Open(srv.bus.id, srv.bus.addr)
	if err != nil {
		return nil, fmt.Errorf(
			"error opening SMBus connection (id=%d addr=0x%x): %v",
			srv.bus.id,
			srv.bus.addr,
			err,
		)
	}
	go srv.run(conn)

	return srv, nil
}

func (srv *server) Freq() float64 {
	return 1 / srv.freq.Seconds()
}

func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	srv.wrap(srv.rootHandler)(w, r)
}

func (srv *server) rootHandler(w http.ResponseWriter, r *http.Request) error {
	return srv.tmpl.Execute(w, srv)
}

func (srv *server) wrap(f func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		err := f(w, r)
		if err != nil {
			log.Printf("error: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

func (srv *server) echoHandler(w http.ResponseWriter, r *http.Request) error {
	if r.Method != http.MethodGet {
		return fmt.Errorf("invalid HTTP request (got=%v, want=%v)", r.Method, http.MethodGet)
	}
	timeout := time.NewTimer(2 * srv.freq)
	defer timeout.Stop()
	select {
	case <-timeout.C:
		return fmt.Errorf("timeout retrieving data from board")
	case data := <-srv.echo:
		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(data)
		if err != nil {
			return err
		}
	}
	return nil
}

func (srv *server) dataHandler(ws *websocket.Conn) {
	log.Printf("new client...")
	c := &client{
		srv:   srv,
		reg:   &srv.dataReg,
		datac: make(chan []byte, 256),
		ws:    ws,
	}
	c.reg.register <- c
	defer c.Release()

	c.run()
}

func (srv *server) run(bus *smbus.Conn) {
	go srv.daq(bus)
	go srv.mon()
	for {
		select {
		case <-srv.quit:
			log.Printf("shutting down server")
			close(srv.quit)
			return
		case c := <-srv.dataReg.register:
			log.Printf("client registering [%v]...", c.ws.LocalAddr())
			srv.dataReg.clients[c] = true

		case c := <-srv.dataReg.unregister:
			if _, ok := srv.dataReg.clients[c]; ok {
				delete(srv.dataReg.clients, c)
				close(c.datac)
				log.Printf(
					"client disconnected [%v]\n",
					c.ws.LocalAddr(),
				)
			}

		case plots := <-srv.plots:
			if len(srv.dataReg.clients) == 0 {
				// no client connected
				continue
			}
			dataBuf := new(bytes.Buffer)
			err := json.NewEncoder(dataBuf).Encode(&plots)
			if err != nil {
				log.Printf("error marshalling data: %v\n", err)
				continue
			}
			for c := range srv.dataReg.clients {
				select {
				case c.datac <- dataBuf.Bytes():
				default:
					close(c.datac)
					delete(srv.dataReg.clients, c)
				}
			}
		}
	}
}

func (srv *server) daq(bus *smbus.Conn) {
	defer bus.Close()

	tick := time.NewTicker(srv.freq)
	defer tick.Stop()

	i := 0
	for range tick.C {
		data, err := srv.fetchData(bus)
		if err != nil {
			log.Printf("error fetching data: %v\n", err)
			continue
		}

		i++
		if i%10 == 0 {
			log.Printf("daq: %+v\n", data)
		}
		select {
		case srv.bus.data <- data:
		default:
			// nobody is listening
			// drop it on the floor
		}
	}
}

func (srv *server) mon() {
	trendTick := time.NewTicker(time.Minute)
	defer trendTick.Stop()

	table := newNtuple()
	trend := newNtuple()

	first := true
	var data sensors.Sensors
	for {
		select {
		case data = <-srv.bus.data:
			table.add(data)
			if first {
				first = false
				trend.add(data)
			}
			psFast, err := newControlPlots(table.data)
			if err != nil {
				log.Printf("error creating monitoring plots: %v", err)
				continue
			}
			psSlow, err := newControlPlots(trend.data)
			if err != nil {
				log.Printf("error creating (trend) monitoring plots: %v", err)
				continue
			}
			ps := Plots{
				update: time.Now().UTC(),
				plots:  psFast,
				trends: psSlow,
				data:   data,
			}
			select {
			case srv.plots <- ps:
			default:
				// nobody is listening
			}
		case <-trendTick.C:
			trend.add(data)

		case srv.echo <- data:
		}
	}
}

type ntuple struct {
	data []sensors.Sensors
}

func newNtuple() *ntuple {
	return &ntuple{
		data: make([]sensors.Sensors, 0, 2048),
	}
}

func (nt *ntuple) add(data sensors.Sensors) {
	if len(nt.data) == cap(nt.data) {
		i := len(nt.data)
		copy(nt.data[:i/2], nt.data[i/2:])
		nt.data = nt.data[:i/2]
	}
	nt.data = append(nt.data, data)
}

func (srv *server) fetchData(bus *smbus.Conn) (sensors.Sensors, error) {
	data, err := sensors.New(bus, srv.bus.addr, srv.bus.descr)
	if err != nil {
		return data, err
	}

	return data, nil
}

func getHostIP() string {
	host, err := os.Hostname()
	if err != nil {
		log.Fatalf("could not retrieve hostname: %v\n", err)
	}

	addrs, err := net.LookupIP(host)
	if err != nil {
		log.Fatalf("could not lookup hostname IP: %v\n", err)
	}

	for _, addr := range addrs {
		ipv4 := addr.To4()
		if ipv4 == nil {
			continue
		}
		return ipv4.String()
	}

	log.Fatalf("could not infer host IP")
	return ""
}
<|endoftext|>