From 762ecd62c9bca5d9a4c9696dcf477d1000a28f42 Mon Sep 17 00:00:00 2001 From: Bernhard Tittelbach Date: Thu, 7 Nov 2013 18:01:24 +0000 Subject: [PATCH] restart xmpp server after error or ping-timeout --- go/r3-netstatus/main.go | 5 ++- go/r3-netstatus/r3xmppbot/logging.go | 22 +++++++++++ go/r3-netstatus/r3xmppbot/ping.go | 66 ++++++++++++++++++++++++++++++++ go/r3-netstatus/r3xmppbot/r3xmppbot.go | 61 ++++++++++++----------------- tuer_core.initscript | 4 +- 5 files changed, 117 insertions(+), 41 deletions(-) create mode 100644 go/r3-netstatus/r3xmppbot/ping.go diff --git a/go/r3-netstatus/main.go b/go/r3-netstatus/main.go index 66763ff..bb6ffb5 100644 --- a/go/r3-netstatus/main.go +++ b/go/r3-netstatus/main.go @@ -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(): diff --git a/go/r3-netstatus/r3xmppbot/logging.go b/go/r3-netstatus/r3xmppbot/logging.go index d8ae7f6..e4d2998 100644 --- a/go/r3-netstatus/r3xmppbot/logging.go +++ b/go/r3-netstatus/r3xmppbot/logging.go @@ -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 index 0000000..a2e6f0e --- /dev/null +++ b/go/r3-netstatus/r3xmppbot/ping.go @@ -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 { + /// + 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) { +/// +/// +/// + 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 +} diff --git a/go/r3-netstatus/r3xmppbot/r3xmppbot.go b/go/r3-netstatus/r3xmppbot/r3xmppbot.go index 34a1472..6740a84 100644 --- a/go/r3-netstatus/r3xmppbot/r3xmppbot.go +++ b/go/r3-netstatus/r3xmppbot/r3xmppbot.go @@ -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 } diff --git a/tuer_core.initscript b/tuer_core.initscript index 34dd4f5..ed3902d 100755 --- a/tuer_core.initscript +++ b/tuer_core.initscript @@ -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) -- 1.7.10.4