<commit_before>// Copyright 2012 GAEGo 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 config provides persisted configuration maps that can be set
and retrieved using a unique key.

Each Config is stored in memory, memcache, and the datastore. This allows
Config settings to be shared between instances, while at the same time
elimating the cost associated with retrieving directly form the datastore.

Example Usage:

Set the config

	c := appengine.Context(r)
	appMap := map[string]string{
		"Title": "Storeski"
	}
	appConfig, err := config.GetOrInsert(c, "app", appMap)

Get the config in another package

	appConfig, err := config.Get(c, "app")
	appTitle := appConfig.Values"Title"] // "Storeski"

Edit and save the config

	appConfig.Values["Title"] = "Changed"
  err := appConfig.Put(c)
	appConfig2, err := config.Get(c, "app")
  appTitle := appConfig2.Values["Title"] // "Changed"

*/
package config

// TODO(kylefinley) move the JSON encoding of invalid datastore types
// to ds.

import (
	"appengine"
	"appengine/datastore"
	"encoding/json"
	"github.com/gaego/ds"
)

func init() {
	// Store Config entities in datastore, memcache and memory.
	ds.Register("Config", true, true, true)
}

// Config is the struct that is stored. The map[string]string{}
// is gob encoded before being passed to ds.
type Config struct {
	Key        *datastore.Key `datastore"-"`
	ValuesJSON []byte
	Values     map[string]string `datastore:"-"`
}

// SetValues sets the map to the Values property and sets ValuesJSON
// to a JSON encoded []byte
func (cnfg *Config) SetValues(m map[string]string) (err error) {

	val, err := json.Marshal(m)
	cnfg.Values = m
	cnfg.ValuesJSON = val
	return
}

// SetKey set the Key property to a datastore.Key using the passed key
// as the StringID
func (cnfg *Config) SetKey(c appengine.Context, key string) {
	cnfg.Key = datastore.NewKey(c, "Config", key, 0, nil)
	return
}

// Put encodes the Values and saves the Config to the store.
func (cnfg *Config) Put(c appengine.Context) (err error) {

	val, err := json.Marshal(cnfg.Values)
	if err != nil {
		return
	}
	cnfg.ValuesJSON = val

	key, err := ds.Put(c, cnfg.Key, cnfg)
	cnfg.Key = key
	return
}

// Get retieves a Config using the string key. If Config is not found
// it returns ds/error.ErrNoSuchEntity.
func Get(c appengine.Context, key string) (cnfg *Config, err error) {

	cnfg = new(Config)
	cnfg.SetKey(c, key)
	err = ds.Get(c, cnfg.Key, cnfg)

	m := make(map[string]string)
	err = json.Unmarshal(cnfg.ValuesJSON, &m)
	cnfg.Values = m

	return
}

// GetOrInsert takes takes a key and map. If the key belongs to a previously
// saved Config it is returned; Otherwise the map is saved.
func GetOrInsert(c appengine.Context, key string,
	m map[string]string) (cnfg *Config, err error) {

	cnfg, err = Get(c, key)
	if err == nil {
		return
	}
	cnfg = new(Config)
	err = cnfg.SetValues(m)
	if err != nil {
		return
	}
	cnfg.SetKey(c, key)
	cnfg.Put(c)
	return
}
<commit_msg>Fixed a typo on the datastore settings for Config<commit_after>// Copyright 2012 GAEGo 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 config provides persisted configuration maps that can be set
and retrieved using a unique key.

Each Config is stored in memory, memcache, and the datastore. This allows
Config settings to be shared between instances, while at the same time
elimating the cost associated with retrieving directly form the datastore.

Example Usage:

Set the config

	c := appengine.Context(r)
	appMap := map[string]string{
		"Title": "Storeski"
	}
	appConfig, err := config.GetOrInsert(c, "app", appMap)

Get the config in another package

	appConfig, err := config.Get(c, "app")
	appTitle := appConfig.Values"Title"] // "Storeski"

Edit and save the config

	appConfig.Values["Title"] = "Changed"
  err := appConfig.Put(c)
	appConfig2, err := config.Get(c, "app")
  appTitle := appConfig2.Values["Title"] // "Changed"

*/
package config

// TODO(kylefinley) move the JSON encoding of invalid datastore types
// to ds.

import (
	"appengine"
	"appengine/datastore"
	"encoding/json"
	"github.com/gaego/ds"
)

func init() {
	// Store Config entities in datastore, memcache and memory.
	ds.Register("Config", true, true, true)
}

// Config is the struct that is stored. The map[string]string{}
// is gob encoded before being passed to ds.
type Config struct {
	Key        *datastore.Key `datastore:"-"`
	ValuesJSON []byte
	Values     map[string]string `datastore:"-"`
}

// SetValues sets the map to the Values property and sets ValuesJSON
// to a JSON encoded []byte
func (cnfg *Config) SetValues(m map[string]string) (err error) {

	val, err := json.Marshal(m)
	cnfg.Values = m
	cnfg.ValuesJSON = val
	return
}

// SetKey set the Key property to a datastore.Key using the passed key
// as the StringID
func (cnfg *Config) SetKey(c appengine.Context, key string) {
	cnfg.Key = datastore.NewKey(c, "Config", key, 0, nil)
	return
}

// Put encodes the Values and saves the Config to the store.
func (cnfg *Config) Put(c appengine.Context) (err error) {

	val, err := json.Marshal(cnfg.Values)
	if err != nil {
		return
	}
	cnfg.ValuesJSON = val

	key, err := ds.Put(c, cnfg.Key, cnfg)
	cnfg.Key = key
	return
}

// Get retieves a Config using the string key. If Config is not found
// it returns ds/error.ErrNoSuchEntity.
func Get(c appengine.Context, key string) (cnfg *Config, err error) {

	cnfg = new(Config)
	cnfg.SetKey(c, key)
	err = ds.Get(c, cnfg.Key, cnfg)

	m := make(map[string]string)
	err = json.Unmarshal(cnfg.ValuesJSON, &m)
	cnfg.Values = m

	return
}

// GetOrInsert takes takes a key and map. If the key belongs to a previously
// saved Config it is returned; Otherwise the map is saved.
func GetOrInsert(c appengine.Context, key string,
	m map[string]string) (cnfg *Config, err error) {

	cnfg, err = Get(c, key)
	if err == nil {
		return
	}
	cnfg = new(Config)
	err = cnfg.SetValues(m)
	if err != nil {
		return
	}
	cnfg.SetKey(c, key)
	cnfg.Put(c)
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 Martin Schnabel. 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 (
	"fmt"
	"io"
	"os/user"
	"path/filepath"
	"text/template"

	"github.com/mb0/vmail/feeds"
	"github.com/mb0/vmail/store"
)

type Config struct {
	*user.User
}

func NewConfig(name string) (*Config, error) {
	u, err := user.Lookup(name)
	if err != nil {
		return nil, err
	}
	return &Config{User: u}, nil
}

func (c *Config) Current() error {
	usr, err := user.Current()
	if err != nil {
		return err
	}
	if usr.Username != c.Username {
		return fmt.Errorf("current user is not %s", c.Username)
	}
	return nil
}

func (c *Config) DbFile() string {
	return filepath.Join(c.HomeDir, "vmail.sqlite")
}

func (c *Config) FeedsDir() string {
	return filepath.Join(c.HomeDir, "feeds") + "/"
}

func (c *Config) Fprint(w io.Writer, conf string) error {
	if conf == "sql" {
		var sqls []string
		sqls = append(sqls, store.CreateSql...)
		sqls = append(sqls, feeds.CreateSql...)
		for _, s := range sqls {
			_, err := fmt.Fprintln(w, s)
			if err != nil {
				return err
			}
		}
		return nil
	}
	tmpl := tmpls.Lookup(conf)
	if tmpl == nil {
		return fmt.Errorf("config requires valid option")
	}
	if conf != "" {
		_, err := fmt.Fprintf(w, "# generated with 'vmail config %s'", conf)
		if err != nil {
			return err
		}
	}
	return tmpl.Execute(w, c)
}

var tmpls = template.Must(template.New("").Parse(`vmail config
User: {{ .Username }}
Home: {{ .HomeDir }}
{{define "postfix_domain"}}
dbpath = {{ .HomeDir }}/vmail.sqlite
query = SELECT domain FROM dest WHERE domain='%s' AND enable=1 AND type=1

{{end}}{{define "postfix_mailbox"}}
dbpath = {{ .HomeDir }}/vmail.sqlite
query = SELECT name||"@"||domain||"/" as maildir FROM dest WHERE name='%u' AND domain='%d' AND enable=1 AND type=1

{{end}}{{define "postfix_alias"}}
dbpath = {{ .HomeDir }}/vmail.sqlite
query = SELECT forwrd FROM dest WHERE name='%u' AND domain='%d' AND enable=1 AND type=2

{{end}}{{define "dovecot_auth"}}
mail_uid = {{ .Username }}
mail_gid = {{ .Username }}

mail_location = maildir:{{ .HomeDir }}/%u
mail_home = {{ .HomeDir }}/%u/home

userdb {
  driver = static
  args =
}

passdb {
    driver = sql
    args = /etc/dovecot/vmail-sql.conf.ext
}

{{end}}{{define "dovecot_sql"}}
driver = sqlite
connect = {{ .HomeDir }}/vmail.sqlite
default_pass_scheme = SHA512-CRYPT

password_query = \
    SELECT passwd as password, name||'@'||domain as user FROM dest \
    WHERE name = '%n' AND domain = '%d' AND type = 1 AND enable = 1

{{end}}`))
<commit_msg>fixes virtual mailbox domains to also return alias domains otherwise relay for aliases is denied<commit_after>// Copyright 2013 Martin Schnabel. 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 (
	"fmt"
	"io"
	"os/user"
	"path/filepath"
	"text/template"

	"github.com/mb0/vmail/feeds"
	"github.com/mb0/vmail/store"
)

type Config struct {
	*user.User
}

func NewConfig(name string) (*Config, error) {
	u, err := user.Lookup(name)
	if err != nil {
		return nil, err
	}
	return &Config{User: u}, nil
}

func (c *Config) Current() error {
	usr, err := user.Current()
	if err != nil {
		return err
	}
	if usr.Username != c.Username {
		return fmt.Errorf("current user is not %s", c.Username)
	}
	return nil
}

func (c *Config) DbFile() string {
	return filepath.Join(c.HomeDir, "vmail.sqlite")
}

func (c *Config) FeedsDir() string {
	return filepath.Join(c.HomeDir, "feeds") + "/"
}

func (c *Config) Fprint(w io.Writer, conf string) error {
	if conf == "sql" {
		var sqls []string
		sqls = append(sqls, store.CreateSql...)
		sqls = append(sqls, feeds.CreateSql...)
		for _, s := range sqls {
			_, err := fmt.Fprintln(w, s)
			if err != nil {
				return err
			}
		}
		return nil
	}
	tmpl := tmpls.Lookup(conf)
	if tmpl == nil {
		return fmt.Errorf("config requires valid option")
	}
	if conf != "" {
		_, err := fmt.Fprintf(w, "# generated with 'vmail config %s'", conf)
		if err != nil {
			return err
		}
	}
	return tmpl.Execute(w, c)
}

