Home / Alt manpages / varlinkctl(1)

  • varlinkctl(1)
  • User command
  • linux

Inspect and Call Varlink Services with varlinkctl

Varlink services describe themselves, and varlinkctl is how you read that description before calling anything blind. This guide finds what a service offers, reads its exact interface, and calls a method with plain JSON. The examples use varlinkctl from systemd 255.4-1ubuntu8.17: the inspection commands never change a service or its configuration, only a method call can.

Allow about fifteen minutes. You need a shell, the varlinkctl command, and access to a Varlink socket or executable. Most inspection commands are ordinary user commands. A protected socket or executable may need elevated privileges, but sudo does not make an unsafe method call safe: a method may have side effects, so read its interface before invoking it.

1. Confirm the installed command

Start by checking the binary and package version. This is read-only and needs no elevated privileges:

$ command -v varlinkctl
/usr/bin/varlinkctl
$ varlinkctl --version
systemd 255 (255.4-1ubuntu8.17)

The installed man page documents the commands used here as additions in version 255. If your version is older, check varlinkctl --help and its local man page before copying an example.

Checkpoint

If command -v finds nothing, install the systemd package provided by your distribution or use the executable from the correct package. Do not download a replacement binary into a system path just to make this guide work.

2. Choose a service address

  • unix:/absolute/path refers to a Unix socket. An abstract socket uses unix:@name.
  • exec:/absolute/path refers to an executable that varlinkctl starts.
  • An absolute socket or executable path can be used directly. A relative path must begin with ./.

On this host, systemd-resolved exposes a Varlink socket at /run/systemd/resolve/io.systemd.Resolve. Check that the path exists before using it:

$ test -S /run/systemd/resolve/io.systemd.Resolve && echo 'socket found'
socket found

Replace that path with the socket or executable your own service supplies. Do not guess an address from a service name: a missing socket is usually a service or installation problem, not a reason to create one by hand.

3. Read the service summary

Use info to ask the service for its vendor, product, version, URL and implemented interfaces:

$ varlinkctl info /run/systemd/resolve/io.systemd.Resolve
    Vendor: The systemd Project
   Product: systemd (systemd-resolved)
   Version: 255 (255.4-1ubuntu8.17)
       URL: https://systemd.io/
Interfaces: io.systemd
            io.systemd.Resolve
            org.varlink.service

This describes the endpoint that answered. It does not prove every method is harmless, or that a service is suitable for an untrusted caller.

Checkpoint

Copy the interface name you intend to inspect exactly, including capitalisation and full stops. A service can implement several interfaces, and a shortened name will not identify the same one.

4. List and inspect the interface

If you only need the names, use list-interfaces:

$ varlinkctl list-interfaces /run/systemd/resolve/io.systemd.Resolve
io.systemd
io.systemd.Resolve
org.varlink.service

Then use introspect with one complete interface name. It returns the IDL, including types, methods, parameters and return values:

$ varlinkctl introspect /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve
interface io.systemd.Resolve
type ResolvedAddress(
        ifindex: ?int,
        ...

The output above is intentionally abbreviated: read the complete output from your own service before constructing a call. In particular, check which fields are required, which are optional, and whether a method can return multiple replies.

For readable terminal output, add -j where JSON output is available. The default JSON mode is short, with minimal whitespace. -j gives pretty JSON on an interactive terminal but short JSON when output is piped, so scripts should never depend on the indentation.

5. Make one JSON method call

Use the fully qualified method name and a JSON object containing its arguments. This example asks systemd-resolved to resolve an IPv4 address:

$ varlinkctl call /run/systemd/resolve/io.systemd.Resolve \
    io.systemd.Resolve.ResolveHostname \
    '{"name":"systemd.io","family":2}' -j
{
        "addresses" : [
                {
                        "ifindex" : 2,
                        "family" : 2,
                        "address" : [
                                185,
                                199,
                                111,
                                153
                        ]
                }
        ],
        "name" : "systemd.io",
        "flags" : 1048577
}

Your address list, interface index and flags will differ. The useful check is that the response is a JSON object matching the method's declared return fields. If a method takes no parameters, pass {}: omitting the argument object does something else entirely, since varlinkctl then reads JSON from standard input instead.

For example, this is equivalent when the input is kept in a shell variable:

$ params='{"name":"systemd.io","family":2}'
$ printf '%s\n' "$params" | varlinkctl call \
    /run/systemd/resolve/io.systemd.Resolve \
    io.systemd.Resolve.ResolveHostname -j
{"addresses":[...],"name":"systemd.io","flags":1048577}

Keep JSON in single quotes when writing it directly in a POSIX shell. If values come from users or another command, construct and validate the JSON with a JSON-aware tool rather than concatenating untrusted text into a shell command.

6. Handle streaming and one-way calls deliberately

Most calls expect one reply.

  • --more is for methods that explicitly support multiple replies. It sets Varlink's more flag and switches output to JSON-SEQ so separate reply objects can be told apart. Replace the placeholders below only after introspection: the command keeps running until the service marks the final reply, so a method that does not support this may fail or wait unexpectedly. Do not use it merely because you want verbose output.
  • --oneway tells the service not to send a reply. varlinkctl exits immediately after sending the request. Use it only when the method contract allows one-way calls; there is no response to confirm success, so record that limitation in scripts and monitoring.
$ varlinkctl --more call SOCKET_PATH FULL.INTERFACE.Method '{}'

Safety boundary

Calling a method can change state, trigger work or expose data, and there is no general undo command in varlinkctl. For a state-changing method, identify the service owner, read the IDL and method documentation, test against a disposable service where possible, and keep the original request so you can use the service's own documented reversal procedure.

7. Validate an interface definition file

validate-idl parses a Varlink interface definition, checks its syntax and internal consistency, and prints the validated definition with syntax highlighting. Give it a file path:

$ varlinkctl validate-idl ./example.varlink
interface example.Service
...

It reads standard input when the file argument is omitted:

$ varlinkctl validate-idl < ./example.varlink
interface example.Service
...

This command does not install, load or publish the interface. A successful parse does not mean a service implements it, so verify the deployed endpoint separately with list-interfaces and introspect.

Common traps

  • Wrong address. Use an existing Unix socket or executable path. An absolute path and the unix: form are both accepted, but they must identify a real endpoint.
  • Incomplete method name. Use the fully qualified name shown by introspection, not just the final method component.
  • Missing JSON. An omitted argument object makes varlinkctl wait for JSON on standard input; pass {} for an empty argument set.
  • Unexpected output formatting. Use --json=short for compact machine output or --json=pretty for deliberate human-readable output, and never parse the pretty spacing.
  • Permission denied. Verify the socket's access policy and service documentation first. Use elevated privileges only when they are expected for that endpoint, and avoid running arbitrary executable services as root.

Done means

  • Versions confirmed. You confirmed the installed systemd and varlinkctl versions.
  • Contract identified. You used info and list-interfaces to identify the endpoint's contract.
  • Interface read first. You used introspect before constructing a method request.
  • Call formed correctly. Your call used a fully qualified method name and valid JSON, or you deliberately used standard input.
  • Risky options respected. You treated --more, --oneway and state-changing methods as contract-sensitive operations.
  • IDL validated separately. You validated any local IDL with validate-idl, knowing that validation alone does not deploy it.