Improve exiting (actually it was Mattermost that was creating a deadlock)

This commit is contained in:
Alex 2020-03-04 17:44:22 +01:00
parent df1f2d38b3
commit d4d3cc422b
7 changed files with 49 additions and 42 deletions

View file

@ -44,11 +44,13 @@ func SetAccount(mxid string, name string, protocol string, config map[string]str
return fmt.Errorf("Wrong protocol") return fmt.Errorf("Wrong protocol")
} }
if !reflect.DeepEqual(config, prev_acct.Config) { if !reflect.DeepEqual(config, prev_acct.Config) {
go func() {
prev_acct.Conn.Close() prev_acct.Conn.Close()
prev_acct.JoinedRooms = map[RoomID]bool{} prev_acct.JoinedRooms = map[RoomID]bool{}
prev_acct.Config = config prev_acct.Config = config
go prev_acct.connect() prev_acct.connect()
}()
} }
} else { } else {
proto, ok := Protocols[protocol] proto, ok := Protocols[protocol]
@ -128,11 +130,13 @@ func RemoveAccount(mxUser string, name string) {
} }
} }
func CloseAllAcountsForShutdown() { func CloseAllAccountsForShutdown() {
accountsLock.Lock() accountsLock.Lock()
defer accountsLock.Unlock() defer accountsLock.Unlock()
for _, accl := range registeredAccounts { for _, accl := range registeredAccounts {
for _, acct := range accl { for _, acct := range accl {
log.Printf("Closing %#v", acct)
acct.Conn.Close() acct.Conn.Close()
} }
} }

View file

@ -177,6 +177,7 @@ func (ext *External) restartLoop(generation int) {
break break
} }
log.Printf("Process %s stopped, restarting.", ext.command) log.Printf("Process %s stopped, restarting.", ext.command)
log.Printf("Generation %d vs %d", ext.generation, generation)
err := ext.setupProc(generation) err := ext.setupProc(generation)
if err != nil { if err != nil {
ext.proc = nil ext.proc = nil
@ -326,22 +327,23 @@ func (ext *External) Close() {
ext.sendJson.Encode(&extMessage{ ext.sendJson.Encode(&extMessage{
MsgType: CLOSE, MsgType: CLOSE,
}) })
ext.proc.Process.Signal(os.Interrupt)
proc := ext.proc
proc.Process.Signal(os.Interrupt)
ext.recvPipe.Close() ext.recvPipe.Close()
ext.sendPipe.Close() ext.sendPipe.Close()
go func() {
time.Sleep(1 * time.Second)
log.Info("Sending SIGKILL to external process (did not terminate within 1 second)")
ext.proc.Process.Kill()
}()
ext.proc.Wait()
ext.proc = nil ext.proc = nil
ext.recvPipe = nil ext.recvPipe = nil
ext.sendPipe = nil ext.sendPipe = nil
ext.sendJson = nil ext.sendJson = nil
ext.handlerChan = nil ext.handlerChan = nil
go func() {
time.Sleep(1 * time.Second)
proc.Process.Kill()
}()
} }
// ---- Actual message handling :) // ---- Actual message handling :)

View file

@ -59,7 +59,7 @@ func (mm *Mattermost) Configure(c Configuration) error {
} }
// Reinitialize shared data structures // Reinitialize shared data structures
mm.handlerStopChan = make(chan bool) mm.handlerStopChan = make(chan bool, 1)
mm.caches.mmusers = make(map[string]string) mm.caches.mmusers = make(map[string]string)
mm.caches.sentjoined = make(map[string]bool) mm.caches.sentjoined = make(map[string]bool)

View file

@ -225,7 +225,7 @@ class MessengerBridge:
self.client = MessengerBridgeClient(email=email, password=password, max_tries=1) self.client = MessengerBridgeClient(email=email, password=password, max_tries=1)
if not self.client.isLoggedIn(): if not self.client.isLoggedIn():
return {"_type": "ret_error", "error": "Unable to login (?)"} return {"_type": "rep_error", "error": "Unable to login (?)"}
try: try:
f = open(client_file, "wb") f = open(client_file, "wb")

41
main.go
View file

@ -10,7 +10,6 @@ import (
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -176,44 +175,36 @@ func main() {
reg_file := readRegistration(config.Registration) reg_file := readRegistration(config.Registration)
registration = &reg_file registration = &reg_file
// Start appservice and web management interface // Create context and handlers for errors and signals
ctx, stop_all := context.WithCancel(context.Background())
errch := make(chan error) errch := make(chan error)
sigch := make(chan os.Signal) sigch := make(chan os.Signal)
signal.Notify(sigch, os.Interrupt, syscall.SIGTERM) signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)
defer func() {
signal.Stop(sigch)
stop_all()
}()
as_server, err := StartAppService(errch) // Start appservice and web server
_, err = StartAppService(errch, ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
web_server := StartWeb(errch) _ = StartWeb(errch, ctx)
// Wait for an error somewhere or interrupt signal // Wait for an error somewhere or interrupt signal
select { select {
case err = <-errch: case err = <-errch:
if err != nil {
log.Error(err) log.Error(err)
} stop_all()
case sig := <-sigch: case sig := <-sigch:
log.Warnf("Got signal %s", sig.String()) log.Warnf("Got signal: %s", sig.String())
stop_all()
case <-ctx.Done():
} }
// Shut down, hopefully this is not a too bad way to do it log.Info("Closing all account connections...")
log.Warn("Shuttind down") CloseAllAccountsForShutdown()
delay := 2 * time.Second log.Info("Exiting.")
ctx1, _ := context.WithTimeout(context.TODO(), delay)
go as_server.Shutdown(ctx1)
ctx2, _ := context.WithTimeout(context.TODO(), delay)
go web_server.Shutdown(ctx2)
time.Sleep(delay)
CloseAllAcountsForShutdown()
if err != nil {
os.Exit(1)
} else {
os.Exit(0)
}
} }

View file

@ -1,8 +1,10 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"net/http" "net/http"
"os" "os"
"strings" "strings"
@ -16,7 +18,7 @@ import (
var mx *mxlib.Client var mx *mxlib.Client
func StartAppService(errch chan error) (*http.Server, error) { func StartAppService(errch chan error, ctx context.Context) (*http.Server, error) {
mx = mxlib.NewClient(config.Server, registration.AsToken) mx = mxlib.NewClient(config.Server, registration.AsToken)
err := InitDb() err := InitDb()
@ -56,6 +58,9 @@ func StartAppService(errch chan error) (*http.Server, error) {
http_server := &http.Server{ http_server := &http.Server{
Addr: config.ASBindAddr, Addr: config.ASBindAddr,
Handler: checkTokenAndLog(router), Handler: checkTokenAndLog(router),
BaseContext: func(net.Listener) context.Context {
return ctx
},
} }
go func() { go func() {
err := http_server.ListenAndServe() err := http_server.ListenAndServe()

7
web.go
View file

@ -1,7 +1,9 @@
package main package main
import ( import (
"context"
"html/template" "html/template"
"net"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -21,7 +23,7 @@ const SESSION_NAME = "easybridge_session"
var sessionsStore sessions.Store = nil var sessionsStore sessions.Store = nil
var userKeys = map[string]*[32]byte{} var userKeys = map[string]*[32]byte{}
func StartWeb(errch chan error) *http.Server { func StartWeb(errch chan error, ctx context.Context) *http.Server {
session_key := blake2b.Sum256([]byte(config.SessionKey)) session_key := blake2b.Sum256([]byte(config.SessionKey))
sessionsStore = sessions.NewCookieStore(session_key[:]) sessionsStore = sessions.NewCookieStore(session_key[:])
@ -39,6 +41,9 @@ func StartWeb(errch chan error) *http.Server {
web_server := &http.Server{ web_server := &http.Server{
Addr: config.WebBindAddr, Addr: config.WebBindAddr,
Handler: logRequest(r), Handler: logRequest(r),
BaseContext: func(net.Listener) context.Context {
return ctx
},
} }
go func() { go func() {
err := web_server.ListenAndServe() err := web_server.ListenAndServe()