var tmpls = template.Must(template.New("").Parse(`vmail config
User: {{ .Username }}
Home: {{ .HomeDir }}
{{define "postfix_domain"}}
dbpath = {{ .HomeDir }}/vmail.sqlite
query = SELECT domain FROM dest WHERE domain='%s' AND enable=1

{{end}}{{define "postfix_mailbox"}}
dbpath = {{ .HomeDir }}/vmail.sqlite
query = SELECT name||"@"||domain||"/" as maildir FROM dest WHERE name='%u' AND domain='%d' AND enable=1 AND type=1

{{end}}{{define "postfix_alias"}}
dbpath = {{ .HomeDir }}/vmail.sqlite
query = SELECT forwrd FROM dest WHERE name='%u' AND domain='%d' AND enable=1 AND type=2

{{end}}{{define "dovecot_auth"}}
mail_uid = {{ .Username }}
mail_gid = {{ .Username }}

mail_location = maildir:{{ .HomeDir }}/%u
mail_home = {{ .HomeDir }}/%u/home

userdb {
  driver = static
  args =
}

passdb {
    driver = sql
    args = /etc/dovecot/vmail-sql.conf.ext
}

{{end}}{{define "dovecot_sql"}}
driver = sqlite
connect = {{ .HomeDir }}/vmail.sqlite
default_pass_scheme = SHA512-CRYPT

password_query = \
    SELECT passwd as password, name||'@'||domain as user FROM dest \
    WHERE name = '%n' AND domain = '%d' AND type = 1 AND enable = 1

{{end}}`))
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"os/user"
	"path"
	// "reflect"
	"strconv"
	"strings"
)

var (
	homeDir    string
	selfURL127 string // 127.0.0.1:listenAddr
	selfURLLH  string // localhost:listenAddr

	printVer bool
)

const (
	dotDir       = ".cow"
	blockedFname = "blocked"
	directFname  = "direct"
	rcFname      = "rc"

	version = "0.2.1"
)

var config struct {
	listenAddr string // server listen address
	socksAddr  string
	numProc    int    // max number of cores to use
	sshServer  string // ssh to the given server to start socks proxy

	dir         string // directory containing config file and blocked site list
	blockedFile string // contains blocked domains
	directFile  string // contains sites that can be directly accessed
	chouFile    string // chou feng, sites which will be temporary blocked
	rcFile      string

	updateBlocked bool // whether update blocked site list
	updateDirect  bool // whether update direct site list
}

func printVersion() {
	fmt.Println("cow-proxy version", version)
}

func init() {
	u, err := user.Current()
	if err != nil {
		errl.Printf("Can't get user information %v", err)
		os.Exit(1)
	}
	homeDir = u.HomeDir

	flag.BoolVar(&printVer, "version", false, "print version")

	flag.StringVar(&config.listenAddr, "listen", "127.0.0.1:7777", "proxy server listen address")
	flag.StringVar(&config.socksAddr, "socks", "127.0.0.1:1080", "socks server address")
	flag.IntVar(&config.numProc, "core", 2, "number of cores to use")
	flag.StringVar(&config.sshServer, "ssh_server", "", "remote server which will ssh to and provide sock server")

	flag.BoolVar(&config.updateBlocked, "update_blocked", true, "update blocked site list")
	flag.BoolVar(&config.updateDirect, "update_direct", true, "update direct site list")

	config.dir = path.Join(homeDir, dotDir)
	config.blockedFile = path.Join(config.dir, blockedFname)
	config.directFile = path.Join(config.dir, directFname)
	config.rcFile = path.Join(config.dir, rcFname)
}

// Tries to open a file, if file not exist, return both nil for os.File and
// err
func openFile(path string) (f *os.File, err error) {
	if f, err = os.Open(path); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		errl.Println("Error opening file:", path, err)
		return nil, err
	}
	return
}

func parseBool(v string, msg string) bool {
	switch v {
	case "true":
		return true
	case "false":
		return false
	default:
		fmt.Printf("%s should be true or false\n", msg)
		os.Exit(1)
	}
	return false
}

func parseConfig() {
	f, err := openFile(config.rcFile)
	if f == nil || err != nil {
		return
	}
	defer f.Close()

	fr := bufio.NewReader(f)

	var line string
	var n int
	for {
		n++
		line, err = ReadLine(fr)
		if err == io.EOF {
			return
		} else if err != nil {
			errl.Println("Error reading rc file:", err)
			errl.Println("Exit")
			os.Exit(1)
		}

		if line == "" {
			continue
		}

		line = strings.TrimSpace(line)
		// Ignore comment
		if line[0] == '#' {
			continue
		}

		v := strings.Split(line, "=")
		if len(v) != 2 {
			fmt.Println("Config error: syntax error on line", n)
			os.Exit(1)
		}
		key, val := strings.TrimSpace(v[0]), strings.TrimSpace(v[1])

		switch {
		case key == "listen":
			config.listenAddr = val
		case key == "core":
			config.numProc, err = strconv.Atoi(val)
			if err != nil {
				fmt.Printf("Config error: core number %d %v", n, err)
				os.Exit(1)
			}
		case key == "socks":
			_, port := splitHostPort(val)
			if port == "" {
				fmt.Println("socks server must have port specified")
				os.Exit(1)
			}
			config.socksAddr = val
		case key == "blocked":
			config.blockedFile = val
		case key == "ssh_server":
			config.sshServer = val
		case key == "update_blocked":
			config.updateBlocked = parseBool(val, "update_blocked")
		case key == "update_direct":
			config.updateDirect = parseBool(val, "update_direct")
		default:
			fmt.Println("Config error: no such option", key)
			os.Exit(1)
		}
	}
}

func loadConfig() {
	parseConfig()

	blockedDs.loadDomainList(config.blockedFile)
	directDs.loadDomainList(config.directFile)

	_, port := splitHostPort(config.listenAddr)
	selfURL127 = "127.0.0.1:" + port
	selfURLLH = "localhost:" + port
}
<commit_msg>Add pipeline option.<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"os/user"
	"path"
	// "reflect"
	"strconv"
	"strings"
)

var (
	homeDir    string
	selfURL127 string // 127.0.0.1:listenAddr
	selfURLLH  string // localhost:listenAddr

	printVer bool
)

const (
	dotDir       = ".cow"
	blockedFname = "blocked"
	directFname  = "direct"
	rcFname      = "rc"

	version = "0.2.1"
)

var config struct {
	listenAddr    string
	socksAddr     string
	numProc       int
	sshServer     string
	updateBlocked bool
	updateDirect  bool
	pipeline      bool

	// These are for internal use
	dir         string // directory containing config file and blocked site list
	blockedFile string // contains blocked domains
	directFile  string // contains sites that can be directly accessed
	chouFile    string // chou feng, sites which will be temporary blocked
	rcFile      string
}

func printVersion() {
	fmt.Println("cow-proxy version", version)
}

func init() {
	u, err := user.Current()
	if err != nil {
		errl.Printf("Can't get user information %v", err)
		os.Exit(1)
	}
	homeDir = u.HomeDir

	flag.BoolVar(&printVer, "version", false, "print version")

	flag.StringVar(&config.listenAddr, "listen", "127.0.0.1:7777", "proxy server listen address")
	flag.StringVar(&config.socksAddr, "socks", "127.0.0.1:1080", "socks server address")
	flag.IntVar(&config.numProc, "core", 2, "number of cores to use")
	flag.StringVar(&config.sshServer, "ssh_server", "", "remote server which will ssh to and provide sock server")

	flag.BoolVar(&config.updateBlocked, "update_blocked", true, "update blocked site list")
	flag.BoolVar(&config.updateDirect, "update_direct", true, "update direct site list")
	flag.BoolVar(&config.pipeline, "pipeline", true, "enable HTTP pipelining (experimental)")

	config.dir = path.Join(homeDir, dotDir)
	config.blockedFile = path.Join(config.dir, blockedFname)
	config.directFile = path.Join(config.dir, directFname)
	config.rcFile = path.Join(config.dir, rcFname)
}

// Tries to open a file, if file not exist, return both nil for os.File and
// err
func openFile(path string) (f *os.File, err error) {
	if f, err = os.Open(path); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		errl.Println("Error opening file:", path, err)
		return nil, err
	}
	return
}

func parseBool(v string, msg string) bool {
	switch v {
	case "true":
		return true
	case "false":
		return false
	default:
		fmt.Printf("%s should be true or false\n", msg)
		os.Exit(1)
	}
	return false
}

func parseConfig() {
	f, err := openFile(config.rcFile)
	if f == nil || err != nil {
		return
	}
	defer f.Close()

	fr := bufio.NewReader(f)

	var line string
	var n int
	for {
		n++
		line, err = ReadLine(fr)
		if err == io.EOF {
			return
		} else if err != nil {
			errl.Printf("Error reading rc file: %v\n", err)
			os.Exit(1)
		}

		if line == "" {
			continue
		}

		line = strings.TrimSpace(line)
		// Ignore comment
		if line[0] == '#' {
			continue
		}

		v := strings.Split(line, "=")
		if len(v) != 2 {
			fmt.Println("Config error: syntax error on line", n)
			os.Exit(1)
		}
		key, val := strings.TrimSpace(v[0]), strings.TrimSpace(v[1])

		switch {
		case key == "listen":
			config.listenAddr = val
		case key == "core":
			config.numProc, err = strconv.Atoi(val)
			if err != nil {
				fmt.Printf("Config error: core number %d %v", n, err)
				os.Exit(1)
			}
		case key == "socks":
			_, port := splitHostPort(val)
			if port == "" {
				fmt.Println("socks server must have port specified")
				os.Exit(1)
			}
			config.socksAddr = val
		case key == "blocked":
			config.blockedFile = val
		case key == "ssh_server":
			config.sshServer = val
		case key == "update_blocked":
			config.updateBlocked = parseBool(val, "update_blocked")
		case key == "update_direct":
			config.updateDirect = parseBool(val, "update_direct")
		case key == "pipeline":
			config.pipeline = parseBool(val, "pipeline")
		default:
			fmt.Println("Config error: no such option", key)
			os.Exit(1)
		}
	}
}

func loadConfig() {
	parseConfig()

	blockedDs.loadDomainList(config.blockedFile)
	directDs.loadDomainList(config.directFile)

	_, port := splitHostPort(config.listenAddr)
	selfURL127 = "127.0.0.1:" + port
	selfURLLH = "localhost:" + port
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"os"
	"sync"
)

type Server struct {
	ID         int
	Name, Path string
	Args       []string
	status     string
}

type Map struct {
	ID         int
	Name, Path string
	Status     string
}

type Config struct {
	mu         sync.RWMutex
	filename   string
	ServerName string
	ServersDir string
	Port       uint16
	Servers    map[int]Server
	Maps       map[int]Map
	selected   int
}

func loadConfig(filename string) (*Config, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	c := &Config{
		filename:   filename,
		ServerName: "Minecraft",
		Port:       8080,
		Servers:    make([]Server, 0),
		Maps:       make([]Map, 0),
		selected:   -1,
	}
	err = json.NewDecoder(f).Decode(c)
	if err != nil {
		return nil, err
	}
	return c, nil
}

