Golang中DBus的使用问题求助

Golang中DBus的使用问题求助 大家好,

首先声明我是Go语言的完全新手。目前我正在尝试创建一个API,通过dbus与远程KMIP服务器通信(这部分功能已经实现)。遗憾的是,除了godbus本身的文档外,找不到其他不包含大量(对我的应用来说不必要的)基础设施或将代码抽象到失去学习价值的示例资源。

我确信这里忽略了一些简单的细节。

我的服务器应用:

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 是服务器对象,目前设置为0且未实际使用
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

// 从输入参数填充结构
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("名称已被占用")
}

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}

// 宣告服务开始运行
conn.Export(s, dbus.ObjectPath(cfg.Bus.Path), cfg.Bus.Iface)
// 为CreateKeyPair服务导出内省信息
conn.Export(introspect.Introspectable(ckpNode), dbus.ObjectPath(cfg.Bus.Path), "org.freedesktop.DBus.Introspectable")
// 注册方法
conn.ExportSubtree(s, dbus.ObjectPath(cfg.Bus.Path), cfg.Bus.Iface)
select {}
}

我的客户端应用:

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("已连接DBus...")

// 由于我们只是客户端,应该只能看到类似浮点数的内容
allOnBus := conn.Names()
for _, name := range allOnBus {
	fmt.Println("返回: " + name)
}

// 检查远程接口
fmt.Println("对 " + cfg.Bus.Iface + " 进行内省调用")

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

fmt.Println("XML名称: " + (node.XMLName.Local))
fmt.Println("名称: " + 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)
// 在打印结构后添加回车符
fmt.Println("")

// 设置预设调用
method := cfg.Bus.Iface + `.servercreatekeypair`
fmt.Println("调用方法 " + 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)
}
}

最终得到的结果(输出最后部分)… panic: org.freedesktop.DBus 无法理解 servercreatekeypair 消息

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

所以我在思考为什么内省将"servercreatekeypair"列为有效方法,但在调用时却无法理解?是否忘记导出某些内容?提前感谢。


更多关于Golang中DBus的使用问题求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

为后人记录解决方案,希望能帮助到其他人。

注意:我从这里得到了需要的提示:https://github.com/hoffoo/spotify-ctrl/blob/master/spotify.go
你需要连接到接口/路径下指定的特定DBus对象!(注意新的 func connDbus 函数。)这在dbus示例中没有明确说明。以下是修订后的客户端应用程序(同时包含增量日志改进)。

我的客户端应用程序

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
// 读取配置。
cfg, err := readconfigs.ReadConfig(configfile)
IFace := cfg.Bus.Iface
Opath := dbus.ObjectPath(cfg.Bus.Path)
pdbus = connDbus(IFace, Opath)
fmt.Println("Joined DBus ...")

// 由于我们只是客户端,应该只看到类似浮点数的内容。
//	allOnBus := conn.Names()
//	for _, name := range allOnBus {
//		fmt.Println("Returned: " + name)
//	}

// 检查远程接口。
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)
}

// 在打印结构后添加回车。
fmt.Println("")

rand.Seed(time.Now().UnixNano())
suid := fmt.Sprintf("%d", rand.Intn(99999))
// 设置预设调用。
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)
}
}

更多关于Golang中DBus的使用问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题在于客户端调用方法时使用了错误的对象路径和方法签名。在DBus中,方法调用需要指定正确的接口、对象路径和方法名。以下是修正后的客户端代码:

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"

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

    // 检查总线上的名称
    allOnBus := conn.Names()
    for _, name := range allOnBus {
        fmt.Println("总线上的名称: " + name)
    }

    // 内省调用
    fmt.Println("对 " + cfg.Bus.Iface + " 进行内省调用")

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

    fmt.Println("XML名称: " + (node.XMLName.Local))
    fmt.Println("名称: " + node.Name)
    for _, inf := range node.Interfaces {
        fmt.Printf("接口: %s\n", inf.Name)
        for _, method := range inf.Methods {
            fmt.Printf("  方法: %s\n", method.Name)
            for _, arg := range method.Args {
                fmt.Printf("    参数: %s %s\n", arg.Direction, arg.Type)
            }
        }
    }

    data, _ := json.MarshalIndent(node, "", "    ")
    os.Stdout.Write(data)
    fmt.Println("")

    // 正确的方法调用方式
    obj := conn.Object(cfg.Bus.Iface, dbus.ObjectPath(cfg.Bus.Path))
    
    // 根据服务器端方法签名调整参数
    var result string
    err = obj.Call(cfg.Bus.Iface+".servercreatekeypair", 0, 
        "RSA",          // ca string
        uint32(2048),   // size uint32  
        "default",      // opn string
        "test15_RSA_Public_Key",  // pubname string
        "VERIFY|DECRYPT",         // pubusage string
        "test15_RSA_Private_Key", // priname string
        "SIGN",                   // priusage string
        false,                    // debug bool
        int32(0)).Store(&result)  // expireTimeout int32

    if err != nil {
        panic(err)
    }
    
    fmt.Printf("调用结果: %s\n", result)
}

服务器端也需要修正内省XML中的方法签名:

ckpNode := `
<node>
    <interface name="` + cfg.Bus.Iface + `">
        <method name="servercreatekeypair">
            <arg direction="in" type="s" name="ca"/>
            <arg direction="in" type="u" name="size"/>
            <arg direction="in" type="s" name="opn"/>
            <arg direction="in" type="s" name="pubname"/>
            <arg direction="in" type="s" name="pubusage"/>
            <arg direction="in" type="s" name="priname"/>
            <arg direction="in" type="s" name="priusage"/>
            <arg direction="in" type="b" name="debug"/>
            <arg direction="in" type="i" name="expireTimeout"/>
            <arg direction="out" type="s" name="output"/>
        </method>
    </interface>` + introspect.IntrospectDataString + `</node> `

主要问题:

  1. 客户端使用了 conn.BusObject().Call() 而不是通过具体的对象调用
  2. 方法参数类型不匹配 - 需要明确指定参数类型(string, uint32, bool, int32)
  3. 内省XML中缺少完整的参数定义

修正后的代码应该能正常工作。

回到顶部