diff options
-rw-r--r-- | libpod/shutdown/handler.go | 105 | ||||
-rw-r--r-- | pkg/api/server/server.go | 17 |
2 files changed, 117 insertions, 5 deletions
diff --git a/libpod/shutdown/handler.go b/libpod/shutdown/handler.go new file mode 100644 index 000000000..79f236ab6 --- /dev/null +++ b/libpod/shutdown/handler.go @@ -0,0 +1,105 @@ +package shutdown + +import ( + "os" + "os/signal" + "sync" + "syscall" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +var ( + stopped bool + sigChan chan os.Signal + cancelChan chan bool + handlers map[string]func() error + shutdownInhibit sync.RWMutex +) + +// Start begins handling SIGTERM and SIGINT and will run the given on-signal +// handlers when one is called. This can be cancelled by calling Stop(). +func Start() error { + if sigChan != nil && !stopped { + // Already running, do nothing. + return nil + } + + sigChan = make(chan os.Signal, 1) + cancelChan = make(chan bool, 1) + stopped = false + + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + go func() { + select { + case <-cancelChan: + signal.Stop(sigChan) + close(sigChan) + close(cancelChan) + stopped = true + return + case sig := <-sigChan: + logrus.Infof("Received shutdown signal %v, terminating!", sig) + shutdownInhibit.Lock() + for name, handler := range handlers { + logrus.Infof("Invoking shutdown handler %s", name) + if err := handler(); err != nil { + logrus.Errorf("Error running shutdown handler %s: %v", name, err) + } + } + shutdownInhibit.Unlock() + return + } + }() + + return nil +} + +// Stop the shutdown signal handler. +func Stop() error { + if cancelChan == nil { + return errors.New("shutdown signal handler has not yet been started") + } + if stopped { + return nil + } + + cancelChan <- true + + return nil +} + +// Temporarily inhibit signals from shutting down Libpod. +func Inhibit() { + shutdownInhibit.RLock() +} + +// Stop inhibiting signals from shutting down Libpod. +func Uninhibit() { + shutdownInhibit.RUnlock() +} + +// Register registers a function that will be executed when Podman is terminated +// by a signal. +func Register(name string, handler func() error) error { + if _, ok := handlers[name]; ok { + return errors.Errorf("handler with name %s already exists", name) + } + + handlers[name] = handler + + return nil +} + +// Unregister un-registers a given shutdown handler. +func Unregister(name string) error { + if _, ok := handlers[name]; !ok { + return errors.Errorf("no handler with name %s found", name) + } + + delete(handlers, name) + + return nil +} diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 355a46fb7..cc5b45a65 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -7,7 +7,6 @@ import ( "net" "net/http" "os" - "os/signal" goRuntime "runtime" "strings" "sync" @@ -15,6 +14,7 @@ import ( "time" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/shutdown" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/server/idle" "github.com/coreos/go-systemd/v22/activation" @@ -180,8 +180,17 @@ func setupSystemd() { // Serve starts responding to HTTP requests. func (s *APIServer) Serve() error { setupSystemd() - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + // Start the shutdown signal handler. + if err := shutdown.Start(); err != nil { + return err + } + if err := shutdown.Register("server", func() error { + return s.Shutdown() + }); err != nil { + return err + } + errChan := make(chan error, 1) go func() { @@ -220,8 +229,6 @@ func (s *APIServer) Serve() error { select { case err := <-errChan: return err - case sig := <-sigChan: - logrus.Infof("APIServer terminated by signal %v", sig) } return nil |