mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 14:50:56 +00:00
Command line flags
This commit is contained in:
parent
f774b0a5dc
commit
eba1c9e649
126
main.go
126
main.go
@ -17,34 +17,32 @@ import (
|
|||||||
"github.com/calmh/ini"
|
"github.com/calmh/ini"
|
||||||
"github.com/calmh/syncthing/discover"
|
"github.com/calmh/syncthing/discover"
|
||||||
"github.com/calmh/syncthing/protocol"
|
"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 (
|
const (
|
||||||
confDirName = ".syncthing"
|
confDirName = ".syncthing"
|
||||||
confFileName = "syncthing.ini"
|
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 (
|
var (
|
||||||
@ -54,66 +52,40 @@ var (
|
|||||||
|
|
||||||
// Options
|
// Options
|
||||||
var (
|
var (
|
||||||
confDir = path.Join(getHomeDir(), confDirName)
|
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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Useful for debugging; to be adjusted.
|
// Useful for debugging; to be adjusted.
|
||||||
log.SetFlags(log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ltime | log.Lshortfile)
|
||||||
|
|
||||||
arguments, _ := docopt.Parse(usage, nil, true, "syncthing 0.1", false)
|
_, err := flags.Parse(&opts)
|
||||||
|
if err != nil {
|
||||||
addr = arguments["-l"].(string)
|
os.Exit(0)
|
||||||
prof, _ = arguments["-p"].(string)
|
|
||||||
readOnly, _ = arguments["--ro"].(bool)
|
|
||||||
|
|
||||||
if arguments["--home"] != nil {
|
|
||||||
confDir, _ = arguments["--home"].(string)
|
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(opts.ConfDir, "~/") {
|
||||||
scanIntv, _ = strconv.Atoi(arguments["--scan-intv"].(string))
|
opts.ConfDir = strings.Replace(opts.ConfDir, "~", getHomeDir(), 1)
|
||||||
if scanIntv == 0 {
|
|
||||||
fatalln("Invalid --scan-intv")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// Ensure that our home directory exists and that we have a certificate and key.
|
||||||
|
|
||||||
ensureDir(confDir)
|
ensureDir(ConfDir)
|
||||||
cert, err := loadCert(confDir)
|
cert, err := loadCert(ConfDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newCertificate(confDir)
|
newCertificate(ConfDir)
|
||||||
cert, err = loadCert(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)
|
infoln("My ID:", myID)
|
||||||
|
|
||||||
if prof != "" {
|
if opts.Debug.Profiler != "" {
|
||||||
okln("Profiler listening on", prof)
|
|
||||||
go func() {
|
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.
|
// 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 {
|
if err != nil {
|
||||||
fatalln("No config file")
|
fatalln("No config file")
|
||||||
config = ini.Config{}
|
config = ini.Config{}
|
||||||
@ -159,15 +131,15 @@ func main() {
|
|||||||
|
|
||||||
// Routine to listen for incoming connections
|
// Routine to listen for incoming connections
|
||||||
infoln("Listening 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
|
// Routine to connect out to configured nodes
|
||||||
infoln("Attempting to connect to other 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
|
// 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 !readOnly {
|
if !opts.ReadOnly {
|
||||||
infoln("Cleaning out incomplete synchronizations")
|
infoln("Cleaning out incomplete synchronizations")
|
||||||
CleanTempFiles(dir)
|
CleanTempFiles(dir)
|
||||||
okln("Ready to synchronize")
|
okln("Ready to synchronize")
|
||||||
@ -178,7 +150,7 @@ func main() {
|
|||||||
// XXX: Should use some fsnotify mechanism.
|
// XXX: Should use some fsnotify mechanism.
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Duration(scanIntv) * time.Second)
|
time.Sleep(time.Duration(opts.ScanInterval) * time.Second)
|
||||||
updateLocalModel(m)
|
updateLocalModel(m)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -198,7 +170,7 @@ listen:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if traceNet {
|
if opts.Debug.TraceNet {
|
||||||
debugln("NET: Connect from", conn.RemoteAddr())
|
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)
|
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 traceNet {
|
if opts.Debug.TraceNet {
|
||||||
debugln("NET:", err)
|
debugln("NET:", err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -288,7 +260,7 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model,
|
|||||||
nc := protocol.NewConnection(nodeID, conn, conn, m)
|
nc := protocol.NewConnection(nodeID, conn, conn, m)
|
||||||
okln("Connected to node", remoteID, "(out)")
|
okln("Connected to node", remoteID, "(out)")
|
||||||
m.AddNode(nc)
|
m.AddNode(nc)
|
||||||
if traceNet {
|
if opts.Debug.TraceNet {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
nc.Ping()
|
nc.Ping()
|
||||||
timing("NET: Ping reply", t0)
|
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) {
|
func updateLocalModel(m *Model) {
|
||||||
files := Walk(m.Dir(), m, followSymlinks)
|
files := Walk(m.Dir(), m, !opts.NoSymlinks)
|
||||||
m.ReplaceLocal(files)
|
m.ReplaceLocal(files)
|
||||||
saveIndex(m)
|
saveIndex(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveIndex(m *Model) {
|
func saveIndex(m *Model) {
|
||||||
fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -319,7 +291,7 @@ func saveIndex(m *Model) {
|
|||||||
|
|
||||||
func loadIndex(m *Model) {
|
func loadIndex(m *Model) {
|
||||||
fname := fmt.Sprintf("%x.idx", sha1.Sum([]byte(m.Dir())))
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
16
model.go
16
model.go
@ -60,13 +60,13 @@ func (m *Model) Index(nodeID string, fs []protocol.FileInfo) {
|
|||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if traceNet {
|
if opts.Debug.TraceNet {
|
||||||
debugf("NET IDX(in): %s: %d files", nodeID, len(fs))
|
debugf("NET IDX(in): %s: %d files", nodeID, len(fs))
|
||||||
}
|
}
|
||||||
|
|
||||||
m.remote[nodeID] = make(map[string]File)
|
m.remote[nodeID] = make(map[string]File)
|
||||||
for _, f := range fs {
|
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
|
// Files marked as deleted do not even enter the model
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ func (m *Model) Close(node string) {
|
|||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if traceNet {
|
if opts.Debug.TraceNet {
|
||||||
debugf("NET CLOSE: %s", node)
|
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) {
|
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)
|
debugf("NET REQ(in): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
|
||||||
}
|
}
|
||||||
fn := path.Join(m.dir, name)
|
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]
|
nc := m.nodes[nodeID]
|
||||||
m.RUnlock()
|
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)
|
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() {
|
func (m *Model) broadcastIndex() {
|
||||||
idx := m.protocolIndex()
|
idx := m.protocolIndex()
|
||||||
for _, node := range m.nodes {
|
for _, node := range m.nodes {
|
||||||
if traceNet {
|
if opts.Debug.TraceNet {
|
||||||
debugf("NET IDX(out): %s: %d files", node.ID, len(idx))
|
debugf("NET IDX(out): %s: %d files", node.ID, len(idx))
|
||||||
}
|
}
|
||||||
node.Index(idx)
|
node.Index(idx)
|
||||||
@ -346,7 +346,7 @@ func (m *Model) protocolIndex() []protocol.FileInfo {
|
|||||||
Hash: b.Hash,
|
Hash: b.Hash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if traceIdx {
|
if opts.Debug.TraceIdx {
|
||||||
var flagComment string
|
var flagComment string
|
||||||
if mf.Flags&FlagDeleted != 0 {
|
if mf.Flags&FlagDeleted != 0 {
|
||||||
flagComment = " (deleted)"
|
flagComment = " (deleted)"
|
||||||
@ -366,7 +366,7 @@ func (m *Model) AddNode(node *protocol.Connection) {
|
|||||||
idx := m.protocolIndex()
|
idx := m.protocolIndex()
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
if traceNet {
|
if opts.Debug.TraceNet {
|
||||||
debugf("NET IDX(out): %s: %d files", node.ID, len(idx))
|
debugf("NET IDX(out): %s: %d files", node.ID, len(idx))
|
||||||
}
|
}
|
||||||
node.Index(idx)
|
node.Index(idx)
|
||||||
|
@ -171,12 +171,12 @@ func (m *Model) puller() {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
if f.Flags&FlagDeleted == 0 {
|
if f.Flags&FlagDeleted == 0 {
|
||||||
if traceFile {
|
if opts.Debug.TraceFile {
|
||||||
debugf("FILE: Pull %q", n)
|
debugf("FILE: Pull %q", n)
|
||||||
}
|
}
|
||||||
err = m.pullFile(n)
|
err = m.pullFile(n)
|
||||||
} else {
|
} else {
|
||||||
if traceFile {
|
if opts.Debug.TraceFile {
|
||||||
debugf("FILE: Remove %q", n)
|
debugf("FILE: Remove %q", n)
|
||||||
}
|
}
|
||||||
// Cheerfully ignore errors here
|
// Cheerfully ignore errors here
|
||||||
|
6
walk.go
6
walk.go
@ -62,7 +62,7 @@ func genWalker(base string, res *[]File, model *Model) filepath.WalkFunc {
|
|||||||
// No change
|
// No change
|
||||||
*res = append(*res, hf)
|
*res = append(*res, hf)
|
||||||
} else {
|
} else {
|
||||||
if traceFile {
|
if opts.Debug.TraceFile {
|
||||||
debugf("FILE: Hash %q", p)
|
debugf("FILE: Hash %q", p)
|
||||||
}
|
}
|
||||||
fd, err := os.Open(p)
|
fd, err := os.Open(p)
|
||||||
@ -97,7 +97,7 @@ func Walk(dir string, model *Model, followSymlinks bool) []File {
|
|||||||
warnln(err)
|
warnln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if followSymlinks {
|
if !opts.NoSymlinks {
|
||||||
d, err := os.Open(dir)
|
d, err := os.Open(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warnln(err)
|
warnln(err)
|
||||||
@ -127,7 +127,7 @@ func cleanTempFile(path string, info os.FileInfo, err error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.Mode()&os.ModeType == 0 && isTempName(path) {
|
if info.Mode()&os.ModeType == 0 && isTempName(path) {
|
||||||
if traceFile {
|
if opts.Debug.TraceFile {
|
||||||
debugf("FILE: Remove %q", path)
|
debugf("FILE: Remove %q", path)
|
||||||
}
|
}
|
||||||
os.Remove(path)
|
os.Remove(path)
|
||||||
|
Loading…
Reference in New Issue
Block a user