mirror of
https://github.com/foxcpp/maddy.git
synced 2025-04-04 21:47:40 +03:00
Use systemd notify socket to report process status
It has all sorts of benefits due to the service manager being aware of the starting/running/stopping state, see systemd.service(5) On top of that, start-up errors are reported using STATUS= key, so they will be easier to see in the 'systemctl status' output.
This commit is contained in:
parent
cd514fd91a
commit
974dd3c7f8
7 changed files with 141 additions and 3 deletions
3
dist/systemd/maddy.service
vendored
3
dist/systemd/maddy.service
vendored
|
@ -6,6 +6,9 @@ Documentation=https://github.com/foxcpp/maddy/wiki
|
|||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
NotifyAccess=main
|
||||
|
||||
# For systemd before 235. Assumes pre-existing user & group.
|
||||
User=maddy
|
||||
Group=maddy
|
||||
|
|
3
dist/systemd/maddy@.service
vendored
3
dist/systemd/maddy@.service
vendored
|
@ -3,6 +3,9 @@ Description=maddy mail server (using %i.conf)
|
|||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
NotifyAccess=main
|
||||
|
||||
# For systemd before 235. Assumes pre-existing user & group.
|
||||
User=maddy
|
||||
Group=maddy
|
||||
|
|
|
@ -55,8 +55,12 @@ func moduleMain(cfg []config.Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
systemdStatus(SDReady, "Listening for incoming connections...")
|
||||
|
||||
handleSignals()
|
||||
|
||||
systemdStatus(SDStopping, "Waiting for running transactions to complete...")
|
||||
|
||||
for _, inst := range insts {
|
||||
if closer, ok := inst.(io.Closer); ok {
|
||||
if err := closer.Close(); err != nil {
|
||||
|
|
8
run.go
8
run.go
|
@ -43,6 +43,7 @@ func Run() int {
|
|||
var err error
|
||||
log.DefaultLogger.Out, err = LogOutputOption(strings.Split(*logTargets, " "))
|
||||
if err != nil {
|
||||
systemdStatusErr(err)
|
||||
log.Println(err)
|
||||
return 2
|
||||
}
|
||||
|
@ -51,18 +52,21 @@ func Run() int {
|
|||
|
||||
f, err := os.Open(*configPath)
|
||||
if err != nil {
|
||||
log.Printf("cannot open %q: %v\n", *configPath, err)
|
||||
systemdStatusErr(err)
|
||||
log.Println(err)
|
||||
return 2
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
cfg, err := parser.Read(f, *configPath)
|
||||
if err != nil {
|
||||
log.Printf("cannot parse %q: %v\n", *configPath, err)
|
||||
systemdStatusErr(err)
|
||||
log.Println(err)
|
||||
return 2
|
||||
}
|
||||
|
||||
if err := moduleMain(cfg); err != nil {
|
||||
systemdStatusErr(err)
|
||||
log.Println(err)
|
||||
return 2
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ func handleSignals() os.Signal {
|
|||
|
||||
s := <-sig
|
||||
go func() {
|
||||
s := waitForSignal()
|
||||
s := handleSignals()
|
||||
log.Printf("forced shutdown due to signal (%v)!", s)
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
|
110
systemd.go
Normal file
110
systemd.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
//+build linux
|
||||
|
||||
package maddy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/foxcpp/maddy/log"
|
||||
)
|
||||
|
||||
type SDStatus string
|
||||
|
||||
const (
|
||||
SDReady = "READY=1"
|
||||
SDStopping = "STOPPING=1"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoNotifySock = errors.New("no systemd socket")
|
||||
)
|
||||
|
||||
func sdNotifySock() (*net.UnixConn, error) {
|
||||
sockAddr := os.Getenv("NOTIFY_SOCKET")
|
||||
if sockAddr == "" {
|
||||
return nil, ErrNoNotifySock
|
||||
}
|
||||
if strings.HasPrefix(sockAddr, "@") {
|
||||
sockAddr = "\x00" + sockAddr[1:]
|
||||
}
|
||||
|
||||
return net.DialUnix("unixgram", nil, &net.UnixAddr{
|
||||
Name: sockAddr,
|
||||
Net: "unixgram",
|
||||
})
|
||||
}
|
||||
|
||||
func setScmPassCred(sock *net.UnixConn) error {
|
||||
sConn, err := sock.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sConn.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptByte(int(fd), syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func systemdStatus(status SDStatus, desc string) {
|
||||
sock, err := sdNotifySock()
|
||||
if err != nil {
|
||||
if err != ErrNoNotifySock {
|
||||
log.Println("systemd: failed to acquire notify socket:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer sock.Close()
|
||||
|
||||
if err := setScmPassCred(sock); err != nil {
|
||||
log.Println("failed to set SCM_PASSCRED on the socket for communication with systemd:", err)
|
||||
}
|
||||
|
||||
if desc != "" {
|
||||
if _, err := io.WriteString(sock, fmt.Sprintf("%s\nSTATUS=%s", status, desc)); err != nil {
|
||||
log.Println("systemd: I/O error:", err)
|
||||
}
|
||||
log.Debugf(`systemd: %s STATUS="%s"`, status, desc)
|
||||
} else {
|
||||
if _, err := io.WriteString(sock, string(status)); err != nil {
|
||||
log.Println("systemd: I/O error:", err)
|
||||
}
|
||||
log.Debugf(`systemd: %s`, status)
|
||||
}
|
||||
}
|
||||
|
||||
func systemdStatusErr(reportedErr error) {
|
||||
sock, err := sdNotifySock()
|
||||
if err != nil {
|
||||
if err != ErrNoNotifySock {
|
||||
log.Println("systemd: failed to acquire notify socket:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer sock.Close()
|
||||
|
||||
if err := setScmPassCred(sock); err != nil {
|
||||
log.Println("systemd: failed to set SCM_PASSCRED on the socket:", err)
|
||||
}
|
||||
|
||||
var errno syscall.Errno
|
||||
if errors.As(reportedErr, &errno) {
|
||||
log.Debugf(`systemd: ERRNO=%d STATUS="%v"`, errno, reportedErr)
|
||||
if _, err := io.WriteString(sock, fmt.Sprintf("ERRNO=%d\nSTATUS=%v", errno, reportedErr)); err != nil {
|
||||
log.Println("systemd: I/O error:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := io.WriteString(sock, fmt.Sprintf("STATUS=%v\n", reportedErr)); err != nil {
|
||||
log.Println("systemd: I/O error:", err)
|
||||
}
|
||||
log.Debugf(`systemd: STATUS="%v"`, reportedErr)
|
||||
}
|
14
systemd_nonlinux.go
Normal file
14
systemd_nonlinux.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
//+build !linux
|
||||
|
||||
package maddy
|
||||
|
||||
type SDStatus string
|
||||
|
||||
const (
|
||||
SDReady = "READY=1"
|
||||
SDStopping = "STOPPING=1"
|
||||
)
|
||||
|
||||
func systemdStatus(SDStatus, string) {}
|
||||
|
||||
func systemdStatusErr(error) {}
|
Loading…
Add table
Add a link
Reference in a new issue