From 10ac20f7e02aacfa6682a16414b56208ab304b56 Mon Sep 17 00:00:00 2001 From: Andreas Linde <42185+DerAndereAndi@users.noreply.github.com> Date: Sun, 6 Feb 2022 18:26:15 +0100 Subject: [PATCH] Properly shut down EEBUS mdns entry (#2493) --- cmd/charger.go | 7 ++++++ cmd/configure.go | 23 +++++++++++++++++ cmd/configure/eebus.go | 5 ++-- cmd/configure/helper.go | 4 +-- cmd/configure/survey.go | 20 +++++---------- cmd/root.go | 18 +++++++++++--- cmd/setup.go | 2 ++ cmd/shutdown/shutdown.go | 53 ++++++++++++++++++++++++++++++++++++++++ server/eebus.go | 9 ++++++- 9 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 cmd/shutdown/shutdown.go diff --git a/cmd/charger.go b/cmd/charger.go index f574a4bc4..1b27b516c 100644 --- a/cmd/charger.go +++ b/cmd/charger.go @@ -2,6 +2,7 @@ package cmd import ( "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/cmd/shutdown" "github.com/evcc-io/evcc/server" "github.com/evcc-io/evcc/util" "github.com/spf13/cobra" @@ -38,6 +39,9 @@ func runCharger(cmd *cobra.Command, args []string) { log.FATAL.Fatal(err) } + stopC := make(chan struct{}) + go shutdown.Run(stopC) + chargers := cp.chargers if len(args) == 1 { arg := args[0] @@ -48,4 +52,7 @@ func runCharger(cmd *cobra.Command, args []string) { for name, v := range chargers { d.DumpWithHeader(name, v) } + + close(stopC) + <-shutdown.Done() } diff --git a/cmd/configure.go b/cmd/configure.go index 0ea890acd..7ef88fa21 100644 --- a/cmd/configure.go +++ b/cmd/configure.go @@ -2,8 +2,12 @@ package cmd import ( _ "embed" + "os" + "os/signal" + "syscall" "github.com/evcc-io/evcc/cmd/configure" + "github.com/evcc-io/evcc/cmd/shutdown" "github.com/evcc-io/evcc/util" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -43,5 +47,24 @@ func runConfigure(cmd *cobra.Command, args []string) { util.LogLevel(viper.GetString("log"), nil) + stopC := make(chan struct{}) + go shutdown.Run(stopC) + + // catch signals + go func() { + signalC := make(chan os.Signal, 1) + signal.Notify(signalC, os.Interrupt, syscall.SIGTERM) + + <-signalC // wait for signal + close(stopC) // signal loop to end + + <-shutdown.Done() + + os.Exit(1) + }() + impl.Run(log, lang, advanced, expand) + + close(stopC) + <-shutdown.Done() } diff --git a/cmd/configure/eebus.go b/cmd/configure/eebus.go index 7784c760b..bb3fdaf94 100644 --- a/cmd/configure/eebus.go +++ b/cmd/configure/eebus.go @@ -5,6 +5,7 @@ import ( "fmt" certhelper "github.com/evcc-io/eebus/cert" + "github.com/evcc-io/evcc/cmd/shutdown" "github.com/evcc-io/evcc/server" ) @@ -13,13 +14,13 @@ func (c *CmdConfigure) configureEEBus(conf map[string]interface{}) error { var err error if server.EEBusInstance, err = server.NewEEBus(conf); err == nil { go server.EEBusInstance.Run() + shutdown.Register(server.EEBusInstance.Shutdown) } return nil } -// eebusCertificate setup EEBUS certificate -// returns privagte key, public key and error +// eebusCertificate creates EEBUS certificate and returns private/public key func (c *CmdConfigure) eebusCertificate() (map[string]interface{}, error) { details := server.EEBUSDetails diff --git a/cmd/configure/helper.go b/cmd/configure/helper.go index 6eda23ff6..d3dafdd5c 100644 --- a/cmd/configure/helper.go +++ b/cmd/configure/helper.go @@ -161,8 +161,7 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template return fmt.Errorf("%s: %s", c.localizedString("Requirements_EEBUS_Cert_Error", nil), err) } - err = c.configureEEBus(eebusConfig) - if err != nil { + if err := c.configureEEBus(eebusConfig); err != nil { return err } @@ -170,6 +169,7 @@ func (c *CmdConfigure) processDeviceRequirements(templateItem templates.Template if err != nil { return err } + c.configuration.config.EEBUS = string(eebusYaml) fmt.Println() fmt.Println("--------------------------------------------") diff --git a/cmd/configure/survey.go b/cmd/configure/survey.go index b4e04ba6d..ce1b5193e 100644 --- a/cmd/configure/survey.go +++ b/cmd/configure/survey.go @@ -39,7 +39,7 @@ func (c *CmdConfigure) askConfigFailureNextStep() bool { } // select item from list -func (c *CmdConfigure) askSelection(message string, items []string) (error, string, int) { +func (c *CmdConfigure) askSelection(message string, items []string) (string, int, error) { selection := "" prompt := &survey.Select{ Message: message, @@ -47,19 +47,11 @@ func (c *CmdConfigure) askSelection(message string, items []string) (error, stri } err := c.surveyAskOne(prompt, &selection) - if err != nil { - return err, "", 0 + if err == nil { + return selection, funk.IndexOf(items, selection), nil } - var selectedIndex int - for index, item := range items { - if item == selection { - selectedIndex = index - break - } - } - - return err, selection, selectedIndex + return "", 0, err } // selectItem selects item from list @@ -78,7 +70,7 @@ func (c *CmdConfigure) selectItem(deviceCategory DeviceCategory) templates.Templ } text := fmt.Sprintf("%s %s %s:", c.localizedString("Choose", nil), DeviceCategories[deviceCategory].article, DeviceCategories[deviceCategory].title) - err, _, selected := c.askSelection(text, items) + _, selected, err := c.askSelection(text, items) if err != nil { c.log.FATAL.Fatal(err) } @@ -88,7 +80,7 @@ func (c *CmdConfigure) selectItem(deviceCategory DeviceCategory) templates.Templ // askChoice selects item from list func (c *CmdConfigure) askChoice(label string, choices []string) (int, string) { - err, selection, index := c.askSelection(label, choices) + selection, index, err := c.askSelection(label, choices) if err != nil { c.log.FATAL.Fatal(err) } diff --git a/cmd/root.go b/cmd/root.go index e22bae2bb..d6646ca6e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,9 +6,11 @@ import ( _ "net/http/pprof" // pprof handler "os" "os/signal" + "sync" "syscall" "time" + "github.com/evcc-io/evcc/cmd/shutdown" "github.com/evcc-io/evcc/server" "github.com/evcc-io/evcc/server/updater" "github.com/evcc-io/evcc/util" @@ -233,15 +235,16 @@ func run(cmd *cobra.Command, args []string) { site.Prepare(valueChan, pushChan) stopC := make(chan struct{}) - exitC := make(chan struct{}) + go shutdown.Run(stopC) + siteC := make(chan struct{}) go func() { site.Run(stopC, conf.Interval) - close(exitC) + close(siteC) }() // uds health check listener - go server.HealthListener(site, exitC) + go server.HealthListener(site, siteC) // catch signals go func() { @@ -251,6 +254,15 @@ func run(cmd *cobra.Command, args []string) { <-signalC // wait for signal close(stopC) // signal loop to end + exitC := make(chan struct{}) + wg := new(sync.WaitGroup) + wg.Add(2) + + // wait for main loop and shutdown functions to finish + go func() { <-shutdown.Done(conf.Interval); wg.Done() }() + go func() { <-siteC; wg.Done() }() + go func() { wg.Wait(); close(exitC) }() + select { case <-exitC: // wait for loop to end case <-time.NewTimer(conf.Interval).C: // wait max 1 period diff --git a/cmd/setup.go b/cmd/setup.go index fe7242f75..e0b06b0f6 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -11,6 +11,7 @@ import ( paho "github.com/eclipse/paho.mqtt.golang" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/api/proto/pb" + "github.com/evcc-io/evcc/cmd/shutdown" "github.com/evcc-io/evcc/core" "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/hems" @@ -164,6 +165,7 @@ func configureEEBus(conf map[string]interface{}) error { var err error if server.EEBusInstance, err = server.NewEEBus(conf); err == nil { go server.EEBusInstance.Run() + shutdown.Register(server.EEBusInstance.Shutdown) } return nil diff --git a/cmd/shutdown/shutdown.go b/cmd/shutdown/shutdown.go new file mode 100644 index 000000000..5a329dd55 --- /dev/null +++ b/cmd/shutdown/shutdown.go @@ -0,0 +1,53 @@ +package shutdown + +import ( + "sync" + "time" +) + +var ( + mu sync.Mutex + handlers = make([]func(), 0) + exitC = make(chan struct{}) +) + +func Register(cb func()) { + mu.Lock() + handlers = append(handlers, cb) + mu.Unlock() +} + +func Run(stopC <-chan struct{}) { + <-stopC + wg := new(sync.WaitGroup) + + mu.Lock() + for _, cb := range handlers { + wg.Add(1) + + go func(cb func()) { + cb() + wg.Done() + }(cb) + } + mu.Unlock() + + wg.Wait() + close(exitC) +} + +func Done(timeout ...time.Duration) <-chan struct{} { + to := time.Second + if len(timeout) == 1 { + to = timeout[0] + } + + select { + case <-exitC: + return exitC + case <-time.After(to): + exitC := make(chan struct{}) + close(exitC) + return exitC + } +} diff --git a/server/eebus.go b/server/eebus.go index 3d7f1c8c6..00aaf4035 100644 --- a/server/eebus.go +++ b/server/eebus.go @@ -37,6 +37,7 @@ type EEBus struct { log *util.Logger srv *server.Server id string + zc *zeroconf.Server clients map[string]EEBusClientCBs connectedClients map[string]ship.Conn discoveredClients map[string]*zeroconf.ServiceEntry @@ -90,11 +91,13 @@ func NewEEBus(other map[string]interface{}) (*EEBus, error) { Register: true, } - if _, err = srv.Announce(); err != nil { + zc, err := srv.Announce() + if err != nil { return nil, err } c := &EEBus{ + zc: zc, log: log, srv: srv, id: id, @@ -152,6 +155,10 @@ func (c *EEBus) Run() { } } +func (c *EEBus) Shutdown() { + c.zc.Shutdown() +} + func (c *EEBus) addDisoveredEntry(entry *zeroconf.ServiceEntry) { // we need to get the SKI only svc, err := mdns.NewFromDNSEntry(entry)