Command line flags

This commit is contained in:
Jakob Borg 2013-12-18 19:36:28 +01:00
parent f774b0a5dc
commit eba1c9e649
4 changed files with 62 additions and 90 deletions

126
main.go
View File

@ -17,34 +17,32 @@ import (
"github.com/calmh/ini"
"github.com/calmh/syncthing/discover"
"github.com/calmh/syncthing/protocol"
docopt "github.com/docopt/docopt.go"
flags "github.com/jessevdk/go-flags"
)
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 `long:"ro" description:"Repository is read only"`
Delete bool `long:"delete" description:"Delete files from repo when deleted from cluster"`
NoSymlinks bool `long:"no-symlinks" description:"Don't follow first level symlinks in the repo"`
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"`
Debug DebugOptions `group:"Debugging Options"`
}
type DebugOptions struct {
TraceFile bool `long:"trace-file"`
TraceNet bool `long:"trace-net"`
TraceIdx bool `long:"trace-idx"`
Profiler string `long:"profiler"`
}
var opts Options
const (
confDirName = ".syncthing"
confFileName = "syncthing.ini"
usage = `Usage:
syncthing [options]
Options:
-l <addr> Listening address [default: :22000]
-p <addr> Enable HTTP profiler on addr
--home <path> Home directory
--delete Delete files that were deleted on a peer node
--ro Local repository is read only
--scan-intv <s> Repository scan interval, in seconds [default: 60]
--conn-intv <s> Node reconnect interval, in seconds [default: 15]
--no-symlinks Don't follow first level symlinks in the repo
Help Options:
-h, --help Show this help
--version Show version
Debug Options:
--trace-file Trace file operations
--trace-net Trace network operations
--trace-idx Trace sent indexes
`
)
var (
@ -54,66 +52,40 @@ var (
// Options
var (
confDir = path.Join(getHomeDir(), confDirName)
addr string
prof string
readOnly bool
scanIntv int
connIntv int
traceNet bool
traceFile bool
traceIdx bool
doDelete bool
followSymlinks bool
ConfDir = path.Join(getHomeDir(), confDirName)
)
func main() {
// Useful for debugging; to be adjusted.
log.SetFlags(log.Ltime | log.Lshortfile)
arguments, _ := docopt.Parse(usage, nil, true, "syncthing 0.1", false)
addr = arguments["-l"].(string)
prof, _ = arguments["-p"].(string)
readOnly, _ = arguments["--ro"].(bool)
if arguments["--home"] != nil {
confDir, _ = arguments["--home"].(string)
_, err := flags.Parse(&opts)
if err != nil {
os.Exit(0)
}
scanIntv, _ = strconv.Atoi(arguments["--scan-intv"].(string))
if scanIntv == 0 {
fatalln("Invalid --scan-intv")
if strings.HasPrefix(opts.ConfDir, "~/") {
opts.ConfDir = strings.Replace(opts.ConfDir, "~", getHomeDir(), 1)
}
connIntv, _ = strconv.Atoi(arguments["--conn-intv"].(string))
if connIntv == 0 {
fatalln("Invalid --conn-intv")
}
doDelete = arguments["--delete"].(bool)
traceFile = arguments["--trace-file"].(bool)
traceNet = arguments["--trace-net"].(bool)
traceIdx = arguments["--trace-idx"].(bool)
followSymlinks = !arguments["--no-symlinks"].(bool)
// Ensure that our home directory exists and that we have a certificate and key.
ensureDir(confDir)
cert, err := loadCert(confDir)
ensureDir(ConfDir)
cert, err := loadCert(ConfDir)
if err != nil {
newCertificate(confDir)
cert, err = loadCert(confDir)
newCertificate(ConfDir)
cert, err = loadCert(ConfDir)
fatalErr(err)
}
myID := string(certId(cert.Certificate[0]))
infoln("My ID:", myID)
if prof != "" {
okln("Profiler listening on", prof)
if opts.Debug.Profiler != "" {
go func() {
http.ListenAndServe(prof, nil)
err := http.ListenAndServe(opts.Debug.Profiler, nil)
if err != nil {
warnln(err)
}
}()
}
@ -130,7 +102,7 @@ func main() {
// Load the configuration file, if it exists.
cf, err := os.Open(path.Join(confDir, confFileName))
cf, err := os.Open(path.Join(ConfDir, confFileName))
if err != nil {
fatalln("No config file")
config = ini.Config{}
@ -159,15 +131,15 @@ func main() {
// Routine to listen for incoming connections
infoln("Listening for incoming connections")
go listen(myID, addr, m, cfg)
go listen(myID, opts.Listen, m, cfg)
// Routine to connect out to configured nodes
infoln("Attempting to connect to other nodes")
go connect(myID, addr, nodeAddrs, m, cfg)
go connect(myID, opts.Listen, nodeAddrs, m, cfg)
// Routine to pull blocks from other nodes to synchronize the local
// repository. Does not run when we are in read only (publish only) mode.
if !readOnly {
if !opts.ReadOnly {
infoln("Cleaning out incomplete synchronizations")
CleanTempFiles(dir)
okln("Ready to synchronize")
@ -178,7 +150,7 @@ func main() {
// XXX: Should use some fsnotify mechanism.
go func() {
for {
time.Sleep(time.Duration(scanIntv) * time.Second)
time.Sleep(time.Duration(opts.ScanInterval) * time.Second)
updateLocalModel(m)
}
}()
@ -198,7 +170,7 @@ listen:
continue
}
if traceNet {
if opts.Debug.TraceNet {
debugln("NET: Connect from", conn.RemoteAddr())
}
@ -267,12 +239,12 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model,
}
}
if traceNet {
if opts.Debug.TraceNet {
debugln("NET: Dial", nodeID, addr)
}
conn, err := tls.Dial("tcp", addr, cfg)
if err != nil {
if traceNet {
if opts.Debug.TraceNet {
debugln("NET:", err)
}
continue
@ -288,7 +260,7 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model,
nc := protocol.NewConnection(nodeID, conn, conn, m)
okln("Connected to node", remoteID, "(out)")
m.AddNode(nc)
if traceNet {
if opts.Debug.TraceNet {
t0 := time.Now()
nc.Ping()
timing("NET: Ping reply", t0)
@ -297,19 +269,19 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model,
}
}
time.Sleep(time.Duration(connIntv) * time.Second)
time.Sleep(time.Duration(opts.ConnInterval) * time.Second)
}
}
func updateLocalModel(m *Model) {
files := Walk(m.Dir(), m, followSymlinks)
files := Walk(m.Dir(), m, !opts.NoSymlinks)
m.ReplaceLocal(files)
saveIndex(m)
}
func saveIndex(m *Model) {
fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
idxf, err := os.Create(path.Join(confDir, fname))
idxf, err := os.Create(path.Join(ConfDir, fname))
if err != nil {
return
}
@ -319,7 +291,7 @@ func saveIndex(m *Model) {
func loadIndex(m *Model) {
fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
idxf, err := os.Open(path.Join(confDir, fname))
idxf, err := os.Open(path.Join(ConfDir, fname))
if err != nil {
return
}

View File

@ -60,13 +60,13 @@ func (m *Model) Index(nodeID string, fs []protocol.FileInfo) {
m.Lock()
defer m.Unlock()
if traceNet {
if opts.Debug.TraceNet {
debugf("NET IDX(in): %s: %d files", nodeID, len(fs))
}
m.remote[nodeID] = make(map[string]File)
for _, f := range fs {
if f.Flags&FlagDeleted != 0 && !doDelete {
if f.Flags&FlagDeleted != 0 && !opts.Delete {
// Files marked as deleted do not even enter the model
continue
}
@ -122,7 +122,7 @@ func (m *Model) Close(node string) {
m.Lock()
defer m.Unlock()
if traceNet {
if opts.Debug.TraceNet {
debugf("NET CLOSE: %s", node)
}
@ -134,7 +134,7 @@ func (m *Model) Close(node string) {
}
func (m *Model) Request(nodeID, name string, offset uint64, size uint32, hash []byte) ([]byte, error) {
if traceNet && nodeID != "<local>" {
if opts.Debug.TraceNet && nodeID != "<local>" {
debugf("NET REQ(in): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
}
fn := path.Join(m.dir, name)
@ -158,7 +158,7 @@ func (m *Model) RequestGlobal(nodeID, name string, offset uint64, size uint32, h
nc := m.nodes[nodeID]
m.RUnlock()
if traceNet {
if opts.Debug.TraceNet {
debugf("NET REQ(out): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
}
@ -200,7 +200,7 @@ func (m *Model) ReplaceLocal(fs []File) {
func (m *Model) broadcastIndex() {
idx := m.protocolIndex()
for _, node := range m.nodes {
if traceNet {
if opts.Debug.TraceNet {
debugf("NET IDX(out): %s: %d files", node.ID, len(idx))
}
node.Index(idx)
@ -346,7 +346,7 @@ func (m *Model) protocolIndex() []protocol.FileInfo {
Hash: b.Hash,
})
}
if traceIdx {
if opts.Debug.TraceIdx {
var flagComment string
if mf.Flags&FlagDeleted != 0 {
flagComment = " (deleted)"
@ -366,7 +366,7 @@ func (m *Model) AddNode(node *protocol.Connection) {
idx := m.protocolIndex()
m.RUnlock()
if traceNet {
if opts.Debug.TraceNet {
debugf("NET IDX(out): %s: %d files", node.ID, len(idx))
}
node.Index(idx)

View File

@ -171,12 +171,12 @@ func (m *Model) puller() {
var err error
if f.Flags&FlagDeleted == 0 {
if traceFile {
if opts.Debug.TraceFile {
debugf("FILE: Pull %q", n)
}
err = m.pullFile(n)
} else {
if traceFile {
if opts.Debug.TraceFile {
debugf("FILE: Remove %q", n)
}
// Cheerfully ignore errors here

View File

@ -62,7 +62,7 @@ func genWalker(base string, res *[]File, model *Model) filepath.WalkFunc {
// No change
*res = append(*res, hf)
} else {
if traceFile {
if opts.Debug.TraceFile {
debugf("FILE: Hash %q", p)
}
fd, err := os.Open(p)
@ -97,7 +97,7 @@ func Walk(dir string, model *Model, followSymlinks bool) []File {
warnln(err)
}
if followSymlinks {
if !opts.NoSymlinks {
d, err := os.Open(dir)
if err != nil {
warnln(err)
@ -127,7 +127,7 @@ func cleanTempFile(path string, info os.FileInfo, err error) error {
return err
}
if info.Mode()&os.ModeType == 0 && isTempName(path) {
if traceFile {
if opts.Debug.TraceFile {
debugf("FILE: Remove %q", path)
}
os.Remove(path)