restart xmpp server after error or ping-timeout
authorBernhard Tittelbach <xro@realraum.at>
Thu, 7 Nov 2013 18:01:24 +0000 (18:01 +0000)
committerBernhard Tittelbach <xro@realraum.at>
Thu, 7 Nov 2013 18:01:24 +0000 (18:01 +0000)
go/r3-netstatus/main.go
go/r3-netstatus/r3xmppbot/logging.go
go/r3-netstatus/r3xmppbot/ping.go [new file with mode: 0644]
go/r3-netstatus/r3xmppbot/r3xmppbot.go
tuer_core.initscript

index 66763ff..bb6ffb5 100644 (file)
@@ -120,7 +120,8 @@ func EventToXMPP(bot *r3xmppbot.XmppBot, events <- chan interface{}, xmpp_presen
                     xmpp_presence_events_chan <- present_status
                     last_buttonpress = 0
                 }
-                
+                // Try to XMPP Ping the server and if that fails, quit XMPPBot
+                if bot.PingServer(2000) == false { return }
             case r3events.DoorProblemEvent:
                 xmpp_presence_events_chan <- r3xmppbot.XMPPMsgEvent{Msg: fmt.Sprintf("Door Problem: %s. SeverityLevel: %d (%s)",event.Problem, event.Severity, time.Unix(event.Ts,0).String()), DistributeLevel: r3xmppbot.R3OnlineOnlyInfo, RememberAsStatus: false}
         }
