X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=lib%2Fsyncthing%2Fst.go;h=924f4075774e0d17562e131039b6d98a46aef7e1;hb=4695555e178bcabe54c5bf82117c9c4cef5440b5;hp=5976e2f769c25fb273ffec032888f47ab3ddd03e;hpb=a50baa7c309f7eb55fe87c71f4c688ace325b6ac;p=src%2Fxds%2Fxds-agent.git diff --git a/lib/syncthing/st.go b/lib/syncthing/st.go index 5976e2f..924f407 100644 --- a/lib/syncthing/st.go +++ b/lib/syncthing/st.go @@ -17,18 +17,21 @@ import ( "os/exec" "github.com/Sirupsen/logrus" - "github.com/iotbzh/xds-agent/lib/common" "github.com/iotbzh/xds-agent/lib/xdsconfig" + common "github.com/iotbzh/xds-common/golib" "github.com/syncthing/syncthing/lib/config" ) // SyncThing . type SyncThing struct { - BaseURL string - APIKey string - Home string - STCmd *exec.Cmd - STICmd *exec.Cmd + BaseURL string + APIKey string + Home string + STCmd *exec.Cmd + STICmd *exec.Cmd + MyID string + Connected bool + Events *Events // Private fields binDir string @@ -37,6 +40,7 @@ type SyncThing struct { exitSTIChan chan ExitChan client *common.HTTPClient log *logrus.Logger + conf *xdsconfig.Config } // ExitChan Channel used for process exit @@ -45,10 +49,45 @@ type ExitChan struct { err error } +// ConfigInSync Check whether if Syncthing configuration is in sync +type configInSync struct { + ConfigInSync bool `json:"configInSync"` +} + +// FolderStatus Information about the current status of a folder. +type FolderStatus struct { + GlobalFiles int `json:"globalFiles"` + GlobalDirectories int `json:"globalDirectories"` + GlobalSymlinks int `json:"globalSymlinks"` + GlobalDeleted int `json:"globalDeleted"` + GlobalBytes int64 `json:"globalBytes"` + + LocalFiles int `json:"localFiles"` + LocalDirectories int `json:"localDirectories"` + LocalSymlinks int `json:"localSymlinks"` + LocalDeleted int `json:"localDeleted"` + LocalBytes int64 `json:"localBytes"` + + NeedFiles int `json:"needFiles"` + NeedDirectories int `json:"needDirectories"` + NeedSymlinks int `json:"needSymlinks"` + NeedDeletes int `json:"needDeletes"` + NeedBytes int64 `json:"needBytes"` + + InSyncFiles int `json:"inSyncFiles"` + InSyncBytes int64 `json:"inSyncBytes"` + + State string `json:"state"` + StateChanged time.Time `json:"stateChanged"` + + Sequence int64 `json:"sequence"` + + IgnorePatterns bool `json:"ignorePatterns"` +} + // NewSyncThing creates a new instance of Syncthing func NewSyncThing(conf *xdsconfig.Config, log *logrus.Logger) *SyncThing { var url, apiKey, home, binDir string - var err error stCfg := conf.FileConf.SThgConf if stCfg != nil { @@ -66,13 +105,7 @@ func NewSyncThing(conf *xdsconfig.Config, log *logrus.Logger) *SyncThing { } if home == "" { - home = "/mnt/share" - } - - if binDir == "" { - if binDir, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { - binDir = "/usr/local/bin" - } + panic("home parameter must be set") } s := SyncThing{ @@ -82,24 +115,46 @@ func NewSyncThing(conf *xdsconfig.Config, log *logrus.Logger) *SyncThing { binDir: binDir, logsDir: conf.FileConf.LogsDir, log: log, + conf: conf, } + // Create Events monitoring + s.Events = s.NewEventListener() + return &s } // Start Starts syncthing process func (s *SyncThing) startProc(exeName string, args []string, env []string, eChan *chan ExitChan) (*exec.Cmd, error) { + var err error + var exePath string // Kill existing process (useful for debug ;-) ) - if os.Getenv("DEBUG_MODE") != "" { - exec.Command("bash", "-c", "pkill -9 "+exeName).Output() + if _, dbg := os.LookupEnv("XDS_DEBUG_MODE"); dbg { + fmt.Printf("\n!!! DEBUG_MODE set: KILL existing %s process(es) !!!\n", exeName) + exec.Command("bash", "-c", "ps -ax |grep "+exeName+" |grep "+s.BaseURL+" |cut -d' ' -f 1|xargs -I{} kill -9 {}").Output() + } + + // When not set (or set to '.') set bin to path of xds-agent executable + bdir := s.binDir + if bdir == "" || bdir == "." { + exe, _ := os.Executable() + if exeAbsPath, err := filepath.Abs(exe); err == nil { + if exePath, err := filepath.EvalSymlinks(exeAbsPath); err == nil { + bdir = filepath.Dir(exePath) + } + } } - path, err := exec.LookPath(path.Join(s.binDir, exeName)) + exePath, err = exec.LookPath(path.Join(bdir, exeName)) if err != nil { - return nil, fmt.Errorf("Cannot find %s executable in %s", exeName, s.binDir) + // Let's try in /opt/AGL/bin + exePath, err = exec.LookPath(path.Join("opt", "AGL", "bin", exeName)) + if err != nil { + return nil, fmt.Errorf("Cannot find %s executable in %s", exeName, bdir) + } } - cmd := exec.Command(path, args...) + cmd := exec.Command(exePath, args...) cmd.Env = os.Environ() for _, ev := range env { cmd.Env = append(cmd.Env, ev) @@ -169,12 +224,45 @@ func (s *SyncThing) Start() (*exec.Cmd, error) { env := []string{ "STNODEFAULTFOLDER=1", + "STNOUPGRADE=1", } + /* FIXME - STILL NEEDED ?, if not SUP code + + // XXX - temporary hack because -gui-apikey seems to correctly handle by + // syncthing the early first time + stConfigFile := filepath.Join(s.Home, "config.xml") + if s.APIKey != "" && !common.Exists(stConfigFile) { + s.log.Infof("Stop and restart Syncthing (hack for apikey setting)") + s.STCmd, err = s.startProc("syncthing", args, env, &s.exitSTChan) + tmo := 20 + for ; tmo > 0; tmo-- { + s.log.Debugf("Waiting Syncthing config.xml creation (%v)\n", tmo) + time.Sleep(500 * time.Millisecond) + if common.Exists(stConfigFile) { + break + } + } + if tmo <= 0 { + return nil, fmt.Errorf("Cannot start Syncthing for config file creation") + } + s.Stop() + read, err := ioutil.ReadFile(stConfigFile) + if err != nil { + return nil, fmt.Errorf("Cannot read Syncthing config file for apikey setting") + } + re := regexp.MustCompile(`.*`) + newContents := re.ReplaceAllString(string(read), ""+s.APIKey+"") + err = ioutil.WriteFile(stConfigFile, []byte(newContents), 0) + if err != nil { + return nil, fmt.Errorf("Cannot write Syncthing config file to set apikey") + } + } + */ s.STCmd, err = s.startProc("syncthing", args, env, &s.exitSTChan) // Use autogenerated apikey if not set by config.json - if s.APIKey == "" { + if err == nil && s.APIKey == "" { if fd, err := os.Open(filepath.Join(s.Home, "config.xml")); err == nil { defer fd.Close() if b, err := ioutil.ReadAll(fd); err == nil { @@ -253,11 +341,17 @@ func (s *SyncThing) StopInotify() { // Connect Establish HTTP connection with Syncthing func (s *SyncThing) Connect() error { var err error + s.Connected = false s.client, err = common.HTTPNewClient(s.BaseURL, common.HTTPClientConfig{ URLPrefix: "/rest", HeaderClientKeyName: "X-Syncthing-ID", + LogOut: s.conf.LogVerboseOut, + LogPrefix: "SYNCTHING: ", + LogLevel: common.HTTPLogLevelWarning, }) + s.client.SetLogLevel(s.log.Level.String()) + if err != nil { msg := ": " + err.Error() if strings.Contains(err.Error(), "connection refused") { @@ -269,9 +363,17 @@ func (s *SyncThing) Connect() error { return fmt.Errorf("ERROR: cannot connect to Syncthing (null client)") } - s.client.SetLogger(s.log) + s.MyID, err = s.IDGet() + if err != nil { + return fmt.Errorf("ERROR: cannot retrieve ID") + } + + s.Connected = true + + // Start events monitoring + err = s.Events.Start() - return nil + return err } // IDGet returns the Syncthing ID of Syncthing instance running locally @@ -304,3 +406,16 @@ func (s *SyncThing) ConfigSet(cfg config.Configuration) error { } return s.client.HTTPPost("system/config", string(body)) } + +// IsConfigInSync Returns true if configuration is in sync +func (s *SyncThing) IsConfigInSync() (bool, error) { + var data []byte + var d configInSync + if err := s.client.HTTPGet("system/config/insync", &data); err != nil { + return false, err + } + if err := json.Unmarshal(data, &d); err != nil { + return false, err + } + return d.ConfigInSync, nil +}