func (c *Config) save() error {
	f, err := os.Create(c.filename)
	if err != nil {
		return err
	}
	defer f.Close()
	return json.NewEncoder(f).Encode(c)
}

func (c *Config) Name(_ struct{}, serverName *string) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	*serverName = c.ServerName
	return nil
}

func (c *Config) List(_ struct{}, list *[]Server) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	*list = make([]Server, 0, len(c.Servers))
	for _, s := range c.Servers {
		*list = append(*list, s)
	}
	return nil
}

func (c *Config) MapList(_ struct{}, list *[]Map) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	*list = make([]Map, 0, len(c.Maps))
	for _, m := range c.Maps {
		*list = append(*list, m)
	}
	return nil
}

func (c *Config) createServer(name, path string) int {
	c.mu.Lock()
	defer c.mu.Unlock()
	defer c.save()
	id := 0
	for {
		_, ok := c.Servers[id]
		if !ok {
			break
		}
		id++
	}
	c.Servers = append(c.Servers, Server{ID: id, Name: name, Path: path})
	return id
}

func (c *Config) newMap(name, path string) int {
	c.mu.Lock()
	defer c.mu.Unlock()
	defer c.save()
	id := 0
	for {
		_, ok := c.Maps[id]
		if !ok {
			break
		}
		id++
	}
	c.Maps = append(c.Maps, Map{ID: id, Name: name, Path: path})
	return id
}

func (c *Config) serverStatus(id int, status string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.Servers[id].status = status
}
<commit_msg>Fixed change of slice->map<commit_after>package main

import (
	"encoding/json"
	"os"
	"sync"
)

type Server struct {
	ID         int
	Name, Path string
	Args       []string
	status     string
}

type Map struct {
	ID         int
	Name, Path string
	Status     string
}

type Config struct {
	mu         sync.RWMutex
	filename   string
	ServerName string
	ServersDir string
	Port       uint16
	Servers    map[int]Server
	Maps       map[int]Map
	selected   int
}

func loadConfig(filename string) (*Config, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	c := &Config{
		filename:   filename,
		ServerName: "Minecraft",
		Port:       8080,
		Servers:    make(map[int]Server),
		Maps:       make(map[int]Map),
		selected:   -1,
	}
	err = json.NewDecoder(f).Decode(c)
	if err != nil {
		return nil, err
	}
	return c, nil
}

func (c *Config) save() error {
	f, err := os.Create(c.filename)
	if err != nil {
		return err
	}
	defer f.Close()
	return json.NewEncoder(f).Encode(c)
}

func (c *Config) Name(_ struct{}, serverName *string) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	*serverName = c.ServerName
	return nil
}

func (c *Config) List(_ struct{}, list *[]Server) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	*list = make([]Server, 0, len(c.Servers))
	for _, s := range c.Servers {
		*list = append(*list, s)
	}
	return nil
}

func (c *Config) MapList(_ struct{}, list *[]Map) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	*list = make([]Map, 0, len(c.Maps))
	for _, m := range c.Maps {
		*list = append(*list, m)
	}
	return nil
}

func (c *Config) createServer(name, path string) int {
	c.mu.Lock()
	defer c.mu.Unlock()
	defer c.save()
	id := 0
	for {
		_, ok := c.Servers[id]
		if !ok {
			break
		}
		id++
	}
	c.Servers[id] = Server{ID: id, Name: name, Path: path}
	return id
}

func (c *Config) newMap(name, path string) int {
	c.mu.Lock()
	defer c.mu.Unlock()
	defer c.save()
	id := 0
	for {
		_, ok := c.Maps[id]
		if !ok {
			break
		}
		id++
	}
	c.Maps[id] = Map{ID: id, Name: name, Path: path}
	return id
}

func (c *Config) serverStatus(id int, status string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	s, ok := c.Servers[id]
	if !ok {
		return
	}
	s.status = status
	c.Servers[id] = s
}
<|endoftext|>


<commit_before>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
	"os"
)

func Services() (map[string]*consul.AgentService, error) {
	client, err := NewConsulClient()
	if err != nil {
		return nil, err
	}
	return client.Agent().Services()
}

func ServiceRegister(name string, path string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: appEnv.ApplicationURIs[0],
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schemaForServices() + "://" + appEnv.ApplicationURIs[0] + path),
			Interval: "60s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

func NewConsulClient() (*consul.Client, error) {
	dialString, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialString,
		Scheme:  schemaForConsul(),
		Token:   os.Getenv("CONSUL_TOKEN"),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func consulDialstring(serviceName string) (string, error) {
	appEnv, _ := Current()
	consulService, err := appEnv.Services.WithName(serviceName)
	if err != nil {
		return "", err
	}
	port := "8500"

	uri, ok := consulService.Credentials["uri"].(string)
	if ok {
		u, err := url.Parse(uri)
		if err == nil {
			return fmt.Sprintf("%s", u.Host), nil
		}
		// Fallback to hostname/port lookup
	}

	hostname, ok := consulService.Credentials["hostname"].(string)
	if !ok {
		return "", errors.New("consul service not available")
	}
	port, ok = consulService.Credentials["port"].(string)
	if ok {
		return fmt.Sprintf("%s:%s", hostname, port), nil
	}
	return fmt.Sprintf("%s:8500", hostname), nil
}

func schemaForServices() string {
	if ForceHTTP() {
		return "http"
	}
	return "https"
}

func schemaForConsul() string {
	return "http"
}
<commit_msg>Emit scheme too<commit_after>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
	"os"
)

func Services() (map[string]*consul.AgentService, error) {
	client, err := NewConsulClient()
	if err != nil {
		return nil, err
	}
	return client.Agent().Services()
}

func ServiceRegister(name string, path string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: appEnv.ApplicationURIs[0],
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schemaForServices() + "://" + appEnv.ApplicationURIs[0] + path),
			Interval: "60s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

func NewConsulClient() (*consul.Client, error) {
	dialScheme, dialHost, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialHost,
		Scheme:  dialScheme,
		Token:   os.Getenv("CONSUL_TOKEN"),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func consulDialstring(serviceName string) (string, string, error) {
	if consulMaster := os.Getenv("CONSUL_MASTER"); consulMaster != "" {
		parsed, err := url.Parse(consulMaster)
		if err == nil {
			return parsed.Scheme, parsed.Host, nil
		}
	}
	// Fallack to Consul service. Deprecated
	appEnv, _ := Current()
	consulService, err := appEnv.Services.WithName(serviceName)
	if err != nil {
		return schemaForConsul(), "", err
	}
	port := "8500"

	uri, ok := consulService.Credentials["uri"].(string)
	if ok {
		u, err := url.Parse(uri)
		if err == nil {
			return schemaForConsul(), fmt.Sprintf("%s", u.Host), nil
		}
		// Fallback to hostname/port lookup
	}

	hostname, ok := consulService.Credentials["hostname"].(string)
	if !ok {
		return schemaForConsul(), "", errors.New("consul service not available")
	}
	port, ok = consulService.Credentials["port"].(string)
	if ok {
		return schemaForConsul(), fmt.Sprintf("%s:%s", hostname, port), nil
	}
	return schemaForConsul(), fmt.Sprintf("%s:8500", hostname), nil
}

func schemaForServices() string {
	if ForceHTTP() {
		return "http"
	}
	return "https"
}

func schemaForConsul() string {
	return "http"
}
<|endoftext|>


<commit_before>package copier

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"reflect"
	"strings"
)

// These flags define options for tag handling
const (
	// Denotes that a destination field must be copied to. If copying fails then a panic will ensue.
	tagMust uint8 = 1 << iota

	// Denotes that the program should not panic when the must flag is on and
	// value is not copied. The program will return an error instead.
	tagNoPanic

	// Ignore a destation field from being copied to.
	tagIgnore

	// Denotes that the value as been copied
	hasCopied
)

// Option sets copy options
type Option struct {
	IgnoreEmpty bool
	DeepCopy    bool
}

// Copy copy things
func Copy(toValue interface{}, fromValue interface{}) (err error) {
	return copier(toValue, fromValue, Option{})
}

// CopyWithOption copy with option
func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error) {
	return copier(toValue, fromValue, opt)
}

func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) {
	var (
		isSlice bool
		amount  = 1
		from    = indirect(reflect.ValueOf(fromValue))
		to      = indirect(reflect.ValueOf(toValue))
	)

	if !to.CanAddr() {
		return ErrInvalidCopyDestination
	}

	// Return is from value is invalid
	if !from.IsValid() {
		return ErrInvalidCopyFrom
	}

	fromType, isPtrFrom := indirectType(from.Type())
	toType, _ := indirectType(to.Type())

	if fromType.Kind() == reflect.Interface {
		fromType = reflect.TypeOf(from.Interface())
	}

	if toType.Kind() == reflect.Interface {
		toType = reflect.TypeOf(to.Interface())
	}

	// Just set it if possible to assign for normal types
	if from.Kind() != reflect.Slice && from.Kind() != reflect.Struct && from.Kind() != reflect.Map && (from.Type().AssignableTo(to.Type()) || from.Type().ConvertibleTo(to.Type())) {
		if !isPtrFrom || !opt.DeepCopy {
			to.Set(from.Convert(to.Type()))
		} else {
			fromCopy := reflect.New(from.Type())
			fromCopy.Set(from.Elem())
			to.Set(fromCopy.Convert(to.Type()))
		}
		return
	}

	if fromType.Kind() == reflect.Map && toType.Kind() == reflect.Map {
		if !fromType.Key().ConvertibleTo(toType.Key()) {
			return ErrMapKeyNotMatch
		}

		if to.IsNil() {
			to.Set(reflect.MakeMapWithSize(toType, from.Len()))
		}

		for _, k := range from.MapKeys() {
			toKey := indirect(reflect.New(toType.Key()))
			if !set(toKey, k, opt.DeepCopy) {
				return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
			}

			elemType, _ := indirectType(toType.Elem())
			toValue := indirect(reflect.New(elemType))
			if !set(toValue, from.MapIndex(k), opt.DeepCopy) {
				if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
					return err
				}
			}

			for {
				if elemType == toType.Elem() {
					to.SetMapIndex(toKey, toValue)
					break
				}
				elemType = reflect.PtrTo(elemType)
				toValue = toValue.Addr()
			}
		}
		return
	}

	if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice && fromType.ConvertibleTo(toType) {
		if to.IsNil() {
			slice := reflect.MakeSlice(reflect.SliceOf(to.Type().Elem()), from.Len(), from.Cap())
			to.Set(slice)
		}
		for i := 0; i < from.Len(); i++ {
			if !set(to.Index(i), from.Index(i), opt.DeepCopy) {
				err = CopyWithOption(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
				if err != nil {
					continue
				}
			}
		}
		return
	}

	if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
		// skip not supported type
		return
	}

	if to.Kind() == reflect.Slice {
		isSlice = true
		if from.Kind() == reflect.Slice {
			amount = from.Len()
		}
	}

	for i := 0; i < amount; i++ {
		var dest, source reflect.Value

		if isSlice {
			// source
			if from.Kind() == reflect.Slice {
				source = indirect(from.Index(i))
			} else {
				source = indirect(from)
			}
			// dest
			dest = indirect(reflect.New(toType).Elem())
		} else {
			source = indirect(from)
			dest = indirect(to)
		}

		destKind := dest.Kind()
		initDest := false
		if destKind == reflect.Interface {
			initDest = true
			dest = indirect(reflect.New(toType))
		}

		// Get tag options
		tagBitFlags := map[string]uint8{}
		if dest.IsValid() {
			tagBitFlags = getBitFlags(toType)
		}

		// check source
		if source.IsValid() {
			// Copy from source field to dest field or method
			fromTypeFields := deepFields(fromType)
			for _, field := range fromTypeFields {
				name := field.Name

				// Get bit flags for field
				fieldFlags, _ := tagBitFlags[name]

				// Check if we should ignore copying
				if (fieldFlags & tagIgnore) != 0 {
					continue
				}

				if fromField := source.FieldByName(name); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) {
					// process for nested anonymous field
					destFieldNotSet := false
					if f, ok := dest.Type().FieldByName(name); ok {
						for idx, x := range f.Index {
							destFieldKind := dest.Field(x).Kind()
							if destFieldKind != reflect.Ptr {
								continue
							}

							if !dest.Field(x).IsNil() {
								continue
							}

							if !dest.Field(x).CanSet() {
								destFieldNotSet = true
								break
							}

							newValue := reflect.New(dest.FieldByIndex(f.Index[0 : idx+1]).Type().Elem())
							dest.Field(x).Set(newValue)
						}
					}

					if destFieldNotSet {
						break
					}

					toField := dest.FieldByName(name)
					if toField.IsValid() {
						if toField.CanSet() {
							if !set(toField, fromField, opt.DeepCopy) {
								if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
									return err
								}
							} else {
								if fieldFlags != 0 {
									// Note that a copy was made
									tagBitFlags[name] = fieldFlags | hasCopied
								}
							}
						}
					} else {
						// try to set to method
						var toMethod reflect.Value
						if dest.CanAddr() {
							toMethod = dest.Addr().MethodByName(name)
						} else {
							toMethod = dest.MethodByName(name)
						}

						if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
							toMethod.Call([]reflect.Value{fromField})
						}
					}
				}
			}

			// Copy from from method to dest field
			for _, field := range deepFields(toType) {
				name := field.Name

				var fromMethod reflect.Value
				if source.CanAddr() {
					fromMethod = source.Addr().MethodByName(name)
				} else {
					fromMethod = source.MethodByName(name)
				}

				if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, opt.IgnoreEmpty) {
					if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
						values := fromMethod.Call([]reflect.Value{})
						if len(values) >= 1 {
							set(toField, values[0], opt.DeepCopy)
						}
					}
				}
			}
		}

		if isSlice {
			if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
				to.Set(reflect.Append(to, dest.Addr()))
			} else if dest.Type().AssignableTo(to.Type().Elem()) {
				to.Set(reflect.Append(to, dest))
			}
		} else if initDest {
			to.Set(dest)
		}

		err = checkBitFlags(tagBitFlags)
	}

	return
}

