systemd

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Apr 3, 2023 License: MIT Imports: 14 Imported by: 0

README

systemd

Documentation Go Report Card

This package provides an access to systemd via D-Bus to list services (think systemctl list-units) with a low overhead for a caller. If you find the API too limiting or missing some of functionality, perhaps https://github.com/coreos/go-systemd might suit you better.

c, err := systemd.New()
if err != nil {
    log.Fatal(err)
}
defer c.Close()

err = c.ListUnits(systemd.IsService, func(u *systemd.Unit) {
    fmt.Printf("%s %s\n", u.Name, u.ActiveState)
})
if err != nil {
    log.Fatal(err)
}
// Output:
// dirmngr.service inactive
// dbus.service active
// snapd.session-agent.service inactive
// gpg-agent.service inactive
// pk-debconf-helper.service inactive

Check out units program to see how to get PIDs of services.

$ go run ./cmd/units -svc
0 dirmngr.service inactive
2375 dbus.service active
0 snapd.session-agent.service inactive
0 gpg-agent.service inactive
0 pk-debconf-helper.service inactive

You can get the same results with dbus-send.

$ dbus-send --system --print-reply --dest=org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager.ListUnits
$ dbus-send --system --print-reply --dest=org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/dbus_2eservice org.freedesktop.DBus.Properties.Get string:'org.freedesktop.systemd1.Service' string:'MainPID'

Testing

Run tests and linters.

$ go test -v -count=1 .
$ golangci-lint run

Benchmarks help to spot performance changes and troubleshoot performance issues. For example, you can see where and how much memory gets allocated when a 35KB D-Bus ListUnits reply is decoded into 157 Unit structs.

$ go test -run=^$ -bench=^BenchmarkDecodeListUnits$ -benchmem -memprofile list_units.allocs
$ go tool pprof list_units.allocs

It is recommended to run benchmarks multiple times and check how stable they are using Benchstat tool.

$ go test -timeout 20m -bench=. -benchmem -count=50 . | tee bench-new.txt
$ benchstat bench-new.txt

The old and new stats are compared as follows.

$ benchstat bench-old.txt bench-new.txt
name               old time/op    new time/op    delta
AuthExternal-2        475ns ± 2%     467ns ± 2%    -1.72%  (p=0.000 n=46+45)
DecodeString-2       54.3ns ± 3%    53.4ns ± 4%    -1.67%  (p=0.000 n=49+50)
EscapeBusLabel-2     43.8ns ± 2%    43.2ns ± 2%    -1.35%  (p=0.000 n=48+50)
DecodeHeader-2        340ns ± 4%     340ns ±12%    -0.17%  (p=0.017 n=48+45)
EncodeHeader-2        185ns ± 1%     187ns ± 3%    +1.28%  (p=0.000 n=48+49)
EncodeHello-2         224ns ± 2%     221ns ± 3%    -1.03%  (p=0.000 n=46+50)
DecodeHello-2         122ns ± 4%      84ns ±11%   -31.21%  (p=0.000 n=49+49)
EncodeMainPID-2       383ns ± 4%     383ns ± 3%      ~     (p=0.548 n=43+48)
DecodeMainPID-2       140ns ± 2%      96ns ± 3%   -31.63%  (p=0.000 n=48+50)
EncodeListUnits-2     234ns ± 2%     236ns ± 2%    +0.61%  (p=0.001 n=41+47)
DecodeListUnits-2    92.9µs ± 2%    92.5µs ± 5%    -0.39%  (p=0.028 n=48+45)

name               old alloc/op   new alloc/op   delta
AuthExternal-2        80.0B ± 0%     80.0B ± 0%      ~     (all equal)
DecodeString-2        0.00B          0.00B           ~     (all equal)
EscapeBusLabel-2      0.00B          0.00B           ~     (all equal)
DecodeHeader-2        15.0B ± 0%     15.0B ± 0%      ~     (all equal)
EncodeHeader-2        0.00B          0.00B           ~     (all equal)
EncodeHello-2         0.00B          0.00B           ~     (all equal)
DecodeHello-2         29.0B ± 0%      5.0B ± 0%   -82.76%  (p=0.000 n=50+50)
EncodeMainPID-2       45.0B ± 0%     45.0B ± 0%      ~     (all equal)
DecodeMainPID-2       24.0B ± 0%      0.0B       -100.00%  (p=0.000 n=50+50)
EncodeListUnits-2     0.00B          0.00B           ~     (all equal)
DecodeListUnits-2    21.0kB ± 0%    20.9kB ± 0%    -0.11%  (p=0.000 n=50+50)

name               old allocs/op  new allocs/op  delta
AuthExternal-2         3.00 ± 0%      3.00 ± 0%      ~     (all equal)
DecodeString-2         0.00           0.00           ~     (all equal)
EscapeBusLabel-2       0.00           0.00           ~     (all equal)
DecodeHeader-2         0.00           0.00           ~     (all equal)
EncodeHeader-2         0.00           0.00           ~     (all equal)
EncodeHello-2          0.00           0.00           ~     (all equal)
DecodeHello-2          1.00 ± 0%      0.00       -100.00%  (p=0.000 n=50+50)
EncodeMainPID-2        0.00           0.00           ~     (all equal)
DecodeMainPID-2        1.00 ± 0%      0.00       -100.00%  (p=0.000 n=50+50)
EncodeListUnits-2      0.00           0.00           ~     (all equal)
DecodeListUnits-2      6.00 ± 0%      5.00 ± 0%   -16.67%  (p=0.000 n=50+50)

