+func NewSyncThing(conf *xdsconfig.Config, log *logrus.Logger) *SyncThing {
+ var url, apiKey, home, binDir string
+
+ stCfg := conf.FileConf.SThgConf
+ if stCfg != nil {
+ url = stCfg.GuiAddress
+ apiKey = stCfg.GuiAPIKey
+ home = stCfg.Home
+ binDir = stCfg.BinDir
+ }
+
+ if url == "" {
+ url = "http://localhost:8385"
+ }
+ if url[0:7] != "http://" {
+ url = "http://" + url
+ }
+
+ if home == "" {
+ panic("home parameter must be set")
+ }
+
+ s := SyncThing{
+ BaseURL: url,
+ APIKey: apiKey,
+ Home: home,
+ 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") != "" {
+ 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)
+ }
+ }
+ }
+
+ exePath, err = exec.LookPath(path.Join(bdir, exeName))
+ if err != nil {
+ // 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(exePath, args...)
+ cmd.Env = os.Environ()
+ for _, ev := range env {
+ cmd.Env = append(cmd.Env, ev)
+ }
+
+ // open log file
+ var outfile *os.File
+ logFilename := filepath.Join(s.logsDir, exeName+".log")
+ if s.logsDir != "" {
+ outfile, err := os.Create(logFilename)
+ if err != nil {
+ return nil, fmt.Errorf("Cannot create log file %s", logFilename)
+ }
+
+ cmdOut, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, fmt.Errorf("Pipe stdout error for : %v", err)
+ }
+
+ go io.Copy(outfile, cmdOut)
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ return nil, err
+ }
+
+ *eChan = make(chan ExitChan, 1)
+ go func(c *exec.Cmd, oF *os.File) {
+ status := 0
+ sts, err := c.Process.Wait()
+ if !sts.Success() {
+ s := sts.Sys().(syscall.WaitStatus)
+ status = s.ExitStatus()
+ }
+ if oF != nil {
+ oF.Close()
+ }
+ s.log.Debugf("%s exited with status %d, err %v", exeName, status, err)
+
+ *eChan <- ExitChan{status, err}
+ }(cmd, outfile)
+
+ return cmd, nil
+}
+
+// Start Starts syncthing process
+func (s *SyncThing) Start() (*exec.Cmd, error) {
+ var err error
+
+ s.log.Infof(" ST home=%s", s.Home)
+ s.log.Infof(" ST url=%s", s.BaseURL)
+
+ args := []string{
+ "--home=" + s.Home,
+ "-no-browser",
+ "--gui-address=" + s.BaseURL,
+ }
+
+ if s.APIKey != "" {
+ args = append(args, "-gui-apikey=\""+s.APIKey+"\"")
+ s.log.Infof(" ST apikey=%s", s.APIKey)
+ }
+ if s.log.Level == logrus.DebugLevel {
+ args = append(args, "-verbose")
+ }
+
+ env := []string{
+ "STNODEFAULTFOLDER=1",
+ "STNOUPGRADE=1",
+ }
+
+ s.STCmd, err = s.startProc("syncthing", args, env, &s.exitSTChan)
+
+ // Use autogenerated apikey if not set by server-config.json
+ 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 {
+ re := regexp.MustCompile("<apikey>(.*)</apikey>")
+ key := re.FindStringSubmatch(string(b))
+ if len(key) >= 1 {
+ s.APIKey = key[1]
+ }
+ }
+ }
+ }
+
+ return s.STCmd, err
+}
+
+// StartInotify Starts syncthing-inotify process
+func (s *SyncThing) StartInotify() (*exec.Cmd, error) {
+ var err error
+ exeName := "syncthing-inotify"
+
+ s.log.Infof(" STI url=%s", s.BaseURL)
+
+ args := []string{
+ "-target=" + s.BaseURL,
+ }
+ if s.APIKey != "" {
+ args = append(args, "-api="+s.APIKey)
+ s.log.Infof("%s uses apikey=%s", exeName, s.APIKey)
+ }
+ if s.log.Level == logrus.DebugLevel {
+ args = append(args, "-verbosity=4")
+ }
+
+ env := []string{}
+
+ s.STICmd, err = s.startProc(exeName, args, env, &s.exitSTIChan)
+
+ return s.STICmd, err
+}
+
+func (s *SyncThing) stopProc(pname string, proc *os.Process, exit chan ExitChan) {
+ if err := proc.Signal(os.Interrupt); err != nil {
+ s.log.Infof("Proc interrupt %s error: %s", pname, err.Error())
+
+ select {
+ case <-exit:
+ case <-time.After(time.Second):
+ // A bigger bonk on the head.
+ if err := proc.Signal(os.Kill); err != nil {
+ s.log.Infof("Proc term %s error: %s", pname, err.Error())
+ }
+ <-exit
+ }
+ }
+ s.log.Infof("%s stopped (PID %d)", pname, proc.Pid)
+}
+
+// Stop Stops syncthing process
+func (s *SyncThing) Stop() {
+ if s.STCmd == nil {
+ return
+ }
+ s.stopProc("syncthing", s.STCmd.Process, s.exitSTChan)
+ s.STCmd = nil
+}
+
+// StopInotify Stops syncthing process
+func (s *SyncThing) StopInotify() {
+ if s.STICmd == nil {
+ return
+ }
+ s.stopProc("syncthing-inotify", s.STICmd.Process, s.exitSTIChan)
+ s.STICmd = nil
+}
+
+// Connect Establish HTTP connection with Syncthing
+func (s *SyncThing) Connect() error {
+ var err error
+ s.Connected = false
+ s.client, err = common.HTTPNewClient(s.BaseURL,