Need some help with DBus

Hi Folks,

Let me start with stating I’m a complete noob at golang. I’m currently attempting to spin up an API to communicate to a remote KMIP server (that part I have working) from a client across the dbus. Unfortunately there’s not a resources with examples outside of godbus itself that doesn’t add a bunch of (unnecessary for my application) infrastructure and/or abstract the code to the point it is no longer good as a learning tool.

I’m sure there is something trivial I’m overlooking here.

My Server App.
package main

import (
"log"

"github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
"local-gitlab.com/gkbassf/go-pykmip-wrapper/kmipcreatekeypair"
"local-gitlab.com/gkbassf/go-pykmip-wrapper/readconfigs"
)

const configfile = "/etc/ndvm/config/kmip-interpreter.conf"

// Server is a server object, for the time being, it is set to 0 and not really used.
type Server struct {
id uint32
}

func (s Server) servercreatekeypair(ca string, size uint32, opn string, pubname string, pubusage string, priname string, priusage string, debug bool, expireTimeout int32) (output string, dbuserr *dbus.Error) {
var Kpps kmipcreatekeypair.KeyPairParams

// Fill in structure from input params.
Kpps.Gca = ca
Kpps.Gsize = size
Kpps.Gopn = opn
Kpps.Gpubname = pubname
Kpps.Gpubuse.Set(pubusage)
Kpps.Gpriname = priname
Kpps.Gpriuse.Set(priusage)
Kpps.Gdebug = debug

out, err := kmipcreatekeypair.CreateKeyPair(Kpps)

if err != nil {
	log.Fatal(err)
}
//	dbuserr = err.(dbus.Error)
return out, dbuserr
}

func main() {
cfg, err := readconfigs.ReadConfig(configfile)
conn, err := dbus.SystemBus()
if err != nil {
	panic(err)
}

reply, err := conn.RequestName(cfg.Bus.Iface, dbus.NameFlagDoNotQueue)
if err != nil {
	panic(err)
}
if reply != dbus.RequestNameReplyPrimaryOwner {
	panic("Name already taken")
}

ckpNode := `
<node>
	<interface name="` + cfg.Bus.Iface + `">
		<method name="servercreatekeypair">
			<arg direction="in" type="v"/>
			<arg direction="out" type="s"/>
		</method>
	</interface>` + introspect.IntrospectDataString + `</node> `

s := Server{id: 0}

// Advertize service is open for business.
conn.Export(s, dbus.ObjectPath(cfg.Bus.Path), cfg.Bus.Iface)
// Export introspection for CreateKeyPair service.
conn.Export(introspect.Introspectable(ckpNode), dbus.ObjectPath(cfg.Bus.Path), "org.freedesktop.DBus.Introspectable")
// Register methods.
conn.ExportSubtree(s, dbus.ObjectPath(cfg.Bus.Path), cfg.Bus.Iface)
select {}
}

My Client App.
package main

import (
"encoding/json"
"fmt"
"os"

"github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
"local-gitlab.com/gkbassf/go-pykmip-wrapper/readconfigs"
)

const configfile = "/etc/ndvm/config/kmip-interpreter.conf"

var list []string

func main() {
cfg, err := readconfigs.ReadConfig(configfile)
conn, err := dbus.SystemBus()
if err != nil {
	panic(err)
}
fmt.Println("Joined DBus ...")

// Since we are only a client, we should only see what looks like a floating point number.
allOnBus := conn.Names()
for _, name := range allOnBus {
	fmt.Println("Returned: " + name)
}

// Examine our remote interface.
fmt.Println("Call introspection on " + cfg.Bus.Iface)

node, err := introspect.Call(conn.Object(cfg.Bus.Iface, dbus.ObjectPath(cfg.Bus.Path)))
if err != nil {
	panic(err)
}

fmt.Println("XMLNAME: " + (node.XMLName.Local))
fmt.Println("NAME: " + node.Name)
for _, inf := range node.Interfaces {
	fmt.Println(inf)
}

for _, child := range node.Children {
	fmt.Println(child)
}

data, _ := json.MarshalIndent(node, "", "    ")
os.Stdout.Write(data)
// Add a carrage return after printing structure.
fmt.Println("")

// Set up canned call.
method := cfg.Bus.Iface + `.servercreatekeypair`
fmt.Println("Calling method " + method)

err = conn.BusObject().Call(method, 0, `RSA`, 2048, `default`, `test15_RSA_Public_Key`, `VERIFY|DECRYPT`, `test15_RSA_Private_Key`, `SIGN`, false).Store(&list)
if err != nil {
	panic(err)
}
for _, v := range list {
	fmt.Println(v)
}
}