func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
	if !ignoreEmpty {
		return false
	}

	return v.IsZero()
}

func deepFields(reflectType reflect.Type) []reflect.StructField {
	if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
		fields := make([]reflect.StructField, 0, reflectType.NumField())

		for i := 0; i < reflectType.NumField(); i++ {
			v := reflectType.Field(i)
			if v.Anonymous {
				fields = append(fields, deepFields(v.Type)...)
			} else {
				fields = append(fields, v)
			}
		}

		return fields
	}

	return nil
}

func indirect(reflectValue reflect.Value) reflect.Value {
	for reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}
	return reflectValue
}

func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
	for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
		reflectType = reflectType.Elem()
		isPtr = true
	}
	return reflectType, isPtr
}

func set(to, from reflect.Value, deepCopy bool) bool {
	if from.IsValid() {
		if to.Kind() == reflect.Ptr {
			// set `to` to nil if from is nil
			if from.Kind() == reflect.Ptr && from.IsNil() {
				to.Set(reflect.Zero(to.Type()))
				return true
			} else if to.IsNil() {
				// `from`         -> `to`
				// sql.NullString -> *string
				if fromValuer, ok := driverValuer(from); ok {
					v, err := fromValuer.Value()
					if err != nil {
						return false
					}
					// if `from` is not valid do nothing with `to`
					if v == nil {
						return true
					}
				}
				// allocate new `to` variable with default value (eg. *string -> new(string))
				to.Set(reflect.New(to.Type().Elem()))
			}
			// depointer `to`
			to = to.Elem()
		}

		if deepCopy {
			toKind := to.Kind()
			if toKind == reflect.Interface && to.IsNil() {
				to.Set(reflect.New(reflect.TypeOf(from.Interface())).Elem())
				toKind = reflect.TypeOf(to.Interface()).Kind()
			}
			if toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice {
				return false
			}
		}

		if from.Type().ConvertibleTo(to.Type()) {
			to.Set(from.Convert(to.Type()))
		} else if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
			// `from`  -> `to`
			// *string -> sql.NullString
			if from.Kind() == reflect.Ptr {
				// if `from` is nil do nothing with `to`
				if from.IsNil() {
					return true
				}
				// depointer `from`
				from = indirect(from)
			}
			// `from` -> `to`
			// string -> sql.NullString
			// set `to` by invoking method Scan(`from`)
			err := toScanner.Scan(from.Interface())
			if err != nil {
				return false
			}
		} else if fromValuer, ok := driverValuer(from); ok {
			// `from`         -> `to`
			// sql.NullString -> string
			v, err := fromValuer.Value()
			if err != nil {
				return false
			}
			// if `from` is not valid do nothing with `to`
			if v == nil {
				return true
			}
			rv := reflect.ValueOf(v)
			if rv.Type().AssignableTo(to.Type()) {
				to.Set(rv)
			}
		} else if from.Kind() == reflect.Ptr {
			return set(to, from.Elem(), deepCopy)
		} else {
			return false
		}
	}

	return true
}

// parseTags Parses struct tags and returns uint8 bit flags.
func parseTags(tag string) (flags uint8) {
	for _, t := range strings.Split(tag, ",") {
		switch t {
		case "-":
			flags = tagIgnore
			return
		case "must":
			flags = flags | tagMust
		case "nopanic":
			flags = flags | tagNoPanic
		}
	}
	return
}

// getBitFlags Parses struct tags for bit flags.
func getBitFlags(toType reflect.Type) map[string]uint8 {
	flags := map[string]uint8{}
	toTypeFields := deepFields(toType)

	// Get a list dest of tags
	for _, field := range toTypeFields {
		tags := field.Tag.Get("copier")
		if tags != "" {
			flags[field.Name] = parseTags(tags)
		}
	}
	return flags
}

// checkBitFlags Checks flags for error or panic conditions.
func checkBitFlags(flagsList map[string]uint8) (err error) {
	// Check flag conditions were met
	for name, flags := range flagsList {
		if flags&hasCopied == 0 {
			switch {
			case flags&tagMust != 0 && flags&tagNoPanic != 0:
				err = fmt.Errorf("Field %s has must tag but was not copied", name)
				return
			case flags&(tagMust) != 0:
				panic(fmt.Sprintf("Field %s has must tag but was not copied", name))
			}
		}
	}
	return
}

func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {

	if !v.CanAddr() {
		i, ok = v.Interface().(driver.Valuer)
		return
	}

	i, ok = v.Addr().Interface().(driver.Valuer)
	return
}
<commit_msg>[fix] #80 check if field is existsing before access<commit_after>package copier

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"reflect"
	"strings"
)

// These flags define options for tag handling
const (
	// Denotes that a destination field must be copied to. If copying fails then a panic will ensue.
	tagMust uint8 = 1 << iota

	// Denotes that the program should not panic when the must flag is on and
	// value is not copied. The program will return an error instead.
	tagNoPanic

	// Ignore a destation field from being copied to.
	tagIgnore

	// Denotes that the value as been copied
	hasCopied
)

// Option sets copy options
type Option struct {
	IgnoreEmpty bool
	DeepCopy    bool
}

// Copy copy things
func Copy(toValue interface{}, fromValue interface{}) (err error) {
	return copier(toValue, fromValue, Option{})
}

// CopyWithOption copy with option
func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error) {
	return copier(toValue, fromValue, opt)
}

