summaryrefslogtreecommitdiffstats
path: root/lib/syncthing/st.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/syncthing/st.go')
-rw-r--r--lib/syncthing/st.go242
1 files changed, 242 insertions, 0 deletions
diff --git a/lib/syncthing/st.go b/lib/syncthing/st.go
new file mode 100644
index 0000000..e513876
--- /dev/null
+++ b/lib/syncthing/st.go
@@ -0,0 +1,242 @@
+package st
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+ "syscall"
+ "time"
+
+ "fmt"
+
+ "os/exec"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/iotbzh/xds-agent/lib/common"
+ "github.com/iotbzh/xds-agent/lib/xdsconfig"
+ "github.com/syncthing/syncthing/lib/config"
+)
+
+// SyncThing .
+type SyncThing struct {
+ BaseURL string
+ ApiKey string
+ Home string
+ STCmd *exec.Cmd
+ STICmd *exec.Cmd
+
+ // Private fields
+ binDir string
+ exitSTChan chan ExitChan
+ exitSTIChan chan ExitChan
+ client *common.HTTPClient
+ log *logrus.Logger
+}
+
+// Monitor process exit
+type ExitChan struct {
+ status int
+ err error
+}
+
+// NewSyncThing creates a new instance of Syncthing
+//func NewSyncThing(url string, apiKey string, home string, log *logrus.Logger) *SyncThing {
+func NewSyncThing(conf *xdsconfig.SyncThingConf, log *logrus.Logger) *SyncThing {
+ url := conf.GuiAddress
+ apiKey := conf.GuiAPIKey
+ home := conf.Home
+
+ s := SyncThing{
+ BaseURL: url,
+ ApiKey: apiKey,
+ Home: home,
+ binDir: conf.BinDir,
+ log: log,
+ }
+
+ if s.BaseURL == "" {
+ s.BaseURL = "http://localhost:8384"
+ }
+ if s.BaseURL[0:7] != "http://" {
+ s.BaseURL = "http://" + s.BaseURL
+ }
+
+ return &s
+}
+
+// Start Starts syncthing process
+func (s *SyncThing) startProc(exeName string, args []string, env []string, eChan *chan ExitChan) (*exec.Cmd, error) {
+
+ // Kill existing process (useful for debug ;-) )
+ if os.Getenv("DEBUG_MODE") != "" {
+ exec.Command("bash", "-c", "pkill -9 "+exeName).Output()
+ }
+
+ path, err := exec.LookPath(path.Join(s.binDir, exeName))
+ if err != nil {
+ return nil, fmt.Errorf("Cannot find %s executable in %s", exeName, s.binDir)
+ }
+ cmd := exec.Command(path, args...)
+ cmd.Env = os.Environ()
+ for _, ev := range env {
+ cmd.Env = append(cmd.Env, ev)
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ return nil, err
+ }
+
+ *eChan = make(chan ExitChan, 1)
+ go func(c *exec.Cmd) {
+ status := 0
+ cmdOut, err := c.StdoutPipe()
+ if err == nil {
+ s.log.Errorf("Pipe stdout error for : %s", err)
+ } else if cmdOut != nil {
+ stdOutput, _ := ioutil.ReadAll(cmdOut)
+ fmt.Printf("STDOUT: %s\n", stdOutput)
+ }
+ sts, err := c.Process.Wait()
+ if !sts.Success() {
+ s := sts.Sys().(syscall.WaitStatus)
+ status = s.ExitStatus()
+ }
+ *eChan <- ExitChan{status, err}
+ }(cmd)
+
+ return cmd, nil
+}
+
+// Start Starts syncthing process
+func (s *SyncThing) Start() (*exec.Cmd, error) {
+ var err error
+ args := []string{
+ "--home=" + s.Home,
+ "-no-browser",
+ "--gui-address=" + s.BaseURL,
+ }
+
+ if s.ApiKey != "" {
+ args = append(args, "-gui-apikey=\""+s.ApiKey+"\"")
+ }
+ if s.log.Level == logrus.DebugLevel {
+ args = append(args, "-verbose")
+ }
+
+ env := []string{
+ "STNODEFAULTFOLDER=1",
+ }
+
+ s.STCmd, err = s.startProc("syncthing", args, env, &s.exitSTChan)
+
+ return s.STCmd, err
+}
+
+// StartInotify Starts syncthing-inotify process
+func (s *SyncThing) StartInotify() (*exec.Cmd, error) {
+ var err error
+
+ args := []string{
+ "--home=" + s.Home,
+ "-target=" + s.BaseURL,
+ }
+ if s.log.Level == logrus.DebugLevel {
+ args = append(args, "-verbosity=4")
+ }
+
+ env := []string{}
+
+ s.STICmd, err = s.startProc("syncthing-inotify", 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.Errorf("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.Errorf("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.client, err = common.HTTPNewClient(s.BaseURL,
+ common.HTTPClientConfig{
+ URLPrefix: "/rest",
+ HeaderClientKeyName: "X-Syncthing-ID",
+ })
+ if err != nil {
+ msg := ": " + err.Error()
+ if strings.Contains(err.Error(), "connection refused") {
+ msg = fmt.Sprintf("(url: %s)", s.BaseURL)
+ }
+ return fmt.Errorf("ERROR: cannot connect to Syncthing %s", msg)
+ }
+ if s.client == nil {
+ return fmt.Errorf("ERROR: cannot connect to Syncthing (null client)")
+ }
+ return nil
+}
+
+// IDGet returns the Syncthing ID of Syncthing instance running locally
+func (s *SyncThing) IDGet() (string, error) {
+ var data []byte
+ if err := s.client.HTTPGet("system/status", &data); err != nil {
+ return "", err
+ }
+ status := make(map[string]interface{})
+ json.Unmarshal(data, &status)
+ return status["myID"].(string), nil
+}
+
+// ConfigGet returns the current Syncthing configuration
+func (s *SyncThing) ConfigGet() (config.Configuration, error) {
+ var data []byte
+ config := config.Configuration{}
+ if err := s.client.HTTPGet("system/config", &data); err != nil {
+ return config, err
+ }
+ err := json.Unmarshal(data, &config)
+ return config, err
+}
+
+// ConfigSet set Syncthing configuration
+func (s *SyncThing) ConfigSet(cfg config.Configuration) error {
+ body, err := json.Marshal(cfg)
+ if err != nil {
+ return err
+ }
+ return s.client.HTTPPost("system/config", string(body))
+}