Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 0 additions & 59 deletions systemd/sdnotify.go

This file was deleted.

119 changes: 119 additions & 0 deletions systemd/sdnotify_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
//go:build linux

/*
* Copyright (C) 2017-2026 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package systemd

import (
"fmt"
"net"
"os"
"runtime"
"strings"

"golang.org/x/sys/unix"
)

// SdNotify sends the given state string notification to systemd.
//
// inspired by libsystemd/sd-daemon/sd-daemon.c from the systemd source
func SdNotify(notifyState string) error {
if notifyState == "" {
return fmt.Errorf("invalid empty notify state")
}

conn, err := sdNotifyConn()
if err != nil {
return err
}
defer conn.Close()

_, err = conn.Write([]byte(notifyState))
return err
}

// SdNotifyWithFds sends the given state string notification and file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this should be private and we should have a number of public wappers that call it with specific value of notifyState?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a systemd primitive that might be used in different packages like SdNotify, also trying to follow libsystemd

// descriptors associated with passed files to systemd.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SdNotify to send fd removal requests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SdNotifyWithFds could be used to send fds to systemd unrelated to the fdstore

//
// Caller is responsible for closing the passed files.
//
// inspired by libsystemd/sd-daemon/sd-daemon.c from the systemd source
Comment thread
zyga marked this conversation as resolved.
func SdNotifyWithFds(notifyState string, files ...*os.File) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what files would be sent with FDSTOREREMOVE=1 ?

@ZeyadYasser ZeyadYasser Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if notifyState == "" {
return fmt.Errorf("invalid empty notify state")
}

if len(files) == 0 {
return fmt.Errorf("at least one file is required")
}

conn, err := sdNotifyConn()
if err != nil {
return err
}
defer conn.Close()

rawConn, err := conn.SyscallConn()
if err != nil {
return err
}

var sendMsgErr error
err = rawConn.Control(func(sdNotifyFd uintptr) {
fds := make([]int, len(files))
for i := range files {
fds[i] = int(files[i].Fd())
}

rights := unix.UnixRights(fds...)
creds := unix.UnixCredentials(&unix.Ucred{
Pid: int32(os.Getpid()),
Uid: uint32(os.Getuid()),
Gid: uint32(os.Getgid()),
})
oob := append(creds, rights...)
sendMsgErr = unix.Sendmsg(int(sdNotifyFd), []byte(notifyState), oob, nil, 0)

// Ensure finalizer is not called for passed files.
runtime.KeepAlive(files)
})
Comment thread
zyga marked this conversation as resolved.
if err != nil {
return err
}

return sendMsgErr
}

var osGetenv = os.Getenv

func sdNotifyConn() (*net.UnixConn, error) {
notifySocket := osGetenv("NOTIFY_SOCKET")
if notifySocket == "" {
return nil, fmt.Errorf("cannot find NOTIFY_SOCKET environment variable")
}
if !strings.HasPrefix(notifySocket, "@") && !strings.HasPrefix(notifySocket, "/") {
return nil, fmt.Errorf("cannot use NOTIFY_SOCKET %q", notifySocket)
}

raddr := &net.UnixAddr{
Name: notifySocket,
Net: "unixgram",
}
return net.DialUnix("unixgram", nil, raddr)
}
Loading
Loading