func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) {
	var (
		isSlice bool
		amount  = 1
		from    = indirect(reflect.ValueOf(fromValue))
		to      = indirect(reflect.ValueOf(toValue))
	)

	if !to.CanAddr() {
		return ErrInvalidCopyDestination
	}

	// Return is from value is invalid
	if !from.IsValid() {
		return ErrInvalidCopyFrom
	}

	fromType, isPtrFrom := indirectType(from.Type())
	toType, _ := indirectType(to.Type())

	if fromType.Kind() == reflect.Interface {
		fromType = reflect.TypeOf(from.Interface())
	}

	if toType.Kind() == reflect.Interface {
		toType = reflect.TypeOf(to.Interface())
	}

	// Just set it if possible to assign for normal types
	if from.Kind() != reflect.Slice && from.Kind() != reflect.Struct && from.Kind() != reflect.Map && (from.Type().AssignableTo(to.Type()) || from.Type().ConvertibleTo(to.Type())) {
		if !isPtrFrom || !opt.DeepCopy {
			to.Set(from.Convert(to.Type()))
		} else {
			fromCopy := reflect.New(from.Type())
			fromCopy.Set(from.Elem())
			to.Set(fromCopy.Convert(to.Type()))
		}
		return
	}

	if fromType.Kind() == reflect.Map && toType.Kind() == reflect.Map {
		if !fromType.Key().ConvertibleTo(toType.Key()) {
			return ErrMapKeyNotMatch
		}

		if to.IsNil() {
			to.Set(reflect.MakeMapWithSize(toType, from.Len()))
		}

		for _, k := range from.MapKeys() {
			toKey := indirect(reflect.New(toType.Key()))
			if !set(toKey, k, opt.DeepCopy) {
				return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
			}

			elemType, _ := indirectType(toType.Elem())
			toValue := indirect(reflect.New(elemType))
			if !set(toValue, from.MapIndex(k), opt.DeepCopy) {
				if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
					return err
				}
			}

			for {
				if elemType == toType.Elem() {
					to.SetMapIndex(toKey, toValue)
					break
				}
				elemType = reflect.PtrTo(elemType)
				toValue = toValue.Addr()
			}
		}
		return
	}

	if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice && fromType.ConvertibleTo(toType) {
		if to.IsNil() {
			slice := reflect.MakeSlice(reflect.SliceOf(to.Type().Elem()), from.Len(), from.Cap())
			to.Set(slice)
		}
		for i := 0; i < from.Len(); i++ {
			if !set(to.Index(i), from.Index(i), opt.DeepCopy) {
				err = CopyWithOption(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
				if err != nil {
					continue
				}
			}
		}
		return
	}

	if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
		// skip not supported type
		return
	}

	if to.Kind() == reflect.Slice {
		isSlice = true
		if from.Kind() == reflect.Slice {
			amount = from.Len()
		}
	}

	for i := 0; i < amount; i++ {
		var dest, source reflect.Value

		if isSlice {
			// source
			if from.Kind() == reflect.Slice {
				source = indirect(from.Index(i))
			} else {
				source = indirect(from)
			}
			// dest
			dest = indirect(reflect.New(toType).Elem())
		} else {
			source = indirect(from)
			dest = indirect(to)
		}

		destKind := dest.Kind()
		initDest := false
		if destKind == reflect.Interface {
			initDest = true
			dest = indirect(reflect.New(toType))
		}

		// Get tag options
		tagBitFlags := map[string]uint8{}
		if dest.IsValid() {
			tagBitFlags = getBitFlags(toType)
		}

		// check source
		if source.IsValid() {
			// Copy from source field to dest field or method
			fromTypeFields := deepFields(fromType)
			for _, field := range fromTypeFields {
				name := field.Name

				// Get bit flags for field
				fieldFlags, _ := tagBitFlags[name]

				// Check if we should ignore copying
				if (fieldFlags & tagIgnore) != 0 {
					continue
				}

				if fromField := source.FieldByName(name); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) {
					// process for nested anonymous field
					destFieldNotSet := false
					if f, ok := dest.Type().FieldByName(name); ok {
						for idx, x := range f.Index {
							if x >= dest.NumField() {
								continue
							}

							destFieldKind := dest.Field(x).Kind()
							if destFieldKind != reflect.Ptr {
								continue
							}

							if !dest.Field(x).IsNil() {
								continue
							}

							if !dest.Field(x).CanSet() {
								destFieldNotSet = true
								break
							}

							newValue := reflect.New(dest.FieldByIndex(f.Index[0 : idx+1]).Type().Elem())
							dest.Field(x).Set(newValue)
						}
					}

					if destFieldNotSet {
						break
					}

					toField := dest.FieldByName(name)
					if toField.IsValid() {
						if toField.CanSet() {
							if !set(toField, fromField, opt.DeepCopy) {
								if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
									return err
								}
							} else {
								if fieldFlags != 0 {
									// Note that a copy was made
									tagBitFlags[name] = fieldFlags | hasCopied
								}
							}
						}
					} else {
						// try to set to method
						var toMethod reflect.Value
						if dest.CanAddr() {
							toMethod = dest.Addr().MethodByName(name)
						} else {
							toMethod = dest.MethodByName(name)
						}

						if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
							toMethod.Call([]reflect.Value{fromField})
						}
					}
				}
			}

			// Copy from from method to dest field
			for _, field := range deepFields(toType) {
				name := field.Name

				var fromMethod reflect.Value
				if source.CanAddr() {
					fromMethod = source.Addr().MethodByName(name)
				} else {
					fromMethod = source.MethodByName(name)
				}

				if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, opt.IgnoreEmpty) {
					if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
						values := fromMethod.Call([]reflect.Value{})
						if len(values) >= 1 {
							set(toField, values[0], opt.DeepCopy)
						}
					}
				}
			}
		}

		if isSlice {
			if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
				to.Set(reflect.Append(to, dest.Addr()))
			} else if dest.Type().AssignableTo(to.Type().Elem()) {
				to.Set(reflect.Append(to, dest))
			}
		} else if initDest {
			to.Set(dest)
		}

		err = checkBitFlags(tagBitFlags)
	}

	return
}

func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
	if !ignoreEmpty {
		return false
	}

	return v.IsZero()
}

func deepFields(reflectType reflect.Type) []reflect.StructField {
	if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
		fields := make([]reflect.StructField, 0, reflectType.NumField())

		for i := 0; i < reflectType.NumField(); i++ {
			v := reflectType.Field(i)
			if v.Anonymous {
				fields = append(fields, deepFields(v.Type)...)
			} else {
				fields = append(fields, v)
			}
		}

		return fields
	}

	return nil
}

func indirect(reflectValue reflect.Value) reflect.Value {
	for reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}
	return reflectValue
}

func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
	for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
		reflectType = reflectType.Elem()
		isPtr = true
	}
	return reflectType, isPtr
}

func set(to, from reflect.Value, deepCopy bool) bool {
	if from.IsValid() {
		if to.Kind() == reflect.Ptr {
			// set `to` to nil if from is nil
			if from.Kind() == reflect.Ptr && from.IsNil() {
				to.Set(reflect.Zero(to.Type()))
				return true
			} else if to.IsNil() {
				// `from`         -> `to`
				// sql.NullString -> *string
				if fromValuer, ok := driverValuer(from); ok {
					v, err := fromValuer.Value()
					if err != nil {
						return false
					}
					// if `from` is not valid do nothing with `to`
					if v == nil {
						return true
					}
				}
				// allocate new `to` variable with default value (eg. *string -> new(string))
				to.Set(reflect.New(to.Type().Elem()))
			}
			// depointer `to`
			to = to.Elem()
		}

		if deepCopy {
			toKind := to.Kind()
			if toKind == reflect.Interface && to.IsNil() {
				to.Set(reflect.New(reflect.TypeOf(from.Interface())).Elem())
				toKind = reflect.TypeOf(to.Interface()).Kind()
			}
			if toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice {
				return false
			}
		}

		if from.Type().ConvertibleTo(to.Type()) {
			to.Set(from.Convert(to.Type()))
		} else if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
			// `from`  -> `to`
			// *string -> sql.NullString
			if from.Kind() == reflect.Ptr {
				// if `from` is nil do nothing with `to`
				if from.IsNil() {
					return true
				}
				// depointer `from`
				from = indirect(from)
			}
			// `from` -> `to`
			// string -> sql.NullString
			// set `to` by invoking method Scan(`from`)
			err := toScanner.Scan(from.Interface())
			if err != nil {
				return false
			}
		} else if fromValuer, ok := driverValuer(from); ok {
			// `from`         -> `to`
			// sql.NullString -> string
			v, err := fromValuer.Value()
			if err != nil {
				return false
			}
			// if `from` is not valid do nothing with `to`
			if v == nil {
				return true
			}
			rv := reflect.ValueOf(v)
			if rv.Type().AssignableTo(to.Type()) {
				to.Set(rv)
			}
		} else if from.Kind() == reflect.Ptr {
			return set(to, from.Elem(), deepCopy)
		} else {
			return false
		}
	}

	return true
}

// parseTags Parses struct tags and returns uint8 bit flags.
func parseTags(tag string) (flags uint8) {
	for _, t := range strings.Split(tag, ",") {
		switch t {
		case "-":
			flags = tagIgnore
			return
		case "must":
			flags = flags | tagMust
		case "nopanic":
			flags = flags | tagNoPanic
		}
	}
	return
}

// getBitFlags Parses struct tags for bit flags.
func getBitFlags(toType reflect.Type) map[string]uint8 {
	flags := map[string]uint8{}
	toTypeFields := deepFields(toType)

	// Get a list dest of tags
	for _, field := range toTypeFields {
		tags := field.Tag.Get("copier")
		if tags != "" {
			flags[field.Name] = parseTags(tags)
		}
	}
	return flags
}

// checkBitFlags Checks flags for error or panic conditions.
func checkBitFlags(flagsList map[string]uint8) (err error) {
	// Check flag conditions were met
	for name, flags := range flagsList {
		if flags&hasCopied == 0 {
			switch {
			case flags&tagMust != 0 && flags&tagNoPanic != 0:
				err = fmt.Errorf("Field %s has must tag but was not copied", name)
				return
			case flags&(tagMust) != 0:
				panic(fmt.Sprintf("Field %s has must tag but was not copied", name))
			}
		}
	}
	return
}

