Quickstart¶
In five minutes you'll ask systemd-hostnamed about your machine, the way
hostnamectl does, then resolve a name through systemd-resolved and handle
its typed errors. You need Go 1.24+ and a Linux host with systemd v257 or newer.
1. Create a module¶
mkdir hostinfo && cd hostinfo
go mod init example.com/hostinfo
go get github.com/rossedman/sdvarlink@latest
2. Write the program¶
main.go
package main
import (
"context"
"errors"
"fmt"
"log"
"time"
"github.com/rossedman/sdvarlink/systemd/latest/hostname" // (1)!
"github.com/rossedman/sdvarlink/varlink"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // (2)!
defer cancel()
c, err := hostname.Dial(ctx) // (3)!
if errors.Is(err, varlink.ErrUnsupported) {
log.Fatal("systemd-hostnamed is not available here (systemd < v257?)")
}
if err != nil {
log.Fatal(err)
}
defer c.Close()
h, err := c.Describe(ctx, hostname.DescribeIn{}) // (4)!
if err != nil {
log.Fatal(err)
}
fmt.Println("Hostname:", h.Hostname)
fmt.Println("OS: ", varlink.Deref(h.OperatingSystemPrettyName)) // (5)!
fmt.Println("Kernel: ", h.KernelName, h.KernelRelease)
fmt.Println("Machine: ", h.MachineID)
}
- One package per Varlink interface.
latestfollows the newest systemd release sdvarlink was generated from;systemd/v257/hostnamewould pin the v257 API instead. - The context bounds the whole conversation: dialing, writing and waiting for the reply.
Dialconnects to the interface's well-known socket,hostname.DefaultSocket(/run/systemd/io.systemd.Hostname). UseDialPathfor any other socket, such as a test fake.- Every method takes a
context.Contextand, if the method has parameters, a<Method>Instruct. It returns a*<Method>Out. - Optional (
?string) fields in the IDL are pointers in Go.varlink.Derefreturns the value, or the zero value fornil.
3. Run it¶
Output (illustrative)
Hostname: build-07
OS: Fedora Linux 42 (Server Edition)
Kernel: Linux 6.14.4-300.fc42.x86_64
Machine: 4b0e31a7c1f24f0c8f6d51c0b3a2e9d4
What just happened¶
sequenceDiagram
autonumber
participant P as your program
participant C as hostname.Client
participant S as /run/systemd/io.systemd.Hostname
P->>C: hostname.Dial(ctx)
C->>S: connect(2) on the Unix socket
P->>C: c.Describe(ctx, DescribeIn{})
C->>S: {"method":"…Describe","parameters":{}}\0
S-->>C: {"parameters":{"Hostname":…}}\0
C-->>P: *DescribeOut
P->>C: c.Close()
Dialopened a Unix socket straight tosystemd-hostnamed. There's no bus and no broker in between.DescribeencodedDescribeInas JSON, wrote it followed by a NUL byte, and waited for one NUL-terminated reply.- The reply was decoded into
DescribeOut. If a newer systemd sends fields these bindings don't know, they land inh.Extrainstead of being dropped. Closereleased the client's connection pool.
4. Add a second service, and an error¶
Resolve names through systemd-resolved. A name that doesn't exist comes back
as a typed error:
resolve.go
package main
import (
"context"
"errors"
"fmt"
"net/netip"
"github.com/rossedman/sdvarlink/systemd/latest/resolve"
)
func lookup(ctx context.Context, name string) error {
c, err := resolve.Dial(ctx)
if err != nil {
return err
}
defer c.Close()
out, err := c.ResolveHostname(ctx, resolve.ResolveHostnameIn{Name: name})
switch {
case errors.Is(err, resolve.ErrNoSuchResourceRecord):
fmt.Printf("%s: no such host\n", name)
return nil
case err != nil:
return err
}
for _, a := range out.Addresses {
b := make([]byte, len(a.Address))
for i, v := range a.Address {
b[i] = byte(v)
}
ip, _ := netip.AddrFromSlice(b)
fmt.Printf("%-20s %s\n", out.Name, ip)
}
return nil
}
Call lookup(ctx, "systemd.io") from main, and try a name that doesn't
exist, such as nope.invalid.
Output (illustrative)
systemd.io 185.199.108.153
systemd.io 185.199.109.153
nope.invalid: no such host
Stuck?
permission denied: the method needs privileges. Retry withsudo, or see the Security model.varlink: service not available on this host: the socket doesn't exist. Checkls /run/systemd/andsystemctl status systemd-hostnamed.org.varlink.service.MethodNotFound: your systemd predates that method. See feature detection.
Where next¶
-
Learn the protocol
Frames,
more,oneway, errors, introspection and fds, in one page. -
Stream replies
List every user, unit or DNS query with
range. -
Test without systemd
Run the same code against a fake server in
go test. -
Browse runnable examples
Fifteen programs, from
resolvectl queryto mounting disk images.