From 6f24082705e39b7d4914f145e20c6000f369ffad Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 4 Jul 2021 07:41:59 -0400 Subject: [PATCH 1/3] support systemd notifications Fixes #1733 --- distrib/systemd/ergo.service | 3 +- go.mod | 1 + go.sum | 2 + irc/server.go | 9 ++++- vendor/github.com/okzk/sdnotify/LICENSE | 21 ++++++++++ vendor/github.com/okzk/sdnotify/README.md | 15 +++++++ vendor/github.com/okzk/sdnotify/notify.go | 9 +++++ .../github.com/okzk/sdnotify/notify_linux.go | 23 +++++++++++ vendor/github.com/okzk/sdnotify/util.go | 39 +++++++++++++++++++ vendor/modules.txt | 3 ++ 10 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 vendor/github.com/okzk/sdnotify/LICENSE create mode 100644 vendor/github.com/okzk/sdnotify/README.md create mode 100644 vendor/github.com/okzk/sdnotify/notify.go create mode 100644 vendor/github.com/okzk/sdnotify/notify_linux.go create mode 100644 vendor/github.com/okzk/sdnotify/util.go diff --git a/distrib/systemd/ergo.service b/distrib/systemd/ergo.service index 2a64d770..6355980f 100644 --- a/distrib/systemd/ergo.service +++ b/distrib/systemd/ergo.service @@ -8,13 +8,14 @@ After=network.target # After=network.target mysql.service [Service] -Type=simple +Type=notify User=ergo WorkingDirectory=/home/ergo ExecStart=/home/ergo/ergo run --conf /home/ergo/ircd.yaml ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure LimitNOFILE=1048576 +NotifyAccess=main # Uncomment this for a hidden service: # PrivateNetwork=true diff --git a/go.mod b/go.mod index 1969d66f..170ac473 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/go-test/deep v1.0.6 // indirect github.com/gorilla/websocket v1.4.2 github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef // indirect + github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd github.com/onsi/ginkgo v1.12.0 // indirect github.com/onsi/gomega v1.9.0 // indirect github.com/oragono/confusables v0.0.0-20201108231250-4ab98ab61fb1 // indirect diff --git a/go.sum b/go.sum index c673b188..be715ad2 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,8 @@ github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef h1:07e6GcSuNh1BoZJ github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef/go.mod h1:q/JhvvKLmif3y9q8MDQM+gRCnjEKnu5ClF298TTXJug= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd h1:+iAPaTbi1gZpcpDwe/BW1fx7Xoesv69hLNGPheoyhBs= +github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd/go.mod h1:4soZNh0zW0LtYGdQ416i0jO0EIqMGcbtaspRS4BDvRQ= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= diff --git a/irc/server.go b/irc/server.go index 618f013b..24602c1c 100644 --- a/irc/server.go +++ b/irc/server.go @@ -21,6 +21,7 @@ import ( "unsafe" "github.com/ergochat/irc-go/ircfmt" + "github.com/okzk/sdnotify" "github.com/ergochat/ergo/irc/caps" "github.com/ergochat/ergo/irc/connection_limits" @@ -124,6 +125,8 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) { // Shutdown shuts down the server. func (server *Server) Shutdown() { + sdnotify.Stopping() + //TODO(dan): Make sure we disallow new nicks for _, client := range server.clients.AllClients() { client.Notice("Server is shutting down") @@ -528,6 +531,8 @@ func (server *Server) rehash() error { server.rehashMutex.Lock() defer server.rehashMutex.Unlock() + sdnotify.Reloading() + config, err := LoadConfig(server.configFilename) if err != nil { server.logger.Error("server", "failed to load config file", err.Error()) @@ -709,13 +714,15 @@ func (server *Server) applyConfig(config *Config) (err error) { server.logger.Info("server", "Proxied IPs will be accepted from", strings.Join(config.Server.ProxyAllowedFrom, ", ")) } - // we are now open for business err = server.setupListeners(config) // send other config warnings if config.Accounts.RequireSasl.Enabled && config.Accounts.Registration.Enabled { server.logger.Warning("server", "Warning: although require-sasl is enabled, users can still register accounts. If your server is not intended to be public, you must set accounts.registration.enabled to false.") } + // we are now open for business + sdnotify.Ready() + if !initial { // push new info to all of our clients for _, sClient := range server.clients.AllClients() { diff --git a/vendor/github.com/okzk/sdnotify/LICENSE b/vendor/github.com/okzk/sdnotify/LICENSE new file mode 100644 index 00000000..46547518 --- /dev/null +++ b/vendor/github.com/okzk/sdnotify/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 okzk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/okzk/sdnotify/README.md b/vendor/github.com/okzk/sdnotify/README.md new file mode 100644 index 00000000..ee85e052 --- /dev/null +++ b/vendor/github.com/okzk/sdnotify/README.md @@ -0,0 +1,15 @@ +# sdnotify + +sd_notify utility for golang. + +## Installation + + go get github.com/okzk/sdnotify + +## Example + +see [sample/main.go](sample/main.go) + +## License + +MIT \ No newline at end of file diff --git a/vendor/github.com/okzk/sdnotify/notify.go b/vendor/github.com/okzk/sdnotify/notify.go new file mode 100644 index 00000000..f7d70e0d --- /dev/null +++ b/vendor/github.com/okzk/sdnotify/notify.go @@ -0,0 +1,9 @@ +// +build !linux + +package sdnotify + +// SdNotify sends a specified string to the systemd notification socket. +func SdNotify(state string) error { + // do nothing + return nil +} diff --git a/vendor/github.com/okzk/sdnotify/notify_linux.go b/vendor/github.com/okzk/sdnotify/notify_linux.go new file mode 100644 index 00000000..91d1efac --- /dev/null +++ b/vendor/github.com/okzk/sdnotify/notify_linux.go @@ -0,0 +1,23 @@ +package sdnotify + +import ( + "net" + "os" +) + +// SdNotify sends a specified string to the systemd notification socket. +func SdNotify(state string) error { + name := os.Getenv("NOTIFY_SOCKET") + if name == "" { + return ErrSdNotifyNoSocket + } + + conn, err := net.DialUnix("unixgram", nil, &net.UnixAddr{Name: name, Net: "unixgram"}) + if err != nil { + return err + } + defer conn.Close() + + _, err = conn.Write([]byte(state)) + return err +} diff --git a/vendor/github.com/okzk/sdnotify/util.go b/vendor/github.com/okzk/sdnotify/util.go new file mode 100644 index 00000000..71e1e928 --- /dev/null +++ b/vendor/github.com/okzk/sdnotify/util.go @@ -0,0 +1,39 @@ +package sdnotify + +import ( + "errors" + "fmt" +) + +// ErrSdNotifyNoSocket is the error returned when the NOTIFY_SOCKET does not exist. +var ErrSdNotifyNoSocket = errors.New("No socket") + +// Ready sends READY=1 to the systemd notify socket. +func Ready() error { + return SdNotify("READY=1") +} + +// Stopping sends STOPPING=1 to the systemd notify socket. +func Stopping() error { + return SdNotify("STOPPING=1") +} + +// Reloading sends RELOADING=1 to the systemd notify socket. +func Reloading() error { + return SdNotify("RELOADING=1") +} + +// Errno sends ERRNO=? to the systemd notify socket. +func Errno(errno int) error { + return SdNotify(fmt.Sprintf("ERRNO=%d", errno)) +} + +// Status sends STATUS=? to the systemd notify socket. +func Status(status string) error { + return SdNotify("STATUS=" + status) +} + +// Watchdog sends WATCHDOG=1 to the systemd notify socket. +func Watchdog() error { + return SdNotify("WATCHDOG=1") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 732bb349..8889c3ae 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -35,6 +35,9 @@ github.com/go-sql-driver/mysql github.com/gorilla/websocket # github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef ## explicit +# github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd +## explicit +github.com/okzk/sdnotify # github.com/onsi/ginkgo v1.12.0 ## explicit # github.com/onsi/gomega v1.9.0 From 364193df4edbb466109653f731781a6744fac128 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 4 Jul 2021 17:58:48 -0400 Subject: [PATCH 2/3] refactor some start-stop logging --- ergo.go | 4 ---- irc/server.go | 34 ++++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/ergo.go b/ergo.go index 3820aef2..aa53d967 100644 --- a/ergo.go +++ b/ergo.go @@ -192,10 +192,6 @@ Options: logman.Error("server", fmt.Sprintf("Could not load server: %s", err.Error())) os.Exit(1) } - if !arguments["--quiet"].(bool) { - logman.Info("server", "Server running") - defer logman.Info("server", fmt.Sprintf("%s exiting", irc.Ver)) - } if !arguments["--smoke"].(bool) { server.Run() } diff --git a/irc/server.go b/irc/server.go index 24602c1c..acb309df 100644 --- a/irc/server.go +++ b/irc/server.go @@ -81,7 +81,7 @@ type Server struct { rehashMutex sync.Mutex // tier 4 rehashSignal chan os.Signal pprofServer *http.Server - signals chan os.Signal + exitSignals chan os.Signal snomasks SnoManager store *buntdb.DB historyDB mysql.MySQL @@ -100,7 +100,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) { listeners: make(map[string]IRCListener), logger: logger, rehashSignal: make(chan os.Signal, 1), - signals: make(chan os.Signal, len(ServerExitSignals)), + exitSignals: make(chan os.Signal, len(ServerExitSignals)), defcon: 5, } @@ -115,7 +115,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) { } // Attempt to clean up when receiving these signals. - signal.Notify(server.signals, ServerExitSignals...) + signal.Notify(server.exitSignals, ServerExitSignals...) signal.Notify(server.rehashSignal, syscall.SIGHUP) time.AfterFunc(alwaysOnExpirationPollPeriod, server.handleAlwaysOnExpirations) @@ -126,6 +126,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) { // Shutdown shuts down the server. func (server *Server) Shutdown() { sdnotify.Stopping() + server.logger.Info("server", "Stopping server") //TODO(dan): Make sure we disallow new nicks for _, client := range server.clients.AllClients() { @@ -140,19 +141,17 @@ func (server *Server) Shutdown() { } server.historyDB.Close() + server.logger.Info("server", fmt.Sprintf("%s exiting", Ver)) } // Run starts the server. func (server *Server) Run() { - // defer closing db/store - defer server.store.Close() + defer server.Shutdown() for { select { - case <-server.signals: - server.Shutdown() + case <-server.exitSignals: return - case <-server.rehashSignal: server.logger.Info("server", "Rehashing due to SIGHUP") go server.rehash() @@ -714,14 +713,16 @@ func (server *Server) applyConfig(config *Config) (err error) { server.logger.Info("server", "Proxied IPs will be accepted from", strings.Join(config.Server.ProxyAllowedFrom, ", ")) } + // we are now ready to receive connections: err = server.setupListeners(config) - // send other config warnings - if config.Accounts.RequireSasl.Enabled && config.Accounts.Registration.Enabled { - server.logger.Warning("server", "Warning: although require-sasl is enabled, users can still register accounts. If your server is not intended to be public, you must set accounts.registration.enabled to false.") - } - // we are now open for business - sdnotify.Ready() + if err == nil { + // we are now open for business + if initial { + server.logger.Info("server", "Server running") + } + sdnotify.Ready() + } if !initial { // push new info to all of our clients @@ -736,6 +737,11 @@ func (server *Server) applyConfig(config *Config) (err error) { } } + // send other config warnings + if config.Accounts.RequireSasl.Enabled && config.Accounts.Registration.Enabled { + server.logger.Warning("server", "Warning: although require-sasl is enabled, users can still register accounts. If your server is not intended to be public, you must set accounts.registration.enabled to false.") + } + return err } From 5fc7ac41da2cf2704ea2d084e6eb391f5dc17783 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 4 Jul 2021 19:51:35 -0400 Subject: [PATCH 3/3] always mark the service ready after rehash --- irc/server.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/irc/server.go b/irc/server.go index acb309df..3e041c7b 100644 --- a/irc/server.go +++ b/irc/server.go @@ -531,6 +531,7 @@ func (server *Server) rehash() error { defer server.rehashMutex.Unlock() sdnotify.Reloading() + defer sdnotify.Ready() config, err := LoadConfig(server.configFilename) if err != nil { @@ -716,11 +717,8 @@ func (server *Server) applyConfig(config *Config) (err error) { // we are now ready to receive connections: err = server.setupListeners(config) - if err == nil { - // we are now open for business - if initial { - server.logger.Info("server", "Server running") - } + if initial && err == nil { + server.logger.Info("server", "Server running") sdnotify.Ready() }