What I end up with (last portion of output) …
panic: org.freedesktop.DBus does not understand message servercreatekeypair

goroutine 1 [running]:
main.main()
/home/kbassford/go/src/local-gitlab.com/gkbassf/go-pykmip-wrapper/test/testdbusclient/testdbusclient.go:76 +0xd1a

So I’m trying to figure out why introspection lists “servercreatekeypair” as a valid method, but can’t understand when I call it? Did I forget to export something? Thanks in advance.

Posting solution for posterity in the hope that it may help someone else.

Note: I got the hint in needed from here: https://github.com/hoffoo/spotify-ctrl/blob/master/spotify.go
You need to connect to the specific DBus object specified under the interface/path! (Note the new func connDbus function.) This is not spelled out in the dbus examples. Below is the revised Client App. (with incremental logging improvements as well).

My Client App
package main

import (
"encoding/json"
"fmt"
"log"
"math/rand"
"time"

"github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
"local-gitlab.com/gkbassf/go-pykmip-wrapper/readconfigs"
)

const configfile = "/etc/ndvm/config/kmip-interpreter.conf"

var list []string

func connDbus(Iface string, ObjPath dbus.ObjectPath) *dbus.Object {
conn, err := dbus.SystemBus()
if err != nil {
	log.Panic("ERROR: ", err)
}
return conn.Object(Iface, ObjPath).(*dbus.Object)
}

func main() {
var pdbus *dbus.Object
// Read our config.
cfg, err := readconfigs.ReadConfig(configfile)
IFace := cfg.Bus.Iface
Opath := dbus.ObjectPath(cfg.Bus.Path)
pdbus = connDbus(IFace, Opath)
fmt.Println("Joined DBus ...")

// Since we are only a client, we should only see what looks like a floating point number.
//	allOnBus := conn.Names()
//	for _, name := range allOnBus {
//		fmt.Println("Returned: " + name)
//	}

// Examine our remote interface.
fmt.Println("Call introspection on " + cfg.Bus.Iface)

node, err := introspect.Call(pdbus)
if err != nil {
	panic(err)
}

fmt.Println("XMLNAME: " + (node.XMLName.Local))
fmt.Println("NAME: " + node.Name)
for _, inf := range node.Interfaces {
	fmt.Println("INTERFACES: ", inf)
}

for _, child := range node.Children {
	fmt.Println("CHILDREN: ", child)
}

data, err := json.MarshalIndent(node, "", "    ")
if err == nil {
	fmt.Println(string(data))
	fmt.Println("")
} else {
	fmt.Println("introspection failed ", err)
}

// Add a carrage return after printing structure.
fmt.Println("")

rand.Seed(time.Now().UnixNano())
suid := fmt.Sprintf("%d", rand.Intn(99999))
// Set up canned call.
method := `server_create_keypair`
fmt.Println("Calling method: ", method)

//	kwargs := make(map[string]interface{})
kwargs := make(map[string]string)
kwargs["ca"] = "RSA"
kwargs["size"] = "2048"
kwargs["opn"] = "default"
kwargs["pubname"] = suid + "_RSA_Public_Key"
kwargs["pubusage"] = "VERIFY|DECRYPT"
kwargs["priname"] = suid + "_RSA_Private_Key"
kwargs["priusage"] = "SIGN"
kwargs["debug"] = "true"

fmt.Println("kwargs: ", kwargs)
paramlines, err := json.MarshalIndent(kwargs, "", "    ")
if err == nil {
	fmt.Println(string(paramlines))
	fmt.Println("")
} else {
	fmt.Println("cannot print parameters ", err)
}

err = pdbus.Call(method, 0, kwargs).Store(&list)
if err != nil {
	log.Println(err)
}
for _, v := range list {
	log.Println(v)
}
}