11 "github.com/Sirupsen/logrus"
16 MonitorTime time.Duration
22 cbArr map[string][]cbMap
26 Type string `json:"type"`
27 Time time.Time `json:"time"`
28 Data map[string]string `json:"data"`
31 type EventsCBData map[string]interface{}
32 type EventsCB func(ev Event, cbData *EventsCBData)
35 EventFolderCompletion string = "FolderCompletion"
36 EventFolderSummary string = "FolderSummary"
37 EventFolderPaused string = "FolderPaused"
38 EventFolderResumed string = "FolderResumed"
39 EventFolderErrors string = "FolderErrors"
40 EventStateChanged string = "StateChanged"
43 var EventsAll string = EventFolderCompletion + "|" +
44 EventFolderSummary + "|" +
45 EventFolderPaused + "|" +
46 EventFolderResumed + "|" +
47 EventFolderErrors + "|" +
51 // Per-subscription sequential event ID. Named "id" for backwards compatibility with the REST API
52 SubscriptionID int `json:"id"`
53 // Global ID of the event across all subscriptions
54 GlobalID int `json:"globalID"`
55 Time time.Time `json:"time"`
56 Type string `json:"type"`
57 Data map[string]interface{} `json:"data"`
67 // NewEventListener Create a new instance of Event listener
68 func (s *SyncThing) NewEventListener() *Events {
69 _, dbg := os.LookupEnv("XDS_DEBUG_STEVENTS") // set to add more debug log
71 MonitorTime: 100, // in Milliseconds
73 stop: make(chan bool, 1),
76 cbArr: make(map[string][]cbMap),
80 // Start starts event monitoring loop
81 func (e *Events) Start() error {
86 // Stop stops event monitoring loop
87 func (e *Events) Stop() {
91 // Register Add a listener on an event
92 func (e *Events) Register(evName string, cb EventsCB, filterID string, data *EventsCBData) (int, error) {
93 if evName == "" || !strings.Contains(EventsAll, evName) {
94 return -1, fmt.Errorf("Unknown event name")
97 data = &EventsCBData{}
101 if _, ok := e.cbArr[evName]; ok {
102 cbList = e.cbArr[evName]
106 (*data)["id"] = strconv.Itoa(id)
108 e.cbArr[evName] = append(cbList, cbMap{id: id, cb: cb, filterID: filterID, data: data})
113 // UnRegister Remove a listener event
114 func (e *Events) UnRegister(evName string, id int) error {
115 cbKey, ok := e.cbArr[evName]
117 return fmt.Errorf("No event registered to such name")
120 // FIXME - NOT TESTED
121 if id >= len(cbKey) {
122 return fmt.Errorf("Invalid id")
123 } else if id == len(cbKey) {
124 e.cbArr[evName] = cbKey[:id-1]
126 e.cbArr[evName] = cbKey[id : id+1]
132 // GetEvents returns the Syncthing events
133 func (e *Events) getEvents(since int) ([]STEvent, error) {
138 url += "?since=" + strconv.Itoa(since)
140 if err := e.st.client.HTTPGet(url, &data); err != nil {
143 err := json.Unmarshal(data, &ev)
147 // Loop to monitor Syncthing events
148 func (e *Events) monitorLoop() {
149 e.log.Infof("Event monitoring running...")
156 e.log.Infof("Event monitoring exited")
159 case <-time.After(e.MonitorTime * time.Millisecond):
163 time.Sleep(time.Second)
164 if cntErrConn > cntErrRetry {
165 e.log.Error("ST Event monitor: ST connection down")
168 if _, err := e.getEvents(since); err == nil {
169 e.st.Connected = true
171 // XXX - should we reset since value ?
179 stEvArr, err := e.getEvents(since)
181 e.log.Errorf("Syncthing Get Events: %v", err)
182 e.st.Connected = false
187 for _, stEv := range stEvArr {
188 since = stEv.SubscriptionID
190 e.log.Warnf("ST EVENT: %d %s\n %v", stEv.GlobalID, stEv.Type, stEv)
193 cbKey, ok := e.cbArr[stEv.Type]
204 // FIXME: re-define data struct for each events
205 // instead of map of string and use JSON marshing/unmarshing
207 evData.Data = make(map[string]string)
210 case EventFolderCompletion:
211 fID = convString(stEv.Data["folder"])
212 evData.Data["completion"] = convFloat64(stEv.Data["completion"])
214 case EventFolderSummary:
215 fID = convString(stEv.Data["folder"])
216 evData.Data["needBytes"] = convInt64(stEv.Data["needBytes"])
217 evData.Data["state"] = convString(stEv.Data["state"])
219 case EventFolderPaused, EventFolderResumed:
220 fID = convString(stEv.Data["id"])
221 evData.Data["label"] = convString(stEv.Data["label"])
223 case EventFolderErrors:
224 fID = convString(stEv.Data["folder"])
225 // TODO decode array evData.Data["errors"] = convString(stEv.Data["errors"])
227 case EventStateChanged:
228 fID = convString(stEv.Data["folder"])
229 evData.Data["from"] = convString(stEv.Data["from"])
230 evData.Data["to"] = convString(stEv.Data["to"])
233 e.log.Warnf("Unsupported event type")
237 evData.Data["id"] = fID
240 // Call all registered callbacks
241 for _, c := range cbKey {
243 e.log.Warnf("EVENT CB fID=%s, filterID=%s", fID, c.filterID)
245 // Call when filterID is not set or when it matches
246 if c.filterID == "" || (fID != "" && fID == c.filterID) {
255 func convString(d interface{}) string {
259 func convFloat64(d interface{}) string {
260 return strconv.FormatFloat(d.(float64), 'f', -1, 64)
263 func convInt64(d interface{}) string {
264 return strconv.FormatInt(d.(int64), 10)