2014-01-26 14:28:41 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Options struct {
|
|
|
|
Listen string `ini:"listen-address" default:":22000" description:"ip:port to for incoming sync connections"`
|
|
|
|
ReadOnly bool `ini:"read-only" description:"Allow changes to the local repository"`
|
|
|
|
Delete bool `ini:"allow-delete" default:"true" description:"Allow deletes of files in the local repository"`
|
|
|
|
Symlinks bool `ini:"follow-symlinks" default:"true" description:"Follow symbolic links at the top level of the repository"`
|
|
|
|
GUI bool `ini:"gui-enabled" default:"true" description:"Enable the HTTP GUI"`
|
|
|
|
GUIAddr string `ini:"gui-address" default:"127.0.0.1:8080" description:"ip:port for GUI connections"`
|
|
|
|
ExternalServer string `ini:"global-announce-server" default:"syncthing.nym.se:22025" description:"Global server for announcements"`
|
|
|
|
ExternalDiscovery bool `ini:"global-announce-enabled" default:"true" description:"Announce to the global announce server"`
|
|
|
|
LocalDiscovery bool `ini:"local-announce-enabled" default:"true" description:"Announce to the local network"`
|
2014-01-26 16:48:20 +01:00
|
|
|
ParallelRequests int `ini:"parallel-requests" default:"16" description:"Maximum number of blocks to request in parallel"`
|
2014-01-26 14:28:41 +01:00
|
|
|
LimitRate int `ini:"max-send-kbps" description:"Limit outgoing data rate (kbyte/s)"`
|
|
|
|
ScanInterval time.Duration `ini:"rescan-interval" default:"60s" description:"Scan repository for changes this often"`
|
|
|
|
ConnInterval time.Duration `ini:"reconnection-interval" default:"60s" description:"Attempt to (re)connect to peers this often"`
|
|
|
|
MaxChangeBW int `ini:"max-change-bw" default:"1000" description:"Suppress files changing more than this (kbyte/s)"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfig(m map[string]string, data interface{}) error {
|
|
|
|
s := reflect.ValueOf(data).Elem()
|
|
|
|
t := s.Type()
|
|
|
|
|
|
|
|
for i := 0; i < s.NumField(); i++ {
|
|
|
|
f := s.Field(i)
|
|
|
|
tag := t.Field(i).Tag
|
|
|
|
|
|
|
|
name := tag.Get("ini")
|
|
|
|
if len(name) == 0 {
|
|
|
|
name = strings.ToLower(t.Field(i).Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
v, ok := m[name]
|
|
|
|
if !ok {
|
|
|
|
v = tag.Get("default")
|
|
|
|
}
|
|
|
|
if len(v) > 0 {
|
|
|
|
switch f.Interface().(type) {
|
|
|
|
case time.Duration:
|
|
|
|
d, err := time.ParseDuration(v)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f.SetInt(int64(d))
|
|
|
|
|
|
|
|
case string:
|
|
|
|
f.SetString(v)
|
|
|
|
|
|
|
|
case int:
|
|
|
|
i, err := strconv.ParseInt(v, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f.SetInt(i)
|
|
|
|
|
|
|
|
case bool:
|
|
|
|
f.SetBool(v == "true")
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic(f.Type())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type cfg struct {
|
|
|
|
Key string
|
|
|
|
Value string
|
|
|
|
Comment string
|
|
|
|
}
|
|
|
|
|
|
|
|
func structToValues(data interface{}) []cfg {
|
|
|
|
s := reflect.ValueOf(data).Elem()
|
|
|
|
t := s.Type()
|
|
|
|
|
|
|
|
var vals []cfg
|
|
|
|
for i := 0; i < s.NumField(); i++ {
|
|
|
|
f := s.Field(i)
|
|
|
|
tag := t.Field(i).Tag
|
|
|
|
|
|
|
|
var c cfg
|
|
|
|
c.Key = tag.Get("ini")
|
|
|
|
if len(c.Key) == 0 {
|
|
|
|
c.Key = strings.ToLower(t.Field(i).Name)
|
|
|
|
}
|
|
|
|
c.Value = fmt.Sprint(f.Interface())
|
|
|
|
c.Comment = tag.Get("description")
|
|
|
|
vals = append(vals, c)
|
|
|
|
}
|
|
|
|
return vals
|
|
|
|
}
|
|
|
|
|
|
|
|
var configTemplateStr = `[repository]
|
|
|
|
{{if .comments}}; The directory to synchronize. Will be created if it does not exist.
|
|
|
|
{{end}}dir = {{.dir}}
|
|
|
|
|
|
|
|
[nodes]
|
|
|
|
{{if .comments}}; Map of node ID to addresses, or "dynamic" for automatic discovery. Examples:
|
|
|
|
; J3MZ4G5O4CLHJKB25WX47K5NUJUWDOLO2TTNY3TV3NRU4HVQRKEQ = 172.16.32.24:22000
|
|
|
|
; ZNJZRXQKYHF56A2VVNESRZ6AY4ZOWGFJCV6FXDZJUTRVR3SNBT6Q = dynamic
|
|
|
|
{{end}}{{range $n, $a := .nodes}}{{$n}} = {{$a}}
|
|
|
|
{{end}}
|
|
|
|
[settings]
|
|
|
|
{{range $v := .settings}}; {{$v.Comment}}
|
|
|
|
{{$v.Key}} = {{$v.Value}}
|
|
|
|
{{end}}
|
|
|
|
`
|
|
|
|
|
|
|
|
var configTemplate = template.Must(template.New("config").Parse(configTemplateStr))
|
|
|
|
|
|
|
|
func writeConfig(wr io.Writer, dir string, nodes map[string]string, opts Options, comments bool) {
|
|
|
|
configTemplate.Execute(wr, map[string]interface{}{
|
|
|
|
"dir": dir,
|
|
|
|
"nodes": nodes,
|
|
|
|
"settings": structToValues(&opts),
|
|
|
|
"comments": comments,
|
|
|
|
})
|
|
|
|
}
|