Rework config/flags (fixes #13)
This commit is contained in:
parent
ea5ef28c5a
commit
81d5d1d4a6
129
config.go
Normal file
129
config.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
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"`
|
||||||
|
RequestsInFlight int `ini:"parallell-requests" default:"16" description:"Maximum number of blocks to request in parallell"`
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -100,7 +100,6 @@ type Discoverer struct {
|
|||||||
MyID string
|
MyID string
|
||||||
ListenPort int
|
ListenPort int
|
||||||
BroadcastIntv time.Duration
|
BroadcastIntv time.Duration
|
||||||
ExtListenPort int
|
|
||||||
ExtBroadcastIntv time.Duration
|
ExtBroadcastIntv time.Duration
|
||||||
|
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
@ -114,7 +113,7 @@ type Discoverer struct {
|
|||||||
// When we hit this many errors in succession, we stop.
|
// When we hit this many errors in succession, we stop.
|
||||||
const maxErrors = 30
|
const maxErrors = 30
|
||||||
|
|
||||||
func NewDiscoverer(id string, port int, extPort int, extServer string) (*Discoverer, error) {
|
func NewDiscoverer(id string, port int, extServer string) (*Discoverer, error) {
|
||||||
local4 := &net.UDPAddr{IP: net.IP{0, 0, 0, 0}, Port: AnnouncementPort}
|
local4 := &net.UDPAddr{IP: net.IP{0, 0, 0, 0}, Port: AnnouncementPort}
|
||||||
conn, err := net.ListenUDP("udp4", local4)
|
conn, err := net.ListenUDP("udp4", local4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,7 +124,6 @@ func NewDiscoverer(id string, port int, extPort int, extServer string) (*Discove
|
|||||||
MyID: id,
|
MyID: id,
|
||||||
ListenPort: port,
|
ListenPort: port,
|
||||||
BroadcastIntv: 30 * time.Second,
|
BroadcastIntv: 30 * time.Second,
|
||||||
ExtListenPort: extPort,
|
|
||||||
ExtBroadcastIntv: 1800 * time.Second,
|
ExtBroadcastIntv: 1800 * time.Second,
|
||||||
|
|
||||||
conn: conn,
|
conn: conn,
|
||||||
@ -138,7 +136,7 @@ func NewDiscoverer(id string, port int, extPort int, extServer string) (*Discove
|
|||||||
if disc.ListenPort > 0 {
|
if disc.ListenPort > 0 {
|
||||||
disc.sendAnnouncements()
|
disc.sendAnnouncements()
|
||||||
}
|
}
|
||||||
if len(disc.extServer) > 0 && disc.ExtListenPort > 0 {
|
if len(disc.extServer) > 0 {
|
||||||
disc.sendExtAnnouncements()
|
disc.sendExtAnnouncements()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,13 +151,13 @@ func (d *Discoverer) sendAnnouncements() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Discoverer) sendExtAnnouncements() {
|
func (d *Discoverer) sendExtAnnouncements() {
|
||||||
extIP, err := net.ResolveUDPAddr("udp", d.extServer+":22025")
|
extIP, err := net.ResolveUDPAddr("udp", d.extServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("discover/external: %v; no external announcements", err)
|
log.Printf("discover/external: %v; no external announcements", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := EncodePacket(Packet{AnnouncementMagic, uint16(d.ExtListenPort), d.MyID, nil})
|
buf := EncodePacket(Packet{AnnouncementMagic, uint16(22000), d.MyID, nil})
|
||||||
go d.writeAnnouncements(buf, extIP, d.ExtBroadcastIntv)
|
go d.writeAnnouncements(buf, extIP, d.ExtBroadcastIntv)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +211,7 @@ func (d *Discoverer) recvAnnouncements() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Discoverer) externalLookup(node string) (string, bool) {
|
func (d *Discoverer) externalLookup(node string) (string, bool) {
|
||||||
extIP, err := net.ResolveUDPAddr("udp", d.extServer+":22025")
|
extIP, err := net.ResolveUDPAddr("udp", d.extServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("discover/external: %v; no external lookup", err)
|
log.Printf("discover/external: %v; no external lookup", err)
|
||||||
return "", false
|
return "", false
|
||||||
|
|||||||
162
main.go
162
main.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -18,49 +19,10 @@ import (
|
|||||||
|
|
||||||
"github.com/calmh/ini"
|
"github.com/calmh/ini"
|
||||||
"github.com/calmh/syncthing/discover"
|
"github.com/calmh/syncthing/discover"
|
||||||
flags "github.com/calmh/syncthing/github.com/jessevdk/go-flags"
|
|
||||||
"github.com/calmh/syncthing/model"
|
"github.com/calmh/syncthing/model"
|
||||||
"github.com/calmh/syncthing/protocol"
|
"github.com/calmh/syncthing/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
ConfDir string `short:"c" long:"cfg" description:"Configuration directory" default:"~/.syncthing" value-name:"DIR"`
|
|
||||||
Listen string `short:"l" long:"listen" description:"Listen address" default:":22000" value-name:"ADDR"`
|
|
||||||
ReadOnly bool `short:"r" long:"ro" description:"Repository is read only"`
|
|
||||||
Rehash bool `long:"rehash" description:"Ignore cache and rehash all files in repository"`
|
|
||||||
NoDelete bool `long:"no-delete" description:"Never delete files"`
|
|
||||||
NoSymlinks bool `long:"no-symlinks" description:"Don't follow first level symlinks in the repo"`
|
|
||||||
NoStats bool `long:"no-stats" description:"Don't print model and connection statistics"`
|
|
||||||
NoGUI bool `long:"no-gui" description:"Don't start GUI"`
|
|
||||||
GUIAddr string `long:"gui-addr" description:"GUI listen address" default:"127.0.0.1:8080" value-name:"ADDR"`
|
|
||||||
ShowVersion bool `short:"v" long:"version" description:"Show version"`
|
|
||||||
Discovery DiscoveryOptions `group:"Discovery Options"`
|
|
||||||
Advanced AdvancedOptions `group:"Advanced Options"`
|
|
||||||
Debug DebugOptions `group:"Debugging Options"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DebugOptions struct {
|
|
||||||
LogSource bool `long:"log-source"`
|
|
||||||
TraceModel []string `long:"trace-model" value-name:"TRACE" description:"idx, net, file, need, pull"`
|
|
||||||
TraceConnect bool `long:"trace-connect"`
|
|
||||||
Profiler string `long:"profiler" value-name:"ADDR"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiscoveryOptions struct {
|
|
||||||
ExternalServer string `long:"ext-server" description:"External discovery server" value-name:"NAME" default:"syncthing.nym.se"`
|
|
||||||
ExternalPort int `short:"e" long:"ext-port" description:"External listen port" value-name:"PORT" default:"22000"`
|
|
||||||
NoExternalDiscovery bool `short:"n" long:"no-ext-announce" description:"Do not announce presence externally"`
|
|
||||||
NoLocalDiscovery bool `short:"N" long:"no-local-announce" description:"Do not announce presence locally"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AdvancedOptions struct {
|
|
||||||
RequestsInFlight int `long:"reqs-in-flight" description:"Parallell in flight requests per node" default:"8" value-name:"REQS"`
|
|
||||||
LimitRate int `long:"send-rate" description:"Rate limit for outgoing data" default:"0" value-name:"KBPS"`
|
|
||||||
ScanInterval time.Duration `long:"scan-intv" description:"Repository scan interval" default:"60s" value-name:"INTV"`
|
|
||||||
ConnInterval time.Duration `long:"conn-intv" description:"Node reconnect interval" default:"60s" value-name:"INTV"`
|
|
||||||
MaxChangeBW int `long:"max-change-bw" description:"Max change bandwidth per file" default:"1e6" value-name:"MB/s"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var opts Options
|
var opts Options
|
||||||
var Version string = "unknown-dev"
|
var Version string = "unknown-dev"
|
||||||
|
|
||||||
@ -74,21 +36,27 @@ var (
|
|||||||
nodeAddrs = make(map[string][]string)
|
nodeAddrs = make(map[string][]string)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
showVersion bool
|
||||||
|
showConfig bool
|
||||||
|
confDir string
|
||||||
|
trace string
|
||||||
|
profiler string
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetOutput(os.Stderr)
|
log.SetOutput(os.Stderr)
|
||||||
logger = log.New(os.Stderr, "", log.Flags())
|
logger = log.New(os.Stderr, "", log.Flags())
|
||||||
|
|
||||||
_, err := flags.Parse(&opts)
|
flag.StringVar(&confDir, "home", "~/.syncthing", "Set configuration directory")
|
||||||
if err != nil {
|
flag.BoolVar(&showConfig, "config", false, "Print current configuration")
|
||||||
if err, ok := err.(*flags.Error); ok {
|
flag.StringVar(&trace, "debug.trace", "", "(connect,net,idx,file,pull)")
|
||||||
if err.Type == flags.ErrHelp {
|
flag.StringVar(&profiler, "debug.profiler", "", "(addr)")
|
||||||
os.Exit(0)
|
flag.BoolVar(&showVersion, "version", false, "Show version")
|
||||||
}
|
flag.Usage = usageFor(flag.CommandLine, "syncthing [options]")
|
||||||
}
|
flag.Parse()
|
||||||
fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.ShowVersion {
|
if showVersion {
|
||||||
fmt.Println(Version)
|
fmt.Println(Version)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
@ -101,63 +69,73 @@ func main() {
|
|||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.Debug.TraceModel) > 0 || opts.Debug.LogSource {
|
if len(trace) > 0 {
|
||||||
log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
|
log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||||
logger.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
|
logger.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||||
}
|
}
|
||||||
opts.ConfDir = expandTilde(opts.ConfDir)
|
confDir = expandTilde(confDir)
|
||||||
|
|
||||||
infoln("Version", Version)
|
|
||||||
|
|
||||||
// Ensure that our home directory exists and that we have a certificate and key.
|
// Ensure that our home directory exists and that we have a certificate and key.
|
||||||
|
|
||||||
ensureDir(opts.ConfDir, 0700)
|
ensureDir(confDir, 0700)
|
||||||
cert, err := loadCert(opts.ConfDir)
|
cert, err := loadCert(confDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newCertificate(opts.ConfDir)
|
newCertificate(confDir)
|
||||||
cert, err = loadCert(opts.ConfDir)
|
cert, err = loadCert(confDir)
|
||||||
fatalErr(err)
|
fatalErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
myID = string(certId(cert.Certificate[0]))
|
myID = string(certId(cert.Certificate[0]))
|
||||||
infoln("My ID:", myID)
|
|
||||||
log.SetPrefix("[" + myID[0:5] + "] ")
|
log.SetPrefix("[" + myID[0:5] + "] ")
|
||||||
logger.SetPrefix("[" + myID[0:5] + "] ")
|
logger.SetPrefix("[" + myID[0:5] + "] ")
|
||||||
|
|
||||||
// Load the configuration file, if it exists.
|
// Load the configuration file, if it exists.
|
||||||
// If it does not, create a template.
|
// If it does not, create a template.
|
||||||
|
|
||||||
cfgFile := path.Join(opts.ConfDir, confFileName)
|
cfgFile := path.Join(confDir, confFileName)
|
||||||
cf, err := os.Open(cfgFile)
|
cf, err := os.Open(cfgFile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
infoln("My ID:", myID)
|
||||||
|
|
||||||
infoln("No config file; creating a template")
|
infoln("No config file; creating a template")
|
||||||
config = ini.Config{}
|
|
||||||
config.AddComment("repository", "Set the following to the directory you wish to synchronize")
|
loadConfig(nil, &opts) //loads defaults
|
||||||
config.AddComment("repository", "dir = ~/Syncthing")
|
|
||||||
config.Set("nodes", myID, "auto")
|
|
||||||
config.AddComment("nodes", "Add peer nodes here")
|
|
||||||
fd, err := os.Create(cfgFile)
|
fd, err := os.Create(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalln(err)
|
fatalln(err)
|
||||||
}
|
}
|
||||||
config.Write(fd)
|
|
||||||
|
writeConfig(fd, "~/Sync", map[string]string{myID: "dynamic"}, opts, true)
|
||||||
fd.Close()
|
fd.Close()
|
||||||
infof("Edit %s to suit and restart syncthing.", cfgFile)
|
infof("Edit %s to suit and restart syncthing.", cfgFile)
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
config = ini.Parse(cf)
|
config = ini.Parse(cf)
|
||||||
cf.Close()
|
cf.Close()
|
||||||
|
|
||||||
|
loadConfig(config.OptionMap("settings"), &opts)
|
||||||
|
|
||||||
|
if showConfig {
|
||||||
|
writeConfig(os.Stdout,
|
||||||
|
config.Get("repository", "dir"),
|
||||||
|
config.OptionMap("nodes"), opts, false)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
infoln("Version", Version)
|
||||||
|
infoln("My ID:", myID)
|
||||||
|
|
||||||
var dir = expandTilde(config.Get("repository", "dir"))
|
var dir = expandTilde(config.Get("repository", "dir"))
|
||||||
if len(dir) == 0 {
|
if len(dir) == 0 {
|
||||||
fatalln("No repository directory. Set dir under [repository] in syncthing.ini.")
|
fatalln("No repository directory. Set dir under [repository] in syncthing.ini.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Debug.Profiler != "" {
|
if len(profiler) > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
err := http.ListenAndServe(opts.Debug.Profiler, nil)
|
err := http.ListenAndServe(profiler, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnln(err)
|
warnln(err)
|
||||||
}
|
}
|
||||||
@ -186,16 +164,16 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureDir(dir, -1)
|
ensureDir(dir, -1)
|
||||||
m := model.NewModel(dir, opts.Advanced.MaxChangeBW)
|
m := model.NewModel(dir, opts.MaxChangeBW*1000)
|
||||||
for _, t := range opts.Debug.TraceModel {
|
for _, t := range strings.Split(trace, ",") {
|
||||||
m.Trace(t)
|
m.Trace(t)
|
||||||
}
|
}
|
||||||
if opts.Advanced.LimitRate > 0 {
|
if opts.LimitRate > 0 {
|
||||||
m.LimitRate(opts.Advanced.LimitRate)
|
m.LimitRate(opts.LimitRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GUI
|
// GUI
|
||||||
if !opts.NoGUI && opts.GUIAddr != "" {
|
if opts.GUI && opts.GUIAddr != "" {
|
||||||
host, port, err := net.SplitHostPort(opts.GUIAddr)
|
host, port, err := net.SplitHostPort(opts.GUIAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnf("Cannot start GUI on %q: %v", opts.GUIAddr, err)
|
warnf("Cannot start GUI on %q: %v", opts.GUIAddr, err)
|
||||||
@ -212,10 +190,6 @@ func main() {
|
|||||||
// Walk the repository and update the local model before establishing any
|
// Walk the repository and update the local model before establishing any
|
||||||
// connections to other nodes.
|
// connections to other nodes.
|
||||||
|
|
||||||
if !opts.Rehash {
|
|
||||||
infoln("Loading index cache")
|
|
||||||
loadIndex(m)
|
|
||||||
}
|
|
||||||
infoln("Populating repository index")
|
infoln("Populating repository index")
|
||||||
updateLocalModel(m)
|
updateLocalModel(m)
|
||||||
|
|
||||||
@ -230,13 +204,13 @@ func main() {
|
|||||||
// Routine to pull blocks from other nodes to synchronize the local
|
// Routine to pull blocks from other nodes to synchronize the local
|
||||||
// repository. Does not run when we are in read only (publish only) mode.
|
// repository. Does not run when we are in read only (publish only) mode.
|
||||||
if !opts.ReadOnly {
|
if !opts.ReadOnly {
|
||||||
if opts.NoDelete {
|
if opts.Delete {
|
||||||
infoln("Deletes from peer nodes will be ignored")
|
|
||||||
} else {
|
|
||||||
infoln("Deletes from peer nodes are allowed")
|
infoln("Deletes from peer nodes are allowed")
|
||||||
|
} else {
|
||||||
|
infoln("Deletes from peer nodes will be ignored")
|
||||||
}
|
}
|
||||||
okln("Ready to synchronize (read-write)")
|
okln("Ready to synchronize (read-write)")
|
||||||
m.StartRW(!opts.NoDelete, opts.Advanced.RequestsInFlight)
|
m.StartRW(opts.Delete, opts.RequestsInFlight)
|
||||||
} else {
|
} else {
|
||||||
okln("Ready to synchronize (read only; no external updates accepted)")
|
okln("Ready to synchronize (read only; no external updates accepted)")
|
||||||
}
|
}
|
||||||
@ -245,17 +219,15 @@ func main() {
|
|||||||
// XXX: Should use some fsnotify mechanism.
|
// XXX: Should use some fsnotify mechanism.
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(opts.Advanced.ScanInterval)
|
time.Sleep(opts.ScanInterval)
|
||||||
if m.LocalAge() > opts.Advanced.ScanInterval.Seconds()/2 {
|
if m.LocalAge() > opts.ScanInterval.Seconds()/2 {
|
||||||
updateLocalModel(m)
|
updateLocalModel(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !opts.NoStats {
|
|
||||||
// Periodically print statistics
|
// Periodically print statistics
|
||||||
go printStatsLoop(m)
|
go printStatsLoop(m)
|
||||||
}
|
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
@ -308,7 +280,7 @@ listen:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Debug.TraceConnect {
|
if strings.Contains(trace, "connect") {
|
||||||
debugln("NET: Connect from", conn.RemoteAddr())
|
debugln("NET: Connect from", conn.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,19 +320,19 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *model.M
|
|||||||
fatalErr(err)
|
fatalErr(err)
|
||||||
port, _ := strconv.Atoi(portstr)
|
port, _ := strconv.Atoi(portstr)
|
||||||
|
|
||||||
if opts.Discovery.NoLocalDiscovery {
|
if !opts.LocalDiscovery {
|
||||||
port = -1
|
port = -1
|
||||||
} else {
|
} else {
|
||||||
infoln("Sending local discovery announcements")
|
infoln("Sending local discovery announcements")
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Discovery.NoExternalDiscovery {
|
if !opts.ExternalDiscovery {
|
||||||
opts.Discovery.ExternalPort = -1
|
opts.ExternalServer = ""
|
||||||
} else {
|
} else {
|
||||||
infoln("Sending external discovery announcements")
|
infoln("Sending external discovery announcements")
|
||||||
}
|
}
|
||||||
|
|
||||||
disc, err := discover.NewDiscoverer(myID, port, opts.Discovery.ExternalPort, opts.Discovery.ExternalServer)
|
disc, err := discover.NewDiscoverer(myID, port, opts.ExternalServer)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnf("No discovery possible (%v)", err)
|
warnf("No discovery possible (%v)", err)
|
||||||
@ -391,12 +363,12 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *model.M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Debug.TraceConnect {
|
if strings.Contains(trace, "connect") {
|
||||||
debugln("NET: Dial", nodeID, addr)
|
debugln("NET: Dial", nodeID, addr)
|
||||||
}
|
}
|
||||||
conn, err := tls.Dial("tcp", addr, cfg)
|
conn, err := tls.Dial("tcp", addr, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if opts.Debug.TraceConnect {
|
if strings.Contains(trace, "connect") {
|
||||||
debugln("NET:", err)
|
debugln("NET:", err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -415,19 +387,19 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *model.M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(opts.Advanced.ConnInterval)
|
time.Sleep(opts.ConnInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLocalModel(m *model.Model) {
|
func updateLocalModel(m *model.Model) {
|
||||||
files, _ := m.Walk(!opts.NoSymlinks)
|
files, _ := m.Walk(opts.Symlinks)
|
||||||
m.ReplaceLocal(files)
|
m.ReplaceLocal(files)
|
||||||
saveIndex(m)
|
saveIndex(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveIndex(m *model.Model) {
|
func saveIndex(m *model.Model) {
|
||||||
name := m.RepoID() + ".idx.gz"
|
name := m.RepoID() + ".idx.gz"
|
||||||
fullName := path.Join(opts.ConfDir, name)
|
fullName := path.Join(confDir, name)
|
||||||
idxf, err := os.Create(fullName + ".tmp")
|
idxf, err := os.Create(fullName + ".tmp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -443,7 +415,7 @@ func saveIndex(m *model.Model) {
|
|||||||
|
|
||||||
func loadIndex(m *model.Model) {
|
func loadIndex(m *model.Model) {
|
||||||
name := m.RepoID() + ".idx.gz"
|
name := m.RepoID() + ".idx.gz"
|
||||||
idxf, err := os.Open(path.Join(opts.ConfDir, name))
|
idxf, err := os.Open(path.Join(confDir, name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
52
usage.go
Normal file
52
usage.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"text/tabwriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func optionTable(w io.Writer, rows [][]string) {
|
||||||
|
tw := tabwriter.NewWriter(w, 2, 4, 2, ' ', 0)
|
||||||
|
for _, row := range rows {
|
||||||
|
for i, cell := range row {
|
||||||
|
if i > 0 {
|
||||||
|
tw.Write([]byte("\t"))
|
||||||
|
}
|
||||||
|
tw.Write([]byte(cell))
|
||||||
|
}
|
||||||
|
tw.Write([]byte("\n"))
|
||||||
|
}
|
||||||
|
tw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func usageFor(fs *flag.FlagSet, usage string) func() {
|
||||||
|
return func() {
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString("Usage:\n " + usage + "\n")
|
||||||
|
|
||||||
|
var options [][]string
|
||||||
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
|
var dash = "-"
|
||||||
|
if len(f.Name) > 1 {
|
||||||
|
dash = "--"
|
||||||
|
}
|
||||||
|
var opt = " " + dash + f.Name
|
||||||
|
|
||||||
|
if f.DefValue != "false" {
|
||||||
|
opt += "=" + f.DefValue
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, []string{opt, f.Usage})
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(options) > 0 {
|
||||||
|
b.WriteString("\nOptions:\n")
|
||||||
|
optionTable(&b, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(b.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user