func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {

	if !v.CanAddr() {
		i, ok = v.Interface().(driver.Valuer)
		return
	}

	i, ok = v.Addr().Interface().(driver.Valuer)
	return
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The DevMine 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 (
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/golang/glog"
	_ "github.com/lib/pq"

	"github.com/DevMine/crawld/config"
	"github.com/DevMine/crawld/crawlers"
	"github.com/DevMine/crawld/repo"
	"github.com/DevMine/crawld/tar"
)

func crawlingWorker(cs []crawlers.Crawler, crawlingInterval time.Duration) {
	for {
		var wg sync.WaitGroup

		wg.Add(len(cs))
		for _, c := range cs {
			glog.Infof("starting a goroutine for the %v crawler\n", reflect.TypeOf(c))
			go func(c crawlers.Crawler) {
				defer wg.Done()
				c.Crawl()
			}(c)
		}

		wg.Wait()

		glog.Infof("waiting for %v before re-starting the crawlers.\n", crawlingInterval)
		<-time.After(crawlingInterval)
	}
}

// fetcherCall shall be used to call fetcher functions that might return
// ErrNetwork or ErrNoSpace. These typically include repo.Clone() and
// repo.Update().
// Be aware that fetcherCall will never return until a network error
// occurs or if there is no space left on device. Restoring the network
// connexion or freeing up some space will allow it to return.
func fetcherCall(fct func() error) error {
	var err error
	for {
		if err = fct(); err == repo.ErrNetwork || err == repo.ErrNoSpace {
			// there is no point trying to continue running right now
			// hence, wait a moment and try again
			glog.Error("critical error encountered (" + err.Error() + "), waiting for 1 hour before resuming")
			time.Sleep(time.Hour)
		} else {
			return err
		}
	}
}

func repoWorker(db *sql.DB, langs []string, basePath string, fetchInterval time.Duration, useTar bool) {
	clone := func(r repo.Repo) {
		glog.Infof("cloning %s into %s\n", r.URL(), r.AbsPath())
		if err := fetcherCall(r.Clone); err != nil {
			glog.Errorf("impossible to clone %s in %s ("+err.Error()+") skipping", r.URL(), r.AbsPath())
		}
	}

	update := func(r repo.Repo) {
		glog.Infof("updating %s\n", r.AbsPath())
		if err := fetcherCall(r.Update); err != nil {
			// delete and reclone then
			glog.Warningf("impossible to update %s ("+err.Error()+") => attempting to re-clone", r.AbsPath())
			if err2 := os.RemoveAll(r.AbsPath()); err2 != nil {
				glog.Errorf("cannot remove %s("+err2.Error()+")", r.AbsPath())
			} else {
				clone(r)
			}
		}
	}

	createArchive := func(path string) {
		if err := tar.CreateInPlace(path); err != nil {
			glog.Error("impossible to create the tar archive (" + path + ".tar ): " +
				err.Error())
		}
	}

	for {
		glog.Info("starting the repositories fetcher")
		repos, err := getAllRepos(db, langs, basePath)
		if err != nil {
			fatal(err)
		}

		for _, r := range repos {
			// check if we have a tar archive of the repository in which case
			// we only need to update but extract apriori and recreate the tar
			// archive afterwards
			archive := r.AbsPath() + ".tar"
			if _, err = os.Stat(archive); err == nil {
				if err = tar.ExtractInPlace(archive); err != nil {
					glog.Warning("impossible to extract the tar archive (" + r.AbsPath() + ".tar )" +
						", cannot update the repository: " + err.Error())
					// attempt to remove the eventual mess
					_ = os.Remove(archive)
					_ = os.RemoveAll(r.AbsPath())
					clone(r)
				} else {
					update(r)
				}
			} else {
				if _, err := os.Stat(r.AbsPath()); os.IsNotExist(err) || isDirEmpty(r.AbsPath()) {
					clone(r)
				} else {
					update(r)
				}
			}

			if useTar {
				createArchive(r.AbsPath())
			}

			if err = r.Cleanup(); err != nil {
				glog.Warning(err)
			}
		}

		glog.Infof("waiting for %v before re-starting the fetcher.\n", fetchInterval)
		<-time.After(fetchInterval)
	}
}

func isDirEmpty(path string) bool {
	fis, err := ioutil.ReadDir(path)
	if err != nil {
		return false
	}

	return len(fis) == 0
}

func getAllRepos(db *sql.DB, langs []string, basePath string) ([]repo.Repo, error) {
	var inClause string
	if langs != nil && len(langs) > 0 {
		// Quote languages.
		for idx, val := range langs {
			langs[idx] = "'" + val + "'"
		}
		inClause = " WHERE LOWER(primary_language) IN (" + strings.Join(langs, ",") + ")"
	}

	rows, err := db.Query("SELECT vcs, clone_path, clone_url FROM repositories" + inClause)
	if err != nil {
		glog.Error(err)
		return nil, err
	}
	defer rows.Close()

	var repos []repo.Repo

	for rows.Next() {
		var vcs, clonePath, cloneURL string
		if err := rows.Scan(&vcs, &clonePath, &cloneURL); err != nil {
			glog.Error(err)
			continue
		}

		var newRepo repo.Repo
		var err error

		newRepo, err = repo.New(vcs, filepath.Join(basePath, clonePath), cloneURL)
		if err != nil {
			glog.Error(err)
			continue
		}

		repos = append(repos, newRepo)
	}

	return repos, nil
}

func checkCloneDir(cloneDir string) error {
	// check if clone path exists
	if fi, err := os.Stat(cloneDir); err == nil {
		glog.Error(err)
		return err
	} else if !fi.IsDir() {
		err = errors.New("clone path must be a directory")
		glog.Error(err)
		return err
	}

	// check if clone path is writable
	// note: since the directory already exists, then the file perm param is
	// useless
	file, err := os.OpenFile(cloneDir, os.O_RDWR, 0770)
	if err != nil {
		err = errors.New("clone path must be writable")
		glog.Error(err)
		return err
	}
	file.Close()

	return nil
}

func openDBSession(cfg config.DatabaseConfig) (*sql.DB, error) {
	dbURL := fmt.Sprintf(
		"user='%s' password='%s' host='%s' port=%d dbname='%s' sslmode='%s'",
		cfg.UserName, cfg.Password, cfg.HostName, cfg.Port, cfg.DBName, cfg.SSLMode)

	return sql.Open("postgres", dbURL)
}

func fatal(a ...interface{}) {
	glog.Error(a)
	os.Exit(1)
}

func main() {
	configPath := flag.String("c", "", "configuration file")
	disableCrawlers := flag.Bool("disable-crawlers", false, "disable the data crawlers")
	disableFetcher := flag.Bool("disable-fetcher", false, "disable the repositories fetcher")
	flag.Parse()

	// Make sure we finish writing logs before exiting.
	defer glog.Flush()

	if len(*configPath) == 0 {
		fatal("no configuration specified")
	}

	cfg, err := config.ReadConfig(*configPath)
	if err != nil {
		fatal(err)
	}

	db, err := openDBSession(cfg.Database)
	if err != nil {
		fatal(err)
	}
	defer db.Close()

	var cs []crawlers.Crawler

	for _, crawlerConfig := range cfg.Crawlers {
		c, err := crawlers.New(crawlerConfig, db)
		if err != nil {
			fatal(err)
		}

		cs = append(cs, c)
	}

	crawlingInterval, err := time.ParseDuration(cfg.CrawlingTimeInterval)
	if err != nil {
		fatal(err)
	}

	var wg sync.WaitGroup

	// start the crawling worker
	if !*disableCrawlers {
		wg.Add(1)
		go crawlingWorker(cs, crawlingInterval)
	}

	fetchInterval, err := time.ParseDuration(cfg.FetchTimeInterval)
	if err != nil {
		fatal(err)
	}

	// start the repo puller worker
	if !*disableFetcher {
		wg.Add(1)
		go repoWorker(db, cfg.FetchLanguages, cfg.CloneDir, fetchInterval, cfg.TarRepos)
	}

	// wait until the cows come home saint
	wg.Wait()
}
<commit_msg>repo: use correct variable in log message<commit_after>// Copyright 2014-2015 The DevMine 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 (
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/golang/glog"
	_ "github.com/lib/pq"

	"github.com/DevMine/crawld/config"
	"github.com/DevMine/crawld/crawlers"
	"github.com/DevMine/crawld/repo"
	"github.com/DevMine/crawld/tar"
)

func crawlingWorker(cs []crawlers.Crawler, crawlingInterval time.Duration) {
	for {
		var wg sync.WaitGroup

		wg.Add(len(cs))
		for _, c := range cs {
			glog.Infof("starting a goroutine for the %v crawler\n", reflect.TypeOf(c))
			go func(c crawlers.Crawler) {
				defer wg.Done()
				c.Crawl()
			}(c)
		}

		wg.Wait()

		glog.Infof("waiting for %v before re-starting the crawlers.\n", crawlingInterval)
		<-time.After(crawlingInterval)
	}
}

// fetcherCall shall be used to call fetcher functions that might return
// ErrNetwork or ErrNoSpace. These typically include repo.Clone() and
// repo.Update().
// Be aware that fetcherCall will never return until a network error
// occurs or if there is no space left on device. Restoring the network
// connexion or freeing up some space will allow it to return.
func fetcherCall(fct func() error) error {
	var err error
	for {
		if err = fct(); err == repo.ErrNetwork || err == repo.ErrNoSpace {
			// there is no point trying to continue running right now
			// hence, wait a moment and try again
			glog.Error("critical error encountered (" + err.Error() + "), waiting for 1 hour before resuming")
			time.Sleep(time.Hour)
		} else {
			return err
		}
	}
}

func repoWorker(db *sql.DB, langs []string, basePath string, fetchInterval time.Duration, useTar bool) {
	clone := func(r repo.Repo) {
		glog.Infof("cloning %s into %s\n", r.URL(), r.AbsPath())
		if err := fetcherCall(r.Clone); err != nil {
			glog.Errorf("impossible to clone %s in %s ("+err.Error()+") skipping", r.URL(), r.AbsPath())
		}
	}

	update := func(r repo.Repo) {
		glog.Infof("updating %s\n", r.AbsPath())
		if err := fetcherCall(r.Update); err != nil {
			// delete and reclone then
			glog.Warningf("impossible to update %s ("+err.Error()+") => attempting to re-clone", r.AbsPath())
			if err2 := os.RemoveAll(r.AbsPath()); err2 != nil {
				glog.Errorf("cannot remove %s("+err2.Error()+")", r.AbsPath())
			} else {
				clone(r)
			}
		}
	}

	createArchive := func(path string) {
		if err := tar.CreateInPlace(path); err != nil {
			glog.Error("impossible to create the tar archive (" + path + ".tar ): " +
				err.Error())
		}
	}

	for {
		glog.Info("starting the repositories fetcher")
		repos, err := getAllRepos(db, langs, basePath)
		if err != nil {
			fatal(err)
		}

		for _, r := range repos {
			// check if we have a tar archive of the repository in which case
			// we only need to update but extract apriori and recreate the tar
			// archive afterwards
			archive := r.AbsPath() + ".tar"
			if _, err = os.Stat(archive); err == nil {
				if err = tar.ExtractInPlace(archive); err != nil {
					glog.Warning("impossible to extract the tar archive (" + archive + ")" +
						", cannot update the repository: " + err.Error())
					// attempt to remove the eventual mess
					_ = os.Remove(archive)
					_ = os.RemoveAll(r.AbsPath())
					clone(r)
				} else {
					update(r)
				}
			} else {
				if _, err := os.Stat(r.AbsPath()); os.IsNotExist(err) || isDirEmpty(r.AbsPath()) {
					clone(r)
				} else {
					update(r)
				}
			}

			if useTar {
				createArchive(r.AbsPath())
			}

			if err = r.Cleanup(); err != nil {
				glog.Warning(err)
			}
		}

		glog.Infof("waiting for %v before re-starting the fetcher.\n", fetchInterval)
		<-time.After(fetchInterval)
	}
}

func isDirEmpty(path string) bool {
	fis, err := ioutil.ReadDir(path)
	if err != nil {
		return false
	}

	return len(fis) == 0
}

func getAllRepos(db *sql.DB, langs []string, basePath string) ([]repo.Repo, error) {
	var inClause string
	if langs != nil && len(langs) > 0 {
		// Quote languages.
		for idx, val := range langs {
			langs[idx] = "'" + val + "'"
		}
		inClause = " WHERE LOWER(primary_language) IN (" + strings.Join(langs, ",") + ")"
	}

	rows, err := db.Query("SELECT vcs, clone_path, clone_url FROM repositories" + inClause)
	if err != nil {
		glog.Error(err)
		return nil, err
	}
	defer rows.Close()

	var repos []repo.Repo

	for rows.Next() {
		var vcs, clonePath, cloneURL string
		if err := rows.Scan(&vcs, &clonePath, &cloneURL); err != nil {
			glog.Error(err)
			continue
		}

		var newRepo repo.Repo
		var err error

		newRepo, err = repo.New(vcs, filepath.Join(basePath, clonePath), cloneURL)
		if err != nil {
			glog.Error(err)
			continue
		}

		repos = append(repos, newRepo)
	}

	return repos, nil
}

func checkCloneDir(cloneDir string) error {
	// check if clone path exists
	if fi, err := os.Stat(cloneDir); err == nil {
		glog.Error(err)
		return err
	} else if !fi.IsDir() {
		err = errors.New("clone path must be a directory")
		glog.Error(err)
		return err
	}

	// check if clone path is writable
	// note: since the directory already exists, then the file perm param is
	// useless
	file, err := os.OpenFile(cloneDir, os.O_RDWR, 0770)
	if err != nil {
		err = errors.New("clone path must be writable")
		glog.Error(err)
		return err
	}
	file.Close()

	return nil
}

func openDBSession(cfg config.DatabaseConfig) (*sql.DB, error) {
	dbURL := fmt.Sprintf(
		"user='%s' password='%s' host='%s' port=%d dbname='%s' sslmode='%s'",
		cfg.UserName, cfg.Password, cfg.HostName, cfg.Port, cfg.DBName, cfg.SSLMode)

	return sql.Open("postgres", dbURL)
}

func fatal(a ...interface{}) {
	glog.Error(a)
	os.Exit(1)
}