@@ -193,7 +194,7 @@ func main() {
     go RunXMPPBot(ps, zmqctx)
 
     // --- receive and distribute events ---
-    ticker := time.NewTicker(time.Duration(7) * time.Minute)
+    ticker := time.NewTicker(time.Duration(5) * time.Minute)
     for {
     select {
         case e := <-zmqsub.In():
index d8ae7f6..e4d2998 100644 (file)
@@ -28,4 +28,26 @@ func LogEnableSyslog() {
 func LogEnableDebuglog() {
     Syslog_ = log.New(os.Stdout, "", log.LstdFlags)
     Debug_ = log.New(os.Stderr, "DEBUG ", log.LstdFlags)
+}
+
+type XMPPLogger struct {
+}
+
+func (s *XMPPLogger) Log(v ...interface{}) {
+        Syslog_.Println(v...)
+}
+
+func (s *XMPPLogger) Logf(fmt string, v ...interface{}) {
+        Syslog_.Printf(fmt, v...)
+}
+
+type XMPPDebugLogger struct {
+}
+
+func (s *XMPPDebugLogger) Log(v ...interface{}) {
+        Debug_.Println(v...)
+}
+
+func (s *XMPPDebugLogger) Logf(fmt string, v ...interface{}) {
+        Debug_.Printf(fmt, v...)
 }
\ No newline at end of file
diff --git a/go/r3-netstatus/r3xmppbot/ping.go b/go/r3-netstatus/r3xmppbot/ping.go
new file mode 100644 (file)
index 0000000..a2e6f0e
--- /dev/null
@@ -0,0 +1,66 @@
+package r3xmppbot
+
+import (
+       xmpp "code.google.com/p/goexmpp"
+    "time"
+    "encoding/xml"    
+)
+
+// XMPP Ping
+type XMPPPing struct {
+        XMLName xml.Name     `xml:"urn:xmpp:ping ping"`
+}
+
+func HandleServerToClientPing(iq *xmpp.Iq, xmppout chan<- xmpp.Stanza) bool {
+    ///<iq from='juliet@capulet.lit/balcony' to='capulet.lit' id='s2c1' type='result'/>
+    if iq.Type != "get" { return false}
+    for _, ele := range iq.Nested {
+        if _, ok := ele.(*XMPPPing); ok {
+            xmppout <- &xmpp.Iq{Header: xmpp.Header{To: iq.From, From: iq.To, Id: iq.Id, Type: "result" }}
+            return true
+        }
+    }
+    return false
+}
+
+func (botdata *XmppBot) PingServer(timeout_ms time.Duration) (is_up bool) {
+///<iq from='juliet@capulet.lit/balcony' to='capulet.lit' id='c2s1' type='get'>
+///  <ping xmlns='urn:xmpp:ping'/>
+///</iq>
+    server_jid := new(xmpp.JID)
+    server_jid.Set(botdata.my_jid_)
+    iqping := &xmpp.Iq{Header: xmpp.Header{To: server_jid.Domain,
+                                                            From: botdata.my_jid_,
+                                                            Id: <-xmpp.Id,
+                                                            Type: "get",
+                                                            Nested: []interface{}{XMPPPing{}}   }   }
+    pong := make(chan bool, 1)
+    defer close(pong)
+    f := func(v xmpp.Stanza) bool {
+        defer recover() //recover from writing to possibly already closed chan
+        let_others_handle_stanza := false
+        iq, ok := v.(*xmpp.Iq)
+        if !ok {
+            Syslog_.Printf("response to iq ping wasn't iq: %s", v)
+            pong <- false
+            return true //let other handlers process reply
+        }
+        if iq.Type == "error" && iq.Error != nil && iq.Error.Type == "cancel"{
+            Debug_.Printf("response to iq ping was cancel, server does not support ping")
+            //server does not support ping, but at least we know server is still there
+        } else if iq.Type != "result" {
+            Syslog_.Printf("response to iq ping was not pong: %s", v)
+            let_others_handle_stanza = true //let other handlers process reply
+        }
+        pong <- true
+        return let_others_handle_stanza // return false so that Stanza v will not be appear in xmppclient_.Out()
+    }
+    botdata.xmppclient_.HandleStanza(iqping.Id, f)
+    botdata.xmppclient_.Out <- iqping    
+    go func() {
+        defer func() {if x:= recover(); x == nil { Syslog_.Printf("response to iq ping timed out !!") }}() //recover from writing to possibly already closed chan. If we did not need to recover, then Handler did not receive reply
+        time.Sleep(timeout_ms * time.Millisecond)
+        pong <- false   //xmpp ping timed out
+    }()
+    return <- pong
+}
index 34a1472..6740a84 100644 (file)
@@ -11,18 +11,6 @@ import (
     "path"
 )
 
-//~ type StdLogger struct {
-//~ }
-
-//~ func (s *StdLogger) Log(v ...interface{}) {
-        //~ log.Println(v...)
-//~ }
-
-//~ func (s *StdLogger) Logf(fmt string, v ...interface{}) {
-        //~ log.Printf(fmt, v...)
-//~ }
-
-
 func (botdata *XmppBot) makeXMPPMessage(to string, message interface{}, subject interface{}) *xmpp.Message {
     xmppmsgheader := xmpp.Header{To: to,
                                                             From: botdata.my_jid_,
@@ -136,7 +124,6 @@ type XmppBot struct {
     my_login_password_ string
     xmppclient_ *xmpp.Client
     presence_events_ *chan interface{}
-    ping_reply_ chan bool
 }
 
 
@@ -172,14 +159,6 @@ func (data RealraumXmppNotifierConfig) loadFrom(filepath string) () {
     }
 }
 
-
-func init() {
-        //~ logger := &StdLogger{}
-        //~ xmpp.Debug = logger
-        //~ xmpp.Info = logger
-        //~ xmpp.Warn = logger
-}
-
 func (botdata *XmppBot) handleEventsforXMPP(xmppout chan <- xmpp.Stanza, presence_events <- chan interface{}, jabber_events <- chan JabberEvent) {
     var last_status_msg *string
 
@@ -188,12 +167,13 @@ func (botdata *XmppBot) handleEventsforXMPP(xmppout chan <- xmpp.Stanza, presenc
             Syslog_.Printf("handleEventsforXMPP: run time panic: %v", x)
             //FIXME: signal that xmpp bot has crashed
         }
+        for _ = range(jabber_events) {}    //cleanout jabber_events queue
     }()
 
        for {
                select {
                case pe, pe_still_open := <-presence_events:
-            if ! pe_still_open { break }
+            if ! pe_still_open { return }
             Debug_.Printf("handleEventsforXMPP<-presence_events: %T %+v", pe, pe)
             switch pec := pe.(type) {
                 case xmpp.Stanza:
@@ -219,11 +199,12 @@ func (botdata *XmppBot) handleEventsforXMPP(xmppout chan <- xmpp.Stanza, presenc
                         }
                     }
                 default:
-                    break
+                    Debug_.Println("handleEventsforXMPP<-presence_events: unknown type received: quitting")
+                    return
                 }
 
                case je, je_still_open := <-jabber_events:
-            if ! je_still_open { break }
+            if ! je_still_open { return }
             Debug_.Printf("handleEventsforXMPP<-jabber_events: %T %+v", je, je)
             simple_jid := removeJIDResource(je.JID)
             jid_data, jid_in_map := botdata.realraum_jids_[simple_jid]
@@ -273,8 +254,8 @@ func (botdata *XmppBot) handleIncomingMessageDialog(inmsg xmpp.Message, xmppout
     if inmsg.Body == nil || inmsg.GetHeader() == nil {
         return
     }
-    if inmsg.GetHeader().Error != nil {
-        Syslog_.Printf("XMPP Message Error: %s", inmsg.GetHeader().Error.Error())
+    if inmsg.Type == "error" || inmsg.Error != nil {
+        Syslog_.Printf("XMPP Message Error: %s", inmsg.Error.Error())
     }
     bodytext :=inmsg.Body.Chardata
     if botdata.isAuthenticated(inmsg.GetHeader().From) {
@@ -330,8 +311,8 @@ func (botdata *XmppBot) handleIncomingXMPPStanzas(xmppin <- chan xmpp.Stanza, xm
     defer func() {
         if x := recover(); x != nil {
             Syslog_.Printf("handleIncomingXMPPStanzas: run time panic: %v", x)
-            close(jabber_events)
         }
+        close(jabber_events)
     }()
 
     var incoming_stanza interface{}
@@ -341,8 +322,8 @@ func (botdata *XmppBot) handleIncomingXMPPStanzas(xmppin <- chan xmpp.Stanza, xm
                 botdata.handleIncomingMessageDialog(*stanza, xmppout, jabber_events)
             case *xmpp.Presence:
                 if stanza.GetHeader() == nil { continue }
-                if stanza.GetHeader().Error != nil {
-                    Syslog_.Printf("XMPP Presence Error: %s", stanza.GetHeader().Error.Error())
+                if stanza.Type == "error" || stanza.Error != nil {
+                    Syslog_.Printf("XMPP Presence Error: %s", stanza.Error.Error())
                 }
                 switch stanza.GetHeader().Type {
                     case "subscribe":
@@ -361,13 +342,21 @@ func (botdata *XmppBot) handleIncomingXMPPStanzas(xmppin <- chan xmpp.Stanza, xm
                 }
             case *xmpp.Iq:
                 if stanza.GetHeader() == nil { continue }
-                if stanza.GetHeader().Error != nil {
-                    Syslog_.Printf("XMPP Iq Error: %s", stanza.GetHeader().Error.Error())
+                if stanza.Type == "error" || stanza.Error != nil {
+                    Syslog_.Printf("XMPP Iq Error: %s", stanza.Error.Error())
                 }
+                if HandleServerToClientPing(stanza, xmppout) {continue} //if true then routine handled it and we can continue
+                Debug_.Printf("Unhandled Iq: %s", stanza)
         }
     }
 }
 
+func init() {
+    //~ xmpp.Debug = &XMPPDebugLogger{}
+    xmpp.Info = &XMPPDebugLogger{}
+    xmpp.Warn = &XMPPLogger{}
+}
+
 func NewStartedBot(loginjid, loginpwd, password, state_save_dir string, insecuretls bool) (*XmppBot, chan interface{}, error) {
     var err error
     botdata := new(XmppBot)
@@ -379,15 +368,9 @@ func NewStartedBot(loginjid, loginpwd, password, state_save_dir string, insecure
     botdata.my_jid_ = loginjid
     botdata.my_login_password_ = loginpwd
     botdata.auth_timeout_ = 3600*2
-    botdata.ping_reply_ = make(chan bool)
 
     botdata.config_file_ = path.Join(state_save_dir, "r3xmpp."+removeJIDResource(loginjid)+".json")
 
-    //~ logger := &StdLogger{}
-    //~ xmpp.Debug = logger
-    //~ xmpp.Info = logger
-    //~ xmpp.Warn = logger
-
     xmpp.TlsConfig = tls.Config{InsecureSkipVerify: insecuretls}
     botdata.realraum_jids_.loadFrom(botdata.config_file_)
 
@@ -428,6 +411,7 @@ func NewStartedBot(loginjid, loginpwd, password, state_save_dir string, insecure
 }
 
 func (botdata *XmppBot) StopBot() {
+    Syslog_.Println("Stopping XMPP Bot")
     if botdata.xmppclient_ != nil {
         close(botdata.xmppclient_.Out)
     }
@@ -435,4 +419,7 @@ func (botdata *XmppBot) StopBot() {
         *botdata.presence_events_ <- false
         close(*botdata.presence_events_)
     }
+    botdata.config_file_ = ""
+    botdata.realraum_jids_ = nil
+    botdata.xmppclient_ = nil
 }
index 34dd4f5..ed3902d 100755 (executable)
@@ -32,12 +32,12 @@ chown $DOOR_USR:$DOOR_GRP $DOOR_DEV
 case "$1" in
 start)
   log_daemon_msg "Starting door daemon" "door_daemon_zmq"
-  start-stop-daemon --start --quiet --pidfile $PIDFILE_DOOR -c $DOOR_USR -m -g $DOOR_GRP -b --name door_daemon_zmq --startas $EXE_DOOR -- --device $DOOR_DEV --cmdport "$DOOR_SOCKET" --syslog 
+  start-stop-daemon --start --quiet --pidfile $PIDFILE_DOOR -c $DOOR_USR -m -g $DOOR_GRP -b --name door_daemon_zmq --startas $EXE_DOOR -- --device $DOOR_DEV --cmdport "$DOOR_SOCKET" --syslog
   log_end_msg $?
   ;;
 stop)
   log_daemon_msg "Stopping door daemon" "door_daemon_zmq"
-  start-stop-daemon --stop --quiet --pidfile $PIDFILE_DOOR -m --name door_daemon_zmq 
+  start-stop-daemon --stop --quiet --pidfile $PIDFILE_DOOR -m --name door_daemon_zmq
   log_end_msg $?
         ;;
 restart)