16 "github.com/Sirupsen/logrus"
17 "github.com/iotbzh/xds-agent/lib/common"
18 "github.com/iotbzh/xds-agent/lib/xdsconfig"
19 "github.com/syncthing/syncthing/lib/config"
23 type SyncThing struct {
32 exitSTChan chan ExitChan
33 exitSTIChan chan ExitChan
34 client *common.HTTPClient
38 // Monitor process exit
39 type ExitChan struct {
44 // NewSyncThing creates a new instance of Syncthing
45 //func NewSyncThing(url string, apiKey string, home string, log *logrus.Logger) *SyncThing {
46 func NewSyncThing(conf *xdsconfig.SyncThingConf, log *logrus.Logger) *SyncThing {
47 url := conf.GuiAddress
48 apiKey := conf.GuiAPIKey
60 s.BaseURL = "http://localhost:8384"
62 if s.BaseURL[0:7] != "http://" {
63 s.BaseURL = "http://" + s.BaseURL
69 // Start Starts syncthing process
70 func (s *SyncThing) startProc(exeName string, args []string, env []string, eChan *chan ExitChan) (*exec.Cmd, error) {
72 // Kill existing process (useful for debug ;-) )
73 if os.Getenv("DEBUG_MODE") != "" {
74 exec.Command("bash", "-c", "pkill -9 "+exeName).Output()
77 path, err := exec.LookPath(path.Join(s.binDir, exeName))
79 return nil, fmt.Errorf("Cannot find %s executable in %s", exeName, s.binDir)
81 cmd := exec.Command(path, args...)
82 cmd.Env = os.Environ()
83 for _, ev := range env {
84 cmd.Env = append(cmd.Env, ev)
92 *eChan = make(chan ExitChan, 1)
93 go func(c *exec.Cmd) {
95 cmdOut, err := c.StdoutPipe()
97 s.log.Errorf("Pipe stdout error for : %s", err)
98 } else if cmdOut != nil {
99 stdOutput, _ := ioutil.ReadAll(cmdOut)
100 fmt.Printf("STDOUT: %s\n", stdOutput)
102 sts, err := c.Process.Wait()
104 s := sts.Sys().(syscall.WaitStatus)
105 status = s.ExitStatus()
107 *eChan <- ExitChan{status, err}
113 // Start Starts syncthing process
114 func (s *SyncThing) Start() (*exec.Cmd, error) {
119 "--gui-address=" + s.BaseURL,
123 args = append(args, "-gui-apikey=\""+s.ApiKey+"\"")
125 if s.log.Level == logrus.DebugLevel {
126 args = append(args, "-verbose")
130 "STNODEFAULTFOLDER=1",
133 s.STCmd, err = s.startProc("syncthing", args, env, &s.exitSTChan)
138 // StartInotify Starts syncthing-inotify process
139 func (s *SyncThing) StartInotify() (*exec.Cmd, error) {
144 "-target=" + s.BaseURL,
146 if s.log.Level == logrus.DebugLevel {
147 args = append(args, "-verbosity=4")
152 s.STICmd, err = s.startProc("syncthing-inotify", args, env, &s.exitSTIChan)
157 func (s *SyncThing) stopProc(pname string, proc *os.Process, exit chan ExitChan) {
158 if err := proc.Signal(os.Interrupt); err != nil {
159 s.log.Errorf("Proc interrupt %s error: %s", pname, err.Error())
163 case <-time.After(time.Second):
164 // A bigger bonk on the head.
165 if err := proc.Signal(os.Kill); err != nil {
166 s.log.Errorf("Proc term %s error: %s", pname, err.Error())
171 s.log.Infof("%s stopped (PID %d)", pname, proc.Pid)
174 // Stop Stops syncthing process
175 func (s *SyncThing) Stop() {
179 s.stopProc("syncthing", s.STCmd.Process, s.exitSTChan)
183 // StopInotify Stops syncthing process
184 func (s *SyncThing) StopInotify() {
188 s.stopProc("syncthing-inotify", s.STICmd.Process, s.exitSTIChan)
192 // Connect Establish HTTP connection with Syncthing
193 func (s *SyncThing) Connect() error {
195 s.client, err = common.HTTPNewClient(s.BaseURL,
196 common.HTTPClientConfig{
198 HeaderClientKeyName: "X-Syncthing-ID",
201 msg := ": " + err.Error()
202 if strings.Contains(err.Error(), "connection refused") {
203 msg = fmt.Sprintf("(url: %s)", s.BaseURL)
205 return fmt.Errorf("ERROR: cannot connect to Syncthing %s", msg)
208 return fmt.Errorf("ERROR: cannot connect to Syncthing (null client)")
213 // IDGet returns the Syncthing ID of Syncthing instance running locally
214 func (s *SyncThing) IDGet() (string, error) {
216 if err := s.client.HTTPGet("system/status", &data); err != nil {
219 status := make(map[string]interface{})
220 json.Unmarshal(data, &status)
221 return status["myID"].(string), nil
224 // ConfigGet returns the current Syncthing configuration
225 func (s *SyncThing) ConfigGet() (config.Configuration, error) {
227 config := config.Configuration{}
228 if err := s.client.HTTPGet("system/config", &data); err != nil {
231 err := json.Unmarshal(data, &config)
235 // ConfigSet set Syncthing configuration
236 func (s *SyncThing) ConfigSet(cfg config.Configuration) error {
237 body, err := json.Marshal(cfg)
241 return s.client.HTTPPost("system/config", string(body))