func main() {
	configPath := flag.String("c", "", "configuration file")
	disableCrawlers := flag.Bool("disable-crawlers", false, "disable the data crawlers")
	disableFetcher := flag.Bool("disable-fetcher", false, "disable the repositories fetcher")
	flag.Parse()

	// Make sure we finish writing logs before exiting.
	defer glog.Flush()

	if len(*configPath) == 0 {
		fatal("no configuration specified")
	}

	cfg, err := config.ReadConfig(*configPath)
	if err != nil {
		fatal(err)
	}

	db, err := openDBSession(cfg.Database)
	if err != nil {
		fatal(err)
	}
	defer db.Close()

	var cs []crawlers.Crawler

	for _, crawlerConfig := range cfg.Crawlers {
		c, err := crawlers.New(crawlerConfig, db)
		if err != nil {
			fatal(err)
		}

		cs = append(cs, c)
	}

	crawlingInterval, err := time.ParseDuration(cfg.CrawlingTimeInterval)
	if err != nil {
		fatal(err)
	}

	var wg sync.WaitGroup

	// start the crawling worker
	if !*disableCrawlers {
		wg.Add(1)
		go crawlingWorker(cs, crawlingInterval)
	}

	fetchInterval, err := time.ParseDuration(cfg.FetchTimeInterval)
	if err != nil {
		fatal(err)
	}

	// start the repo puller worker
	if !*disableFetcher {
		wg.Add(1)
		go repoWorker(db, cfg.FetchLanguages, cfg.CloneDir, fetchInterval, cfg.TarRepos)
	}

	// wait until the cows come home saint
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"

	"github.com/TheNewNormal/libxhyve"
	"github.com/codegangsta/cli"
)

func daemonHandler(ctx *cli.Context) {
	config := readConfig()
	go proxySocket(config.Uuid)
	runXhyve(config)
}

func runXhyve(config Config) {
	done := make(chan bool)
	pty := make(chan string)
	args := []string{
		"libxhyve_bug",
		"-A",
		"-c", fmt.Sprintf("%d", config.CpuCount),
		"-m", fmt.Sprintf("%dG", config.Memory),
		"-s", "0:0,hostbridge",
		"-l", "com1,stdio",
		"-s", "31,lpc",
		"-s", "2:0,virtio-net",
		"-s", "4,virtio-blk,"+PREFIX+"/var/db/dlite/disk.img",
		"-U", config.Uuid,
		"-f", fmt.Sprintf("kexec,%s,%s,%q", PREFIX+"/usr/share/dlite/bzImage", PREFIX+"/usr/share/dlite/rootfs.cpio.xz", "console=ttyS0 hostname=dlite uuid="+config.Uuid),
	}

	go func(args []string, pty chan string) {
		err := xhyve.Run(args, pty)
		if err != nil {
			log.Fatalln(err)
		}

		done <- true
	}(args, pty)

	ptyStr := <-pty
	log.Printf("Xhyve listening on term: %s\n", ptyStr)
	<-done
}

func proxySocket(uuid string) {
	ip := getIP(uuid)
	log.Println("got ip:", ip)
	for ip == "" {
		ip = getIP(uuid)
		log.Println(ip)
	}

	_, err := os.Stat("/var/run/docker.sock")
	if err == nil {
		err = os.Remove("/var/run/docker.sock")
		if err != nil {
			log.Fatal(err)
		}
	}
	socket, err := net.Listen("unix", "/var/run/docker.sock")
	if err != nil {
		log.Fatal(err)
	}

	err = os.Chmod("/var/run/docker.sock", 0777)
	if err != nil {
		log.Fatal(err)
	}

	for {
		log.Print("Waiting for connection to socket...")
		conn, err := socket.Accept()
		log.Print("Accepted connection from socket..")
		if err != nil {
			log.Fatal(err)
		}

		docker, err := net.Dial("tcp", ip+":2375")
		if err != nil {
			log.Fatal(err)
		}

		defer docker.Close()

		go func(c, d net.Conn) {
			_, err = io.Copy(d, c)
			if err != nil {
				log.Fatal(err)
			}
		}(conn, docker)

		go func(c, d net.Conn) {
			_, err = io.Copy(c, d)
			if err != nil {
				log.Fatal(err)
			}
		}(conn, docker)
	}
}
<commit_msg>remove unneeded thing<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"

	"github.com/TheNewNormal/libxhyve"
	"github.com/codegangsta/cli"
)

func daemonHandler(ctx *cli.Context) {
	config := readConfig()
	go proxySocket(config.Uuid)
	runXhyve(config)
}

func runXhyve(config Config) {
	done := make(chan bool)
	pty := make(chan string)
	args := []string{
		"-A",
		"-c", fmt.Sprintf("%d", config.CpuCount),
		"-m", fmt.Sprintf("%dG", config.Memory),
		"-s", "0:0,hostbridge",
		"-l", "com1,stdio",
		"-s", "31,lpc",
		"-s", "2:0,virtio-net",
		"-s", "4,virtio-blk,"+PREFIX+"/var/db/dlite/disk.img",
		"-U", config.Uuid,
		"-f", fmt.Sprintf("kexec,%s,%s,%q", PREFIX+"/usr/share/dlite/bzImage", PREFIX+"/usr/share/dlite/rootfs.cpio.xz", "console=ttyS0 hostname=dlite uuid="+config.Uuid),
	}

	go func(args []string, pty chan string) {
		err := xhyve.Run(args, pty)
		if err != nil {
			log.Fatalln(err)
		}

		done <- true
	}(args, pty)

	ptyStr := <-pty
	log.Printf("Xhyve listening on term: %s\n", ptyStr)
	<-done
}

func proxySocket(uuid string) {
	ip := getIP(uuid)
	log.Println("got ip:", ip)
	for ip == "" {
		ip = getIP(uuid)
		log.Println(ip)
	}

	_, err := os.Stat("/var/run/docker.sock")
	if err == nil {
		err = os.Remove("/var/run/docker.sock")
		if err != nil {
			log.Fatal(err)
		}
	}
	socket, err := net.Listen("unix", "/var/run/docker.sock")
	if err != nil {
		log.Fatal(err)
	}

	err = os.Chmod("/var/run/docker.sock", 0777)
	if err != nil {
		log.Fatal(err)
	}

	for {
		log.Print("Waiting for connection to socket...")
		conn, err := socket.Accept()
		log.Print("Accepted connection from socket..")
		if err != nil {
			log.Fatal(err)
		}

		docker, err := net.Dial("tcp", ip+":2375")
		if err != nil {
			log.Fatal(err)
		}

		defer docker.Close()

		go func(c, d net.Conn) {
			_, err = io.Copy(d, c)
			if err != nil {
				log.Fatal(err)
			}
		}(conn, docker)

		go func(c, d net.Conn) {
			_, err = io.Copy(c, d)
			if err != nil {
				log.Fatal(err)
			}
		}(conn, docker)
	}
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package main

import (
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/pkg/errors"
	"log"
	"net/http"
	"os"
)

const (
	defaultListenPort = 9090

	uriAddMatch = "/api/matches/new"
	uriIndex    = "/"
)

var (
	defaultListenAddr = fmt.Sprintf("localhost:%d",
		defaultListenPort)
)

func apiAddMatch(w http.ResponseWriter, req *http.Request) {
	// add new match
}

func homeHandler(w http.ResponseWriter, req *http.Request) {
	// homepage
}

func daemonMain() error {
	r := mux.NewRouter()

	r.HandleFunc(uriIndex, homeHandler).
		Methods("GET")

	// matches only come through POST
	r.HandleFunc(uriAddMatch, apiAddMatch).
		Methods("POST")

	// setup logging for all handlers
	lr := handlers.LoggingHandler(os.Stdout, r)

	http.Handle("/", lr)
	return http.ListenAndServe(fmt.Sprintf(":%d", C.port), nil)
}

func runDaemon() error {
	err := LoadConfig()
	if err != nil {
		return errors.Wrap(err, "daemon startup failed")
	}

	log.Printf("listen port: %d", C.port)

	return daemonMain()
}
<commit_msg>daemon: add match import<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package main

import (
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/pkg/errors"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

const (
	defaultListenPort = 9090

	uriAddMatch = "/api/matches/new"
	uriIndex    = "/"
)

var (
	defaultListenAddr = fmt.Sprintf("localhost:%d",
		defaultListenPort)
)

func apiAddMatch(w http.ResponseWriter, req *http.Request) {
	// add new match
	log.Printf("add new match")

	matchdata, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("failed to receive match data: %s", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if len(matchdata) == 0 {
		log.Printf("no data provided")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	log.Printf("match data: %s", matchdata)

	match, err := LoadMatchData(matchdata)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	log.Printf("match hash: %s", match.DataHash)
	w.Write([]byte(match.DataHash))
}

func homeHandler(w http.ResponseWriter, req *http.Request) {
	// homepage
}

func daemonMain() error {
	r := mux.NewRouter()

	r.HandleFunc(uriIndex, homeHandler).
		Methods("GET")

	// matches only come through POST
	r.HandleFunc(uriAddMatch, apiAddMatch).
		Methods("POST")

	// setup logging for all handlers
	lr := handlers.LoggingHandler(os.Stdout, r)

	http.Handle("/", lr)
	return http.ListenAndServe(fmt.Sprintf(":%d", C.port), nil)
}

func runDaemon() error {
	err := LoadConfig()
	if err != nil {
		return errors.Wrap(err, "daemon startup failed")
	}

	log.Printf("listen port: %d", C.port)

	return daemonMain()
}
<|endoftext|>


<commit_before>// Copyright 2017 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.

// ARM64-specific hardware-assisted CRC32 algorithms. See crc32.go for a
// description of the interface that each architecture-specific file
// implements.

package crc32

import "internal/cpu"

func castagnoliUpdate(crc uint32, p []byte) uint32
func ieeeUpdate(crc uint32, p []byte) uint32

func archAvailableCastagnoli() bool {
	return cpu.ARM64.HasCRC32
}

func archInitCastagnoli() {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for Catagnoli not available")
	}
}

func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for Castagnoli not available")
	}

	return ^castagnoliUpdate(^crc, p)
}

func archAvailableIEEE() bool {
	return cpu.ARM64.HasCRC32
}

func archInitIEEE() {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for IEEE not available")
	}
}

func archUpdateIEEE(crc uint32, p []byte) uint32 {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for IEEE not available")
	}

	return ^ieeeUpdate(^crc, p)
}
<commit_msg>hash/crc32: fix typo in arm64 archInitCastagnoli panic message<commit_after>// Copyright 2017 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.

// ARM64-specific hardware-assisted CRC32 algorithms. See crc32.go for a
// description of the interface that each architecture-specific file
// implements.

package crc32

import "internal/cpu"

func castagnoliUpdate(crc uint32, p []byte) uint32
func ieeeUpdate(crc uint32, p []byte) uint32

func archAvailableCastagnoli() bool {
	return cpu.ARM64.HasCRC32
}

func archInitCastagnoli() {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for Castagnoli not available")
	}
}

func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for Castagnoli not available")
	}

	return ^castagnoliUpdate(^crc, p)
}

func archAvailableIEEE() bool {
	return cpu.ARM64.HasCRC32
}

func archInitIEEE() {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for IEEE not available")
	}
}

func archUpdateIEEE(crc uint32, p []byte) uint32 {
	if !cpu.ARM64.HasCRC32 {
		panic("arch-specific crc32 instruction for IEEE not available")
	}

	return ^ieeeUpdate(^crc, p)
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/BurntSushi/toml"
)

var (
	xdgRe = regexp.MustCompile("^XDG_*")
)

type File struct {
	Options map[string]string `toml:"options"`
	Toggles map[string]bool   `toml:"toggles"`
}

