diff --git a/caddy/caddymain/run.go b/caddy/caddymain/run.go index fd193d5f7..0eebe0108 100644 --- a/caddy/caddymain/run.go +++ b/caddy/caddymain/run.go @@ -100,11 +100,8 @@ func Run() { mustLogFatalf("%v", err) } - // Execute plugins that are registered to run as the process starts - err = caddy.StartupHooks(serverType) - if err != nil { - mustLogFatalf("%v", err) - } + // Executes Startup events + caddy.EmitEvent(caddy.StartupEvent) // Get Caddyfile input caddyfileinput, err := caddy.LoadCaddyfile(serverType) diff --git a/plugins.go b/plugins.go index 885b621aa..c6d52518f 100644 --- a/plugins.go +++ b/plugins.go @@ -2,6 +2,7 @@ package caddy import ( "fmt" + "log" "net" "sort" @@ -21,6 +22,10 @@ var ( // must have a name. plugins = make(map[string]map[string]Plugin) + // eventHooks is a map of hook name to Hook. All hooks plugins + // must have a name. + eventHooks = make(map[string]EventHook) + // parsingCallbacks maps server type to map of directive // to list of callback functions. These aren't really // plugins on their own, but are often registered from @@ -69,30 +74,6 @@ func DescribePlugins() string { return str } -// StartupHooks executes the startup hooks defined when the -// plugins were registered and returns the first error -// it encounters. -func StartupHooks(serverType string) error { - for stype, stypePlugins := range plugins { - if stype != "" && stype != serverType { - continue - } - - for name := range stypePlugins { - if stypePlugins[name].StartupHook == nil { - continue - } - - err := stypePlugins[name].StartupHook() - if err != nil { - return err - } - } - } - - return nil -} - // ValidDirectives returns the list of all directives that are // recognized for the server type serverType. However, not all // directives may be installed. This makes it possible to give @@ -200,10 +181,6 @@ type Plugin struct { // Action is the plugin's setup function, if associated // with a directive in the Caddyfile. Action SetupFunc - - // StartupHook is the plugin's function that is executed - // immediately after the flag parsing. - StartupHook func() error } // RegisterPlugin plugs in plugin. All plugins should register @@ -228,6 +205,42 @@ func RegisterPlugin(name string, plugin Plugin) { plugins[plugin.ServerType][name] = plugin } +// EventName represents the name of an event used with event hooks. +type EventName string + +const ( + StartupEvent EventName = "startup" + ShutdownEvent EventName = "shutdown" +) + +// EventHook is a type which holds information about a startup hook plugin. +type EventHook func(eventType EventName) error + +// RegisterEventHook plugs in hook. All the hooks should register themselves +// and they must have a name. +func RegisterEventHook(name string, hook EventHook) { + if name == "" { + panic("event hook must have a name") + } + if _, dup := eventHooks[name]; dup { + panic("hook named " + name + " already registered") + } + eventHooks[name] = hook +} + +// EmitEvent executes the different hooks passing the EventType as an +// argument. This is a blocking function. Hook developers should +// use 'go' keyword if they don't want to block Caddy. +func EmitEvent(event EventName) { + for name, hook := range eventHooks { + err := hook(event) + + if err != nil { + log.Printf("error on '%s' hook: %v", name, err) + } + } +} + // ParsingCallback is a function that is called after // a directive's setup functions have been executed // for all the server blocks. diff --git a/sigtrap.go b/sigtrap.go index ed7dc803a..3be3b3300 100644 --- a/sigtrap.go +++ b/sigtrap.go @@ -52,6 +52,9 @@ func trapSignalsCrossPlatform() { // This function is idempotent; subsequent invocations always return 0. func executeShutdownCallbacks(signame string) (exitCode int) { shutdownCallbacksOnce.Do(func() { + // execute third-party shutdown hooks + EmitEvent(ShutdownEvent) + errs := allShutdownCallbacks() if len(errs) > 0 { for _, err := range errs {