diff --git a/Makefile b/Makefile index 9eda553..e8c547a 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ bindir = $(exec_prefix)/bin PKGBASE := github.com/openxt/openxt-go PKGS := argo db ioctl -CMDS := argo-nc dbdcmd dbus-send +CMDS := argo-nc db-cmd dbus-send VERSION := 0.1.0 # FIPS is not available until Go 1.24 diff --git a/cmd/db-cmd/main.go b/cmd/db-cmd/main.go new file mode 100644 index 0000000..7f238f4 --- /dev/null +++ b/cmd/db-cmd/main.go @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2026 Apertus Soutions, LLC +// + +package main + +import ( + "encoding/binary" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/godbus/dbus/v5" + argoDbus "github.com/openxt/openxt-go/argo/dbus" + "github.com/openxt/openxt-go/db" + flag "github.com/spf13/pflag" +) + +func die(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, format, a...) + fmt.Fprintln(os.Stderr) + os.Exit(1) +} + +func usage() { + fmt.Fprintf(os.Stderr, "Usage of %s []:\n", os.Args[0]) + flag.PrintDefaults() + die(` +Available commands are: + cat Dump raw value for + exists Check if exist + ls List tree start at + nodes List immediate childtren of + read Retrieve string value for + rm Delete + write Store for `) +} + +func list(c *db.DbClient, fullPath bool, indent int, path string) (string, error) { + path = strings.TrimRight(path, db.PathDelimiter) + result, err := c.List(path) + if err != nil { + return "", err + } + + var key string + if fullPath { + key = path + } else { + key = strings.Repeat(" ", indent) + if path != "" { + key += filepath.Base(path) + } + } + + if len(result) == 0 { + value, err := c.Read(path) + if err != nil { + return "", fmt.Errorf("failed reading %s: %v\n", path, err) + } + return fmt.Sprintf("%s = \"%s\"", key, value), nil + } + + out := key + " =" + for _, elem := range result { + r, err := list(c, fullPath, indent+1, path+"/"+elem) + if err != nil { + return "", err + } + + out += "\n" + r + } + + return out, nil +} + +func main() { + var conn *dbus.Conn + + helpFlag := flag.BoolP("help", "h", false, "Print help") + fullPathFlag := flag.BoolP("full", "f", false, "Full path") + platBusFlag := flag.BoolP("platform", "p", false, "Connect to the platform bus") + flag.CommandLine.MarkHidden("full") + flag.Parse() + + if *helpFlag { + usage() + } + + if *platBusFlag { + var err error + conn, err = argoDbus.ConnectPlatformBus() + if err != nil { + die("Error connecting to platform bus: %v\n", err) + } + } else { + var err error + conn, err = dbus.SystemBus() + if err != nil { + die("Error connecting to system bus: %v\n", err) + } + } + defer conn.Close() + + args := flag.Args() + if len(args) < 1 { + usage() + } + + client := db.NewDbClient(conn, db.DbServiceName, "/") + + operation := args[0] + + args = args[1:] + arglen := len(args) + + switch operation { + case "cat": + if arglen != 1 { + fmt.Fprintf(os.Stderr, + "Error: incorrect number of arguments.\n") + usage() + } + result, err := client.ReadBinary(args[1]) + if err != nil { + die("DB read binary error: %v", err) + } + binary.Write(os.Stdout, binary.LittleEndian, result) + + case "exists": + if arglen != 1 { + fmt.Fprintf(os.Stderr, + "Error: incorrect number of arguments.\n") + usage() + } + result, err := client.Exists(os.Args[0]) + if err != nil { + die("DB exists error: %v", err) + } + fmt.Printf("%t", result) + case "ls": + path := "/" + if len(args) != 0 { + path = args[0] + } + result, err := list(client, *fullPathFlag, 0, path) + if err != nil { + die("DB list error: %v", err) + } + fmt.Printf("%s\n", result) + + case "nodes": + if arglen != 1 { + fmt.Fprintf(os.Stderr, + "Error: incorrect number of arguments.\n") + usage() + } + result, err := client.List(args[0]) + if err != nil { + die("DB read error: %v", err) + } + fmt.Printf("%s\n", strings.Join(result, " ")) + case "read": + if arglen != 1 { + fmt.Fprintf(os.Stderr, + "Error: incorrect number of arguments.\n") + usage() + } + result, err := client.Read(args[0]) + if err != nil { + die("DB read error: %v", err) + } + fmt.Printf("%s\n", result) + case "rm": + if arglen != 1 { + fmt.Fprintf(os.Stderr, + "Error: incorrect number of arguments.\n") + usage() + } + err := client.Rm(args[0]) + if err != nil { + die("DB rm error: %v", err) + } + case "write": + if arglen != 2 { + fmt.Fprintf(os.Stderr, + "Error: incorrect number of arguments.\n") + usage() + } + err := client.Write(args[0], args[1]) + if err != nil { + die("DB write error: %v", err) + } + default: + usage() + } +} diff --git a/cmd/dbdcmd/main.go b/cmd/dbdcmd/main.go deleted file mode 100644 index 5f206e7..0000000 --- a/cmd/dbdcmd/main.go +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright 2026 Apertus Soutions, LLC -// - -package main - -import ( - "fmt" - "os" - - "github.com/openxt/openxt-go/db" -) - -func die(format string, a ...interface{}) { - fmt.Fprintf(os.Stderr, format, a...) - fmt.Fprintln(os.Stderr) - os.Exit(1) -} - -func usage() { - die( - `Usage: dbcmd [] - -Available commands are: - read Retrieve from db - write Store for in the db - rm Delete from db - exists Check if exist in the db - help Print this help`) -} - -func main() { - arglen := len(os.Args) - if arglen < 2 { - usage() - } - - db, err := dbd.NewClient() - - if err != nil { - die("DB connection error: %v", err) - } - - operation := os.Args[1] - - switch operation { - case "read": - if arglen != 3 { - fmt.Fprintf(os.Stderr, - "Error: incorrect number of arguments.\n") - usage() - } - result, err := db.Read(os.Args[2]) - if err != nil { - die("DB read error: %v", err) - } - fmt.Println(os.Stdout, "%s", result) - case "write": - if arglen != 4 { - fmt.Fprintf(os.Stderr, - "Error: incorrect number of arguments.\n") - usage() - } - err := db.Write(os.Args[2], os.Args[3]) - if err != nil { - die("DB write error: %v", err) - } - case "rm": - if arglen != 3 { - fmt.Fprintf(os.Stderr, - "Error: incorrect number of arguments.\n") - usage() - } - err := db.Rm(os.Args[2]) - if err != nil { - die("DB rm error: %v", err) - } - case "exists": - if arglen != 3 { - fmt.Fprintf(os.Stderr, - "Error: incorrect number of arguments.\n") - usage() - } - result, err := db.Exists(os.Args[2]) - if err != nil { - die("DB exists error: %v", err) - } - fmt.Println(os.Stdout, "%t", result) - default: - usage() - } -} diff --git a/db/client.go b/db/client.go new file mode 100644 index 0000000..c173a83 --- /dev/null +++ b/db/client.go @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2026 Apertus Soutions, LLC +// + +package db + +import ( + "github.com/godbus/dbus/v5" +) + +const DbServiceName = "com.citrix.xenclient.db" + +type DbClient struct { + dbus.BusObject +} + +func NewDbClient(conn *dbus.Conn, dest, path string) *DbClient { + return &DbClient{conn.Object(dest, dbus.ObjectPath(path))} +} + +/* Interface com.citrix.xenclient.db */ +func (d *DbClient) Dump(path string) (value string, err error) { + + err = d.Call("com.citrix.xenclient.db.dump", 0, path).Store(&value) + + return +} + +func (d *DbClient) Exists(path string) (ex bool, err error) { + + err = d.Call("com.citrix.xenclient.db.exists", 0, path).Store(&ex) + + return +} + +func (d *DbClient) Inject(path string, value string) error { + + call := d.Call("com.citrix.xenclient.db.inject", 0, path, value) + + return call.Err +} + +func (d *DbClient) List(path string) (value []string, err error) { + + err = d.Call("com.citrix.xenclient.db.list", 0, path).Store(&value) + + return +} + +func (d *DbClient) Read(path string) (value string, err error) { + + err = d.Call("com.citrix.xenclient.db.read", 0, path).Store(&value) + + return +} + +func (d *DbClient) ReadBinary(path string) (value []byte, err error) { + + err = d.Call("com.citrix.xenclient.db.read_binary", 0, path).Store(&value) + + return +} + +func (d *DbClient) Rm(path string) error { + + call := d.Call("com.citrix.xenclient.db.rm", 0, path) + + return call.Err +} + +func (d *DbClient) Write(path string, value string) error { + + call := d.Call("com.citrix.xenclient.db.write", 0, path, value) + + return call.Err +} diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..74091b1 --- /dev/null +++ b/db/db.go @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright 2026 Apertus Soutions, LLC +// + +/* +Package db implements an client and server for OpenXT db. +*/ +package db + +// PathDelimiter allows specifying the delimiter used for path element +// separation. +var PathDelimiter string = "/" diff --git a/db/dbd.go b/db/dbd.go deleted file mode 100644 index c0bdb46..0000000 --- a/db/dbd.go +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// -// Copyright 2026 Apertus Soutions, LLC -// - - -package dbd - -import ( - "github.com/godbus/dbus/v5" -) - -type Client interface { - Read(path string) (string, error) - ReadBinary(path string) ([]byte, error) - Write(path string, value string) error - Dump(path string) (string, error) - Inject(path string, value string) error - List(path string) ([]string, error) - Rm(path string) error - Exists(path string) (bool, error) -} - -type Dbd struct { - conn *dbus.Conn -} - -func NewClient() (Client, error) { - conn, err := dbus.SystemBus() - - if err != nil { - return nil, err - } - return &Dbd{ - conn: conn, - }, nil -} - -// -// -// -// -func (c *Dbd) Read(path string) (string, error) { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - var s string - err := obj.Call("com.citrix.xenclient.db.read", 0, path).Store(&s) - - return s, err -} - -// -// -// -// -func (c *Dbd) ReadBinary(path string) ([]byte, error) { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - var b []byte - err := obj.Call("com.citrix.xenclient.db.read_binary", 0, path).Store(&b) - - return b, err -} - -// -// -// -// -func (c *Dbd) Write(path string, value string) error { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - call := obj.Call("com.citrix.xenclient.db.write", 0, path, value) - - return call.Err -} - -// -// -// -// -func (c *Dbd) Dump(path string) (string, error) { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - var s string - err := obj.Call("com.citrix.xenclient.db.dump", 0, path).Store(&s) - - return s, err -} - -// -// -// -// -func (c *Dbd) Inject(path string, value string) error { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - call := obj.Call("com.citrix.xenclient.db.inject", 0, path, value) - - return call.Err -} - -// -// -// -// -func (c *Dbd) List(path string) ([]string, error) { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - var s []string - err := obj.Call("com.citrix.xenclient.db.list", 0, path).Store(&s) - - return s, err -} - -// -// -// -func (c *Dbd) Rm(path string) error { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - call := obj.Call("com.citrix.xenclient.db.rm", 0, path) - - return call.Err -} - -// -// -// -// -func (c *Dbd) Exists(path string) (bool, error) { - obj := c.conn.Object("com.citrix.xenclient.db", "/") - - var b bool - err := obj.Call("com.citrix.xenclient.db.read", 0, path).Store(&b) - - return b, err -} diff --git a/go.mod b/go.mod index 9de977f..19788df 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/openxt/openxt-go go 1.12 require ( - github.com/godbus/dbus/v5 v5.0.3 + github.com/godbus/dbus/v5 v5.1.0 github.com/spf13/pflag v1.0.5 ) diff --git a/go.sum b/go.sum index 9ae06e1..74d827f 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ -github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=