When there is a statistically significant improvement, please update bench-old.txt and the benchmark results above.

$ cp bench-new.txt bench-old.txt

Documentation

Overview

Package systemd provides access to systemd via D-Bus using Unix domain sockets as a transport. The objective of this package is to list services with a low overhead for a caller.

Index

Constants

View Source
const (
	// DefaultConnectionTimeout is the default
	// read/write timeout associated with the connection.
	DefaultConnectionTimeout = time.Second
	// DefaultConnectionReadSize is the default size (in bytes)
	// of the buffer which is used for reading from a connection.
	// Buffering reduces count of read syscalls,
	// e.g., ListUnits makes 12 read syscalls when decoding 35KB message
	// using 4KB buffer.
	// It takes over 4K syscalls without buffering to decode the same message.
	DefaultConnectionReadSize = 4096
	// DefaultStringConverterSize is the default buffer size (in bytes)
	// of the string converter that is used to convert bytes to strings
	// with less allocs.
	//
	// After trying various buffer sizes on ListUnits,
	// a 4KB buffer showed 24.96 KB/op and 7 allocs/op
	// in a benchmark when decoding 35KB message.
	DefaultStringConverterSize = 4096
)

Variables

This section is empty.

Functions

func IsService

func IsService(fieldIndex int, s []byte) bool

IsService is a predicate that filters systemd services, i.e., units whose name (field index 0) ends with ".service". For example, "ssh.service".

A benchmark showed ~4.5 less KB/op when decoding 35KB message.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client provides access to systemd via dbus. A caller shouldn't use Client concurrently.

func New

func New(opts ...Option) (*Client, error)

New creates a new Client to access systemd via dbus.

By default it connects to the system message bus using address found in DBUS_SYSTEM_BUS_ADDRESS environment variable. If that variable is not set, the Client will try to connect to the well-known address unix:path=/var/run/dbus/system_bus_socket, see https://dbus.freedesktop.org/doc/dbus-specification.html.

func (*Client) Close

func (c *Client) Close() error

Close closes the connection.

func (*Client) ListUnits

func (c *Client) ListUnits(p Predicate, f func(*Unit)) error

ListUnits fetches systemd units, optionally filters them with a given predicate, and calls f. The pointer to Unit struct in f must not be retained, because its fields change on each f call.

Note, don't call any Client's methods within f, because concurrent reading from the same underlying connection is not supported.

func (*Client) MainPID

func (c *Client) MainPID(service string) (uint32, error)

MainPID fetches the main PID of the service. If a service is inactive (see Unit.ActiveState), the returned PID will be zero.

Note, you can't call this method within ListUnits's f func, because that would imply concurrent reading from the same underlying connection. Simply waiting on a lock won't help, because ListUnits won't be able to finish waiting for MainPID, thus creating a deadlock.

func (*Client) Reset added in v0.0.4

func (c *Client) Reset() error

Reset resets the client forcing it to reconnect, perform external auth, and send Hello message.

type Config

type Config struct {
	// contains filtered or unexported fields
}

Config represents a Client config.

type Option

type Option func(*Config)

Option sets up a Config.

func WithAddress added in v0.0.4

func WithAddress(addr string) Option

WithAddress sets a bus address.

func WithConnectionReadSize

func WithConnectionReadSize(size int) Option

WithConnectionReadSize sets a size of a buffer which is used for reading from a D-Bus connection. Bigger the buffer, less read syscalls will be made.

func WithSerialCheck

func WithSerialCheck() Option

WithSerialCheck enables checking of message serials, i.e., the Client will compare the serial number sent within a message to D-Bus with the serial received in the reply.

Note, this requires decoding of header fields which incurs extra allocs. There shouldn't be any request/reply mishmash because the Client guarantees that the underlying D-Bus connection is accessed sequentially.

func WithStringConverterSize

func WithStringConverterSize(size int) Option

WithStringConverterSize sets a buffer size of the string converter to reduce allocs.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the read and write timeouts associated with the connection.

type Predicate

type Predicate func(fieldIndex int, value []byte) bool

Predicate is used to filter out a decoded struct based on its field index and a value. This helps to reduce memory consumption because a decoder can ignore the remaining struct fields. For example, D-Bus ListUnits returns tens of kilobytes in a reply, but a caller is interested only in service units.

The field name would be more convenient to work with compared to a field index, but that causes unintended allocs.

type Unit

type Unit struct {
	// Name is the primary unit name.
	Name string
	// Description is the human readable description.
	Description string
	// LoadState is the load state, i.e., whether the unit file has been loaded successfully.
	LoadState string
	// ActiveState is the active state, i.e., whether the unit is currently started or not.
	ActiveState string
	// SubState is the sub state, i.e.,
	// a more fine-grained version of the active state that
	// is specific to the unit type, which the active state is not.
	SubState string
	// Followed is a unit that is being followed in its state by this unit,
	// if there is any, otherwise the empty string.
	Followed string
	// Path is the unit object path.
	Path string
	// JobID is the numeric job ID
	// if there is a job queued for the job unit, 0 otherwise.
	JobID uint32
	// JobType is the job type.
	JobType string
	// JobPath is the job object path.
	JobPath string
}

Unit represents a currently loaded systemd unit. Note that units may be known by multiple names at the same name, and hence there might be more unit names loaded than actual units behind them.

Directories

Path Synopsis
cmd
units
Program units prints systemd units to show how the package can be configured if needed.
Program units prints systemd units to show how the package can be configured if needed.

Jump to

Keyboard shortcuts

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