func exportConfig() File {
	// update columns param from working config
	Update("columns", ColumnsString())

	lock.RLock()
	defer lock.RUnlock()

	c := File{
		Options: make(map[string]string),
		Toggles: make(map[string]bool),
	}

	for _, p := range GlobalParams {
		c.Options[p.Key] = p.Val
	}
	for _, sw := range GlobalSwitches {
		c.Toggles[sw.Key] = sw.Val
	}

	return c
}

//
func Read() error {
	var config File

	path, err := getConfigPath()
	if err != nil {
		return err
	}

	if _, err := toml.DecodeFile(path, &config); err != nil {
		return err
	}
	for k, v := range config.Options {
		Update(k, v)
	}
	for k, v := range config.Toggles {
		UpdateSwitch(k, v)
	}

	// set working column config, if provided
	colStr := GetVal("columns")
	if len(colStr) > 0 {
		var colNames []string
		for _, s := range strings.Split(colStr, ",") {
			colNames = append(colNames, strings.TrimSpace(s))
		}
		SetColumns(colNames)
	}

	return nil
}

func Write() (path string, err error) {
	path, err = getConfigPath()
	if err != nil {
		return path, err
	}

	cfgdir := basedir(path)
	// create config dir if not exist
	if _, err := os.Stat(cfgdir); err != nil {
		err = os.MkdirAll(cfgdir, 0755)
		if err != nil {
			return path, fmt.Errorf("failed to create config dir [%s]: %s", cfgdir, err)
		}
	}

	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return path, fmt.Errorf("failed to open config for writing: %s", err)
	}

	writer := toml.NewEncoder(file)
	err = writer.Encode(exportConfig())
	if err != nil {
		return path, fmt.Errorf("failed to write config: %s", err)
	}

	return path, nil
}

// determine config path from environment
func getConfigPath() (path string, err error) {
	homeDir, ok := os.LookupEnv("HOME")
	if !ok {
		return path, fmt.Errorf("$HOME not set")
	}

	// use xdg config home if possible
	if xdgSupport() {
		xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME")
		if !ok {
			xdgHome = fmt.Sprintf("%s/.config", homeDir)
		}
		path = fmt.Sprintf("%s/ctop/config", xdgHome)
	} else {
		path = fmt.Sprintf("%s/.ctop", homeDir)
	}

	return path, nil
}

// test for environemnt supporting XDG spec
func xdgSupport() bool {
	for _, e := range os.Environ() {
		if xdgRe.FindAllString(e, 1) != nil {
			return true
		}
	}
	return false
}

func basedir(path string) string {
	parts := strings.Split(path, "/")
	return strings.Join((parts[0 : len(parts)-1]), "/")
}
<commit_msg>handle empty entries in column config<commit_after>package config

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/BurntSushi/toml"
)

var (
	xdgRe = regexp.MustCompile("^XDG_*")
)

type File struct {
	Options map[string]string `toml:"options"`
	Toggles map[string]bool   `toml:"toggles"`
}

func exportConfig() File {
	// update columns param from working config
	Update("columns", ColumnsString())

	lock.RLock()
	defer lock.RUnlock()

	c := File{
		Options: make(map[string]string),
		Toggles: make(map[string]bool),
	}

	for _, p := range GlobalParams {
		c.Options[p.Key] = p.Val
	}
	for _, sw := range GlobalSwitches {
		c.Toggles[sw.Key] = sw.Val
	}

	return c
}

//
func Read() error {
	var config File

	path, err := getConfigPath()
	if err != nil {
		return err
	}

	if _, err := toml.DecodeFile(path, &config); err != nil {
		return err
	}
	for k, v := range config.Options {
		Update(k, v)
	}
	for k, v := range config.Toggles {
		UpdateSwitch(k, v)
	}

	// set working column config, if provided
	colStr := GetVal("columns")
	if len(colStr) > 0 {
		var colNames []string
		for _, s := range strings.Split(colStr, ",") {
			s = strings.TrimSpace(s)
			if s != "" {
				colNames = append(colNames, strings.TrimSpace(s))
			}
		}
		SetColumns(colNames)
	}

	return nil
}

func Write() (path string, err error) {
	path, err = getConfigPath()
	if err != nil {
		return path, err
	}

	cfgdir := basedir(path)
	// create config dir if not exist
	if _, err := os.Stat(cfgdir); err != nil {
		err = os.MkdirAll(cfgdir, 0755)
		if err != nil {
			return path, fmt.Errorf("failed to create config dir [%s]: %s", cfgdir, err)
		}
	}

	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return path, fmt.Errorf("failed to open config for writing: %s", err)
	}

	writer := toml.NewEncoder(file)
	err = writer.Encode(exportConfig())
	if err != nil {
		return path, fmt.Errorf("failed to write config: %s", err)
	}

	return path, nil
}

// determine config path from environment
func getConfigPath() (path string, err error) {
	homeDir, ok := os.LookupEnv("HOME")
	if !ok {
		return path, fmt.Errorf("$HOME not set")
	}

	// use xdg config home if possible
	if xdgSupport() {
		xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME")
		if !ok {
			xdgHome = fmt.Sprintf("%s/.config", homeDir)
		}
		path = fmt.Sprintf("%s/ctop/config", xdgHome)
	} else {
		path = fmt.Sprintf("%s/.ctop", homeDir)
	}

	return path, nil
}

// test for environemnt supporting XDG spec
func xdgSupport() bool {
	for _, e := range os.Environ() {
		if xdgRe.FindAllString(e, 1) != nil {
			return true
		}
	}
	return false
}

func basedir(path string) string {
	parts := strings.Split(path, "/")
	return strings.Join((parts[0 : len(parts)-1]), "/")
}
<|endoftext|>


<commit_before>package rrconfig

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"
	"sync"
)

type JsonConfig struct {
	m     map[string]interface{}
	cache map[string]interface{}
	rb    []byte
	mu    sync.RWMutex
}

func LoadJsonConfigFromFile(path string) (*JsonConfig, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return LoadJsonConfigFromBytes(b)
}

func LoadJsonConfigFromBytes(b []byte) (*JsonConfig, error) {
	var jm map[string]interface{}
	if err := json.Unmarshal(b, &jm); err != nil {
		return nil, err
	}
	s := &JsonConfig{
		m:     jm,
		cache: make(map[string]interface{}),
		rb:    b,
	}
	return s, nil
}

func (s *JsonConfig) Dump() (string, error) {
	var rj bytes.Buffer
	if err := json.Indent(&rj, s.rb, "", "\t"); err != nil {
		return "", err
	}
	return string(rj.Bytes()), nil
}

// Get("a.b.c")
func (s *JsonConfig) Get(key string) (interface{}, error) {
	s.mu.RLock()
	if v, ok := s.cache[key]; ok {
		s.mu.RUnlock()
		return v, nil
	}
	s.mu.RUnlock()
	nodes := strings.Split(key, ".")
	m := s.m
	for i := 0; i < len(nodes); i++ {
		if v, ok := m[nodes[i]]; ok {
			// exist
			if vv, okk := v.(map[string]interface{}); okk {
				// not end
				m = vv
			} else {
				s.mu.Lock()
				s.cache[key] = v
				s.mu.Unlock()
				return v, nil
			}
		} else {
			return nil, fmt.Errorf("no value for key %s", key)
		}
	}
	return m, nil
}

func (s *JsonConfig) GetStringSlice(key string) ([]string, error) {
	f, err := s.Get(key)
	if err != nil {
		return nil, err
	}
	if _, ok := f.([]interface{}); !ok {
		return nil, fmt.Errorf("value for key %s is not slice", key)
	}
	sf := f.([]interface{})
	ss := make([]string, len(sf))
	for i, v := range sf {
		if vv, ok := v.(string); ok {
			ss[i] = vv
		} else {
			return nil, fmt.Errorf("%s[%d] is not a string", key, i)
		}
	}
	return ss, nil
}

func (s *JsonConfig) GetString(key string) (string, error) {
	f, err := s.Get(key)
	if err != nil {
		return "", err
	}
	if _, ok := f.(string); !ok {
		return "", fmt.Errorf("value for key %s is not string", key)
	}
	return f.(string), nil
}

func (s *JsonConfig) GetFloat64(key string) (float64, error) {
	f, err := s.Get(key)
	if err != nil {
		return 0.0, err
	}
	if _, ok := f.(float64); !ok {
		return 0.0, fmt.Errorf("value for key %s is not float64", key)
	}
	return f.(float64), nil
}

<commit_msg>GetStringSlice return empty slice when error happened<commit_after>package rrconfig

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"
	"sync"
)

type JsonConfig struct {
	m     map[string]interface{}
	cache map[string]interface{}
	rb    []byte
	mu    sync.RWMutex
}

func LoadJsonConfigFromFile(path string) (*JsonConfig, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return LoadJsonConfigFromBytes(b)
}

func LoadJsonConfigFromBytes(b []byte) (*JsonConfig, error) {
	var jm map[string]interface{}
	if err := json.Unmarshal(b, &jm); err != nil {
		return nil, err
	}
	s := &JsonConfig{
		m:     jm,
		cache: make(map[string]interface{}),
		rb:    b,
	}
	return s, nil
}

func (s *JsonConfig) Dump() (string, error) {
	var rj bytes.Buffer
	if err := json.Indent(&rj, s.rb, "", "\t"); err != nil {
		return "", err
	}
	return string(rj.Bytes()), nil
}

// Get("a.b.c")
func (s *JsonConfig) Get(key string) (interface{}, error) {
	s.mu.RLock()
	if v, ok := s.cache[key]; ok {
		s.mu.RUnlock()
		return v, nil
	}
	s.mu.RUnlock()
	nodes := strings.Split(key, ".")
	m := s.m
	for i := 0; i < len(nodes); i++ {
		if v, ok := m[nodes[i]]; ok {
			// exist
			if vv, okk := v.(map[string]interface{}); okk {
				// not end
				m = vv
			} else {
				s.mu.Lock()
				s.cache[key] = v
				s.mu.Unlock()
				return v, nil
			}
		} else {
			return nil, fmt.Errorf("no value for key %s", key)
		}
	}
	return m, nil
}

func (s *JsonConfig) GetStringSlice(key string) ([]string, error) {
	empty := []string{}
	f, err := s.Get(key)
	if err != nil {
		return empty, err
	}
	if _, ok := f.([]interface{}); !ok {
		return empty, fmt.Errorf("value for key %s is not slice", key)
	}
	sf := f.([]interface{})
	ss := make([]string, len(sf))
	for i, v := range sf {
		if vv, ok := v.(string); ok {
			ss[i] = vv
		} else {
			return empty, fmt.Errorf("%s[%d] is not a string", key, i)
		}
	}
	return ss, nil
}

func (s *JsonConfig) GetString(key string) (string, error) {
	f, err := s.Get(key)
	if err != nil {
		return "", err
	}
	if _, ok := f.(string); !ok {
		return "", fmt.Errorf("value for key %s is not string", key)
	}
	return f.(string), nil
}

func (s *JsonConfig) GetFloat64(key string) (float64, error) {
	f, err := s.Get(key)
	if err != nil {
		return 0.0, err
	}
	if _, ok := f.(float64); !ok {
		return 0.0, fmt.Errorf("value for key %s is not float64", key)
	}
	return f.(float64), nil
}

<|endoftext|>