Adding varlink to DNF
This page shows, how to easily add a varlink interface to existing software.
Prerequisites
Checkout the dnf git repo with branch varlink:
$ git clone -b varlink https://github.com/haraldh/dnf.git
$ cd dnf
$ cmake . -DPYTHON_DESIRED:str=3 -DWITH_MAN=0 -DPYTHON_EXECUTABLE=/usr/bin/python3
$ make
For Fedora we also need
$ sudo dnf install --enablerepo rawhide python3-varlink libvarlink-util
Interface definition
First, the interface definition has to be specified. With compatibility in mind, we define a general purpose package manager interface: com.redhat.packages.varlink.
Implementation
The implementation is done in main.py
First a varlink.Service object is created:
service = varlink.Service(
vendor='Red Hat',
product='Packages',
version='1',
interface_dir=os.path.dirname(__file__),
namespaced=True
)
The namespaced=True indicates, that the methods are called with arguments of type SimpleNamespace and not dict, so that we can access the subelements with parm.foo instead of parm["foo"].
The DnfVarlinkService class is tagged with the @service.interface('com.redhat.packages') annotator, to indicate, that it is the interface provider for the varlink service com.redhat.packages. The varlink.Service will load the interface definition file com.redhat.packages.varlink from the directory specified with interface_dir.
For our showcase, we implement only the List method. First we create the search_pattern according to the packages parameter. After querying the packages with base._do_package_lists(), the returned lists have to be processed to fit the Package type specified in the varlink com.redhat.packages interface.
The varlink.ThreadingServer class, which is a subclass of socketserver.BaseServer is used to serve from the URL, which is passed in argv[1] (for dnf args[0]). varlink.ThreadingServer, varlink.ForkingServer and varlink.Server parse the varlink URI address, check for socket activation (LISTEN_FD) and bind a socket for us.
main() is called from the dnf-varlink cli utility following dnf-automatic and dnf-cli in the bin directory.
Running
Now we can talk to dnf with varlink. We use the “exec:” mechanism, which will start the dnf-varlink-3 client executable directly in the user context with all the user’s permissions.
$ varlink call exec:./bin/dnf-varlink-3/com.redhat.packages.List '{"packages": [{"name":"libvarlink", "version": {"architecture": "x86_64" }}, {"name": "python3-varlink"}]}'
Last metadata expiration check: 0:04:35 ago on Mi 21 Feb 2018 10:02:42 CET.
{
"packages": [
{
"available": false,
"installed": true,
"name": "libvarlink",
"version": {
"architecture": "x86_64",
"release": "2.fc28",
"version": "1"
}
},
{
"available": true,
"installed": true,
"name": "python3-varlink",
"version": {
"architecture": "noarch",
"release": "1.git.137.51a669b.fc28",
"version": "12"
}
},
{
"available": true,
"installed": false,
"name": "libvarlink",
"version": {
"architecture": "x86_64",
"release": "1.git.224.bd4cd60.fc28",
"version": "2"
}
}
]
}
The dnf-varlink-3 executable can also be started in server mode
$ ./bin/dnf-varlink-3 --varlink=unix:@com.redhat.packages
And a client can talk to it via the unix socket
$ varlink call unix:@com.redhat.packages/com.redhat.packages.List '{"packages": [{"name":"libvarlink", "version": {"architecture": "x86_64" }}, {"name": "python3-varlink"}]}'
{
"packages": [
{
"available": false,
"installed": true,
"name": "libvarlink",
"version": {
"architecture": "x86_64",
"release": "2.fc28",
"version": "1"
}
},
{
"available": true,
"installed": true,
"name": "python3-varlink",
"version": {
"architecture": "noarch",
"release": "1.git.137.51a669b.fc28",
"version": "12"
}
},
{
"available": true,
"installed": false,
"name": "libvarlink",
"version": {
"architecture": "x86_64",
"release": "1.git.224.bd4cd60.fc28",
"version": "2"
}
}
]
}
Other transport mechanisms can be specified via the varlink address. varlink.SimpleServer supports exec:, unix: and tcp:.