package sdnotify import ( "errors" "fmt" "net" "os" "strings" "time" ) const ( ReadyEnabled = "READY=1" StoppingEnabled = "STOPPING=1" ReloadingEnabled = "RELOADING=1" ) var ( socket *net.UnixAddr start = time.Now() errSocketVariableIsNotPresent = errors.New("\"NOTIFY_SOCKET\" environment variable is not present") errSocketIsNotInitialized = errors.New("socket is not initialized") ) // Initializes socket with provided name of // environment variable. func InitSocket() error { notifySocket := os.Getenv("NOTIFY_SOCKET") if notifySocket == "" { return errSocketVariableIsNotPresent } socket = &net.UnixAddr{ Name: notifySocket, Net: "unixgram", } return nil } // FlagAndStatus sends systemd a combination of a // well-known status and STATUS=%s{status}, separated by newline. func FlagAndStatus(status string) error { if status == ReloadingEnabled { // From https://www.man7.org/linux/man-pages/man5/systemd.service.5.html // // When initiating the reload process the service is // expected to reply with a notification message via // sd_notify(3) that contains the "RELOADING=1" field in // combination with "MONOTONIC_USEC=" set to the current // monotonic time (i.e. CLOCK_MONOTONIC in // clock_gettime(2)) in μs, formatted as decimal string. // Once reloading is complete another notification message // must be sent, containing "READY=1". // // For MONOTONIC_USEC format refer to https://www.man7.org/linux/man-pages/man3/sd_notify.3.html status += fmt.Sprintf("\nMONOTONIC_USEC=%d", uint64(time.Since(start))/1e3 /* microseconds in nanoseconds */) } status += "\nSTATUS=" + strings.TrimSuffix(status, "=1") return Send(status) } // Status sends systemd notify STATUS=%s{status}. func Status(status string) error { return Send("STATUS=" + status) } // Send state through the notify socket if any. // If the notify socket was not detected, it returns an error. func Send(state string) error { if socket == nil { return errSocketIsNotInitialized } conn, err := net.DialUnix(socket.Net, nil, socket) if err != nil { return fmt.Errorf("can't open unix socket: %v", err) } defer conn.Close() if _, err = conn.Write([]byte(state)); err != nil { return fmt.Errorf("can't write into the unix socket: %v", err) } return nil }