1//go:build linux2// +build linux34/*5Maddy Mail Server - Composable all-in-one email server.6Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors78This program is free software: you can redistribute it and/or modify9it under the terms of the GNU General Public License as published by10the Free Software Foundation, either version 3 of the License, or11(at your option) any later version.1213This program is distributed in the hope that it will be useful,14but WITHOUT ANY WARRANTY; without even the implied warranty of15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16GNU General Public License for more details.1718You should have received a copy of the GNU General Public License19along with this program. If not, see <https://www.gnu.org/licenses/>.20*/2122package maddy2324import (25 "errors"26 "fmt"27 "io"28 "net"29 "os"30 "strings"31 "syscall"3233 "github.com/foxcpp/maddy/framework/log"34)3536type SDStatus string3738const (39 SDReady = "READY=1"40 SDReloading = "RELOADING=1"41 SDStopping = "STOPPING=1"42)4344var ErrNoNotifySock = errors.New("no systemd socket")4546func sdNotifySock() (*net.UnixConn, error) {47 sockAddr := os.Getenv("NOTIFY_SOCKET")48 if sockAddr == "" {49 return nil, ErrNoNotifySock50 }51 if strings.HasPrefix(sockAddr, "@") {52 sockAddr = "\x00" + sockAddr[1:]53 }5455 return net.DialUnix("unixgram", nil, &net.UnixAddr{56 Name: sockAddr,57 Net: "unixgram",58 })59}6061func setScmPassCred(sock *net.UnixConn) error {62 sConn, err := sock.SyscallConn()63 if err != nil {64 return err65 }6667 var sockoptErr error68 if err := sConn.Control(func(fd uintptr) {69 sockoptErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)70 }); err != nil {71 return err72 }73 if sockoptErr != nil {74 return sockoptErr75 }76 return nil77}7879func systemdStatus(status SDStatus, desc string) {80 sock, err := sdNotifySock()81 if err != nil {82 if !errors.Is(err, ErrNoNotifySock) {83 log.Println("systemd: failed to acquire notify socket:", err)84 }85 return86 }87 defer sock.Close()8889 if err := setScmPassCred(sock); err != nil {90 log.Println("systemd: failed to set SCM_PASSCRED on the socket:", err)91 }9293 if desc != "" {94 if _, err := io.WriteString(sock, fmt.Sprintf("%s\nSTATUS=%s", status, desc)); err != nil {95 log.Println("systemd: I/O error:", err)96 }97 log.Debugf(`systemd: %s STATUS="%s"`, status, desc)98 } else {99 if _, err := io.WriteString(sock, string(status)); err != nil {100 log.Println("systemd: I/O error:", err)101 }102 log.Debugf(`systemd: %s`, status)103 }104}105106func systemdStatusErr(reportedErr error) {107 sock, err := sdNotifySock()108 if err != nil {109 if !errors.Is(err, ErrNoNotifySock) {110 log.Println("systemd: failed to acquire notify socket:", err)111 }112 return113 }114 defer sock.Close()115116 if err := setScmPassCred(sock); err != nil {117 log.Println("systemd: failed to set SCM_PASSCRED on the socket:", err)118 }119120 var errno syscall.Errno121 if errors.As(reportedErr, &errno) {122 log.Debugf(`systemd: ERRNO=%d STATUS="%v"`, errno, reportedErr)123 if _, err := io.WriteString(sock, fmt.Sprintf("ERRNO=%d\nSTATUS=%v", errno, reportedErr)); err != nil {124 log.Println("systemd: I/O error:", err)125 }126 return127 }128129 if _, err := io.WriteString(sock, fmt.Sprintf("STATUS=%v\n", reportedErr)); err != nil {130 log.Println("systemd: I/O error:", err)131 }132 log.Debugf(`systemd: STATUS="%v"`, reportedErr)133}