sd

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2019 License: MIT Imports: 11 Imported by: 17

README

gone/sd

Golang systemd library GoDoc

Package gone/sd provides tools for having daemons interact with the Linux systemd init system for socket activation and notification.

This library aims to make it easy to write graceful daemons for Linux Systemd services in the "newstyle" way:

http://www.freedesktop.org/software/systemd/man/daemon.html

Overview

Package gone/sd consists of two parts.

  • Communicating over the systemd notify socket to inform systemd about process status and send file descriptors to the systemd FDSTORE

  • Replacement functions for some of the stdlib "net" package for parsing the environment passed on from systemd to create sockets (and other files) inherited from systemd.

Documentation

Overview

Package sd aims to make it easier to write simple network daemons for Linux systemd

https://www.freedesktop.org/software/systemd/man/daemon.html

Specifically it supports the following:

  • Socket activation for standard standard net package Listeners and Packetconns, with minimal code changes.
  • Notify the init system about startup completion or status updates via the sd_notify(3) interface.
  • Using systemd FDSTORE to hold open file descriptors during restart.
  • Systemd watchdog support

Package "sd" is not depended on systemd as such. If there's no socket activation available the fallback is most often to just create the socket. If there's no notifiy socket, calling sd.Notify() will of course fail.

You can use package "sd" without any interaction with systemd, merely to manage a set of active socket file descriptors using the Cleanup() function to close all file decriptors not in use and Reset() to make all file descriptors in use available for creating Listeners/PacketConns again. This can be used for simple daemon reload schemes to respawn a daemon keeping it's file descriptors open. (with or without systemd).

Index

Constants

View Source
const (
	// Never unlink socket files before listening
	UnixSocketUnlinkPolicyNone uint32 = iota
	// Always unlink socket files before listning
	UnixSocketUnlinkPolicyAlways
	// Always unlink, but stat(2) the file to ensure it's a UNIX socket before unlinking.
	UnixSocketUnlinkPolicySocket
	// Take a flock(2) lock on a socket.lock file before unlinking to ensure no other process is still using it.
	UnixSocketUnlinkPolicyFlock
)

UnixScoketUnlinkPolicy* constants are used to decide how to deal with listening on a UNIX socket where the filesystem file might already exist. One way is to just unlink it, but it's hard to know whether there's still a process using it. The flock(2) advisory locking scheme deals with this in a nice way.

View Source
const (
	// StatusNone - Don't send a STATUS
	StatusNone = iota
	// StatusReady - Tell systemd status is READY
	StatusReady
	// StatusReloading - Tell systemd status is RELOADING
	StatusReloading
	// StatusStopping - Tell systemd status is STOPPING
	StatusStopping
	// StatusWatchdog - Tell the systemd WATCHDOG we are alive
	StatusWatchdog
)

Status* tells what status data to send to systemd over the nofity socket. You can control this in detail yourself by using the Notify() function.

View Source
const (
	// NotifyUnsetEnv flag provided to Notify() to unset the systemd notify/Watchdog env vars
	NotifyUnsetEnv = 1 << iota
	// NotifyWithFds flag to Notify() to instruct it to send active file descriptors along with
	// systemd notify message to FDSTORE
	NotifyWithFds
)

Variables

View Source
var ErrNoSuchFdName = errors.New("No file with the requested name and no requested address")

ErrNoSuchFdName signals an attempt to create a socket without specifying an existing fd name or an address. UNIX sockets require a file system path and can not (like IP socket) listen on all addresses, so it's an error to not provide either a name, or a path.

View Source
var ErrSdNotifyNoSocket = errors.New("No systemd notify socket in environment")

ErrSdNotifyNoSocket is informs the caller that there's no NOTIFY_SOCKET available

Functions

func Cleanup

func Cleanup()

Cleanup closes all inherited file descriptors which have not yet been used by clients of this library by a directly or indirect call to FileWith() The network functions (Listen*, NamedListen*, InheritNamed*) for listening or inheriting sockets will indirectly call FileWith()

func Export

func Export(sdname string, f interface{}) (err error)

Export records a dup() of the file descriptor and makes it managed by the sd package, marked as in active use. Closing the object provided will not close the managed file descriptor, so any socket connection will still be open an be able to be transferred to other processes/objects in open state.

If you want to stop managing the file descriptor and close it, call Forget() on the name, or provide the same object as was exported.

func FileWith

func FileWith(sdname string, tests ...FileTest) (rfile *os.File, rname string, err error)

FileWith returns any file descriptor (as an *os.File) which matches the given systemd name *and* any FileTests provided from the available pool of (inherited) file descriptors. The file descriptor is marked as no longer available and forgotten by the library. If the name requested is "", any file descriptor matching the tests is returned. The actual name is also returned FYI (in case the requested name was "")

The name provided here is *NOT* the same name as the calling .Name() on the returned file. This name is the systemd name as controlled by the systemd socket unit FileDescriptorName=

Calling .Name() on an socket *os.File will usually return information about bound addresses. That's not the name used here.

Notice: Once the file is returned, it's no longer the responsibility of the sd package, so any test to determine whether the file is actually the one you need should be defined and passed as a FileTest. You cannot get a file based on "half-a-test", do some more testing later and then regret and put it back. Write a FileTest function instead. If you want the sd library to track the returned file as in active use, call Export() on it.

func Forget

func Forget(f interface{}) (err error)

Forget makes the sd library forget about its file descriptor (made by Export) associated with either an exported object or a string naming a file descriptor. If more file descriptors are named the same, they are all closed. Forget should be passed, either a string naming the file descriptor, OR the exact object originally exported.

func InheritNamedListener

func InheritNamedListener(wantName string, tests ...FileTest) (l net.Listener, gotName string, err error)

InheritNamedListener returns a net.Listener and its systemd name passing the tests (and name criteria) provided. If there's no inherited FD which can be used the returned listener will be nil. The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func InheritNamedPacketConn

func InheritNamedPacketConn(wantName string, tests ...FileTest) (l net.PacketConn, gotName string, err error)

InheritNamedPacketConn returns a net.Listener and its systemd name passing the tests (and name criteria) provided. If there's no inherited FD which can be used the returned listener will be nil. The returned packetconn will be Export'ed by the sd library. Call Forget() to undo the export.

func Listen

func Listen(nett, laddr string) (net.Listener, error)

Listen returns a net.Listener like net.Listen, but will first check whether an inherited file descriptor is already listening before creating a new socket The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func ListenFdsWithNames

func ListenFdsWithNames() (count int, names []string, err error)

ListenFdsWithNames return the number of inherited filedescriptors and their names, along with any error occurring while inheriting them. Calling Reset() will reset these values too.

func ListenPacket

func ListenPacket(nett, laddr string) (net.PacketConn, error)

ListenPacket returns a net.PacketConn like net.ListenPacket, but will first check whether an inherited file descriptor is already available before creating a new socket The returned packetconn will be Export'ed by the sd library. Call Forget() to undo the export.

func ListenTCP

func ListenTCP(nett string, laddr *net.TCPAddr) (*net.TCPListener, error)

ListenTCP returns a normal *net.TCPListener. It will create a new socket if there's no appropriate inherited file descriptor listening. The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func ListenUDP

func ListenUDP(nett string, laddr *net.UDPAddr) (*net.UDPConn, error)

ListenUDP returns a normal *net.UDPConn. It will create a new socket if there's no appropriate inherited file descriptor listening. The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func ListenUnix

func ListenUnix(nett string, laddr *net.UnixAddr) (*net.UnixListener, error)

ListenUnix is like net.ListenUnix and will return a normal *net.UnixListener. It will create a new socket if there's no appropriate inherited file descriptor listening. The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func ListenUnixgram

func ListenUnixgram(nett string, laddr *net.UnixAddr) (*net.UnixConn, error)

ListenUnixgram returns a normal *net.UnixConn. It will create a new socket if there's no appropriate inherited file descriptor listening. The returned conn will be Export'ed by the sd library. Call Forget() to undo the export.

func NamedListenTCP

func NamedListenTCP(name, nett string, laddr *net.TCPAddr) (l *net.TCPListener, err error)

NamedListenTCP is like ListenTCP, but requires the file descriptor used to have the given systemd socket name The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func NamedListenUDP

func NamedListenUDP(name, net string, laddr *net.UDPAddr) (*net.UDPConn, error)

NamedListenUDP is like ListenUDP, but will require any used inherited file descriptor to have the given systemd socket name. The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func NamedListenUnix

func NamedListenUnix(name, nett string, laddr *net.UnixAddr) (l *net.UnixListener, err error)

NamedListenUnix is like ListenUnix, but will require any used inherited file descriptor to have the given systemd socket name The returned listener will be Export'ed by the sd library. Call Forget() to undo the export.

func NamedListenUnixgram

func NamedListenUnixgram(name, nett string, laddr *net.UnixAddr) (l *net.UnixConn, err error)

NamedListenUnixgram is like ListenUnixgram, but will require any used inherited file descriptor to have the given systemd socket name The returned conn will be Export'ed by the sd library. Call Forget() to undo the export.

func Notify

func Notify(flags int, lines ...string) (err error)

Notify lets you control the message sent to the nofify socket more directly. flags control whether to unset the ENV and/or to include active file descriptors in the message for systemd to store in the FDSTORE

func NotifyStatus

func NotifyStatus(status int, message string) error

NotifyStatus sends systemd the service status over the notify socket.

func ReplaceProcess

func ReplaceProcess(sig syscall.Signal) (int, error)

ReplaceProcess calls StartProcess, after setting up environment variables instructing the new process to signal the process calling ReplaceProcess for termination. To enable this signaling in the child process, call SignalParentTermination when ready.

func ReplaceProcessEnv

func ReplaceProcessEnv(sig syscall.Signal, env []string) (int, error)

ReplaceProcessEnv - like ReplaceProcess, but allows extra environment variables to be passed into the new instance

func Reset

func Reset()

Reset does a Cleanup() and makes the current Exported set of file descriptors available again as if they were inherited.

func SetUnixSocketUnlinkPolicy

func SetUnixSocketUnlinkPolicy(policy uint32)

SetUnixSocketUnlinkPolicy decides how the sd library will deal with stale UNIX socket file when you create a new listening Unix socket which is not inherited from the environment the parent process (maybe systemd socket activation) made for your process.

About UNIX domain sockets: UNIX sockets don't work exactly like TCP/UDP sockets. The kernel doesn't reclaim the "address" (ie. the socket file) when the last file descriptor is closed. The file still hangs around - and will prevent a new bind(2) to that adddress/path.

Go has before version 1.7 solved this by doing unlink(2) on the file when you call Close() on a net.UnixListener. This doesn't play well with systemd socket activation though (or any graceful restart system transferring file descriptors).

Systemd doesn't expect the file to disappear and removing it will prevent clients for connecting to the listener. See: https://github.com/golang/go/issues/13877

Go 1.7 introduced some magic where listeners created with FileListener() would not do this.

Go 1.8 allow the user to control this with SetUnlinkOnClose().

The unix(7) manpage says: "Binding to a socket with a filename creates a socket in the filesystem that must be deleted by the caller when it is no longer needed".

However...

This may not be easy to guarantee... a process can crash before it removes the file. As evidence the same unix(7) man page exemplifies this with code where it calls unlink(2) just before bind(2) "In case the program exited inadvertently on the last run, remove the socket."

Always unlinking the socket file might not be the thing you want either. There might be another instance of your daemon using it. Then unlinking would "steal" the address from the other process. This might be what you want - but you would have a small window without a socket file where clients would get rejected.

So... the sd library will not try to unlink on close. In fact, it uses SetUnlinkOnClose(false) to never do this for any UNIX listener. Instead the sd library encourages "UnlinkBeforeBind". In other words: When it needs to create a new socket file it employs a UnixSocketUnlinkPolicy. This policy can be none (do not unlinK) or always unlink. Or it can be "test if the socket file is a socket file first". None of these will however "just work" in any scenario. Therefore there's a "use flock(2)" policy to use advisory file locking to ensure the socket file is only removed when there's no other process using it.

The default is UnixSocketUnlinkPolicySocket. You need to change this ( like, in you init() ) to use the flock(2) mechanism.

func SignalParentTermination

func SignalParentTermination() error

SignalParentTermination signals any parent who have asked to be terminated via the ENV

func StartProcess

func StartProcess(env []string) (int, error)

StartProcess starts a new process passing it the open files. It starts a new process using the same environment and arguments as when it was originally started. This allows for a newly deployed binary to be started. It returns the pid of the newly started process when successful.

func WatchdogEnabled

func WatchdogEnabled() (enabled bool, when time.Duration)

WatchdogEnabled tell whether systemd asked us to enable watchdog notifications.

Types

type FileTest

type FileTest func(*os.File) (bool, error)

FileTest is a function returning whether an *os.File fulfills certain criteria. You can write these your self - and should, if the provided ones don't cover your requirements fully.

func IsFifo

func IsFifo(path string) FileTest

IsFifo tests whether the *os.File is a FIFO at the given path

func IsListening

func IsListening(want bool) FileTest

IsListening FileTest to determine whether a file descriptor is a listening socket or not.

func IsSoReusePort

func IsSoReusePort() FileTest

IsSoReusePort - Test if SO_REUSEPORT is set on the socket

func IsSocket

func IsSocket(family, sotype int, listening int) FileTest

IsSocket is similar to libsystemd sd-daemon sd_is_socket() Test whether the *os.File is a socket of family, socket type and whether it's listening. Values for "dont' care" is respectively 0, 0, and -1

func IsSocketInet

func IsSocketInet(family int, sotype int, listening int, port uint16) FileTest

IsSocketInet is similar to sd_is_socket_inet Test if the *os.File is a socket of AF_INET/AF_INET6 of the given socket time, listening (0,1,-1) and port

func IsSocketUnix

func IsSocketUnix(sotype int, listening int, path *string) FileTest

IsSocketUnix is like libsystemd sd-daemon sd_is_socket_unix() path is a pointer to allow for "don't care" option by passing nil pointer. The empty string is the unnamed socket.

func IsTCPListener

func IsTCPListener(addr *net.TCPAddr) FileTest

IsTCPListener tests whether the *os.File is a listening TCP socket. If addr != nil it's tested whether it is bound to that address.

func IsUDPListener

func IsUDPListener(addr *net.UDPAddr) FileTest

IsUDPListener is like IsTCPListener, but for UDP

func IsUNIXListener

func IsUNIXListener(addr *net.UnixAddr) FileTest

IsUNIXListener tests if the *os.File is a unix(7) socket listening on the given address. Setting addr == nil, means "any" AF_UNIX address. Including linux abstract sockets.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL