From 0ea7d74c89e5d25480e2eff4018989721561be22 Mon Sep 17 00:00:00 2001 From: Ananth Bhaskararaman Date: Sun, 28 Apr 2024 03:02:04 +0530 Subject: [PATCH] Hostname1 api Add support for the systemd-hostnamed D-Bus interface. See: https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.hostname1.html Signed-off-by: Ananth Bhaskararaman --- hostname1/dbus.go | 302 +++++++++++++++++++++++++++++++++++++++++ hostname1/dbus_test.go | 76 +++++++++++ scripts/ci-runner.sh | 12 +- 3 files changed, 382 insertions(+), 8 deletions(-) create mode 100644 hostname1/dbus.go create mode 100644 hostname1/dbus_test.go diff --git a/hostname1/dbus.go b/hostname1/dbus.go new file mode 100644 index 0000000..0207296 --- /dev/null +++ b/hostname1/dbus.go @@ -0,0 +1,302 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hostname1 provides integration with the systemd hostnamed API. +// See https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.hostname1.html +package hostname1 + +import ( + "os" + "strconv" + + "github.com/godbus/dbus/v5" +) + +const ( + dbusDest = "org.freedesktop.hostname1" + dbusPath = "/org/freedesktop/hostname1" +) + +// Conn is a connection to systemds dbus endpoint. +type Conn struct { + conn *dbus.Conn + object dbus.BusObject +} + +// New establishes a connection to the system bus and authenticates. +func New() (*Conn, error) { + c := new(Conn) + + if err := c.initConnection(); err != nil { + return nil, err + } + + return c, nil +} + +// Close closes the dbus connection +func (c *Conn) Close() { + if c == nil { + return + } + + if c.conn != nil { + c.conn.Close() + } +} + +// Connected returns whether conn is connected +func (c *Conn) Connected() bool { + return c.conn.Connected() +} + +func (c *Conn) initConnection() error { + var err error + if c.conn, err = dbus.SystemBusPrivate(); err != nil { + return err + } + + // Only use EXTERNAL method, and hardcode the uid (not username) + // to avoid a username lookup (which requires a dynamically linked + // libc) + methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} + + if err := c.conn.Auth(methods); err != nil { + c.conn.Close() + return err + } + + if err := c.conn.Hello(); err != nil { + c.conn.Close() + return err + } + + c.object = c.conn.Object(dbusDest, dbus.ObjectPath(dbusPath)) + + return nil +} + +func (c *Conn) SetHostname(hostname string, interactive bool) error { + return c.object.Call(dbusDest+".SetHostname", 0, hostname, interactive).Err +} + +func (c *Conn) SetStaticHostname(hostname string, interactive bool) error { + return c.object.Call(dbusDest+".SetStaticHostname", 0, hostname, interactive).Err +} + +func (c *Conn) SetPrettyHostname(hostname string, interactive bool) error { + return c.object.Call(dbusDest+".SetPrettyHostname", 0, hostname, interactive).Err +} + +func (c *Conn) SetIconName(iconName string, interactive bool) error { + return c.object.Call(dbusDest+".SetIconName", 0, iconName, interactive).Err +} + +func (c *Conn) SetChassis(chassis string, interactive bool) error { + return c.object.Call(dbusDest+".SetChassis", 0, chassis, interactive).Err +} + +func (c *Conn) SetDeployment(deployment string, interactive bool) error { + return c.object.Call(dbusDest+".SetDeployment", 0, deployment, interactive).Err +} + +func (c *Conn) SetLocation(location string, interactive bool) error { + return c.object.Call(dbusDest+".SetLocation", 0, location, interactive).Err +} + +func (c *Conn) GetProductUUID(interactive bool) ([]byte, error) { + var uuid []byte + err := c.object.Call(dbusDest+".GetProductUUID", 0, interactive).Store(&uuid) + return uuid, err +} + +func (c *Conn) GetHardwareSerial() (string, error) { + var serial string + err := c.object.Call(dbusDest+".GetHardwareSerial", 0).Store(&serial) + return serial, err +} + +func (c *Conn) Describe() (string, error) { + var description string + err := c.object.Call(dbusDest+".Describe", 0).Store(&description) + return description, err +} + +func (c *Conn) Hostname() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".Hostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) StaticHostname() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".StaticHostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) PrettyHostname() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".PrettyHostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) DefaultHostname() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".DefaultHostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) HostnameSource() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".HostnameSource") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) IconName() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".IconName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) Chassis() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".Chassis") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) Deployment() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".Deployment") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) Location() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".Location") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) KernelName() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".KernelName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) KernelRelease() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".KernelRelease") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) KernelVersion() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".KernelVersion") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) OperatingSystemPrettyName() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".OperatingSystemPrettyName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) OperatingSystemCPEName() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".OperatingSystemCPEName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) OperatingSystemSupportEnd() (uint64, error) { + out, err := c.object.GetProperty(dbusDest + ".OperatingSystemSupportEnd") + if err != nil { + return 0, err + } + return out.Value().(uint64), nil +} + +func (c *Conn) HomeURL() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".HomeURL") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) HardwareVendor() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".HardwareVendor") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) HardwareModel() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".HardwareModel") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) FirmwareVersion() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".FirmwareVersion") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) FirmwareVendor() (string, error) { + out, err := c.object.GetProperty(dbusDest + ".FirmwareVendor") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) FirmwareDate() (uint64, error) { + out, err := c.object.GetProperty(dbusDest + ".FirmwareDate") + if err != nil { + return 0, err + } + return out.Value().(uint64), nil +} diff --git a/hostname1/dbus_test.go b/hostname1/dbus_test.go new file mode 100644 index 0000000..63d6dcb --- /dev/null +++ b/hostname1/dbus_test.go @@ -0,0 +1,76 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package hostname1 + +import ( + "io" + "os" + "os/exec" + "strings" + "testing" +) + +// TestNew ensures that New() works without errors. +func TestNew(t *testing.T) { + if _, err := New(); err != nil { + t.Fatal(err) + } +} + +// TestHostname ensures that the Hostname() method returns the system hostname. +func TestHostname(t *testing.T) { + expectedHostname, err := exec.Command("hostname").CombinedOutput() + if err != nil { + t.Fatal(err) + } + + h, err := New() + if err != nil { + t.Fatal(err) + } + + if hostname, err := h.Hostname(); err != nil { + t.Fatal(err) + } else if hostname != strings.TrimSuffix(string(expectedHostname), "\n") { + t.Fatalf("expected %q, got %q", expectedHostname, hostname) + } +} + +func TestStaticHostname(t *testing.T) { + hostnameFile, err := os.Open("/etc/hostname") + if err != nil { + t.Fatal(err) + } + defer hostnameFile.Close() + + expectedHostnameBytes := make([]byte, 256) + n, err := hostnameFile.Read(expectedHostnameBytes) + if err != nil && err != io.EOF { + t.Fatal(err) + } + // Close the file so that hostnamed can use it. + hostnameFile.Close() + expectedHostname := strings.TrimSuffix(string(expectedHostnameBytes[:n]), "\n") + + h, err := New() + if err != nil { + t.Fatal(err) + } + + if hostname, err := h.StaticHostname(); err != nil { + t.Fatal(err) + } else if hostname != expectedHostname { + t.Fatalf("expected %q, got %q", expectedHostname, hostname) + } +} diff --git a/scripts/ci-runner.sh b/scripts/ci-runner.sh index 860dcd8..7d3018e 100755 --- a/scripts/ci-runner.sh +++ b/scripts/ci-runner.sh @@ -2,11 +2,7 @@ set -e set -o pipefail -PROJ="go-systemd" -ORG_PATH="github.com/coreos" -REPO_PATH="${ORG_PATH}/${PROJ}" - -PACKAGES="activation daemon dbus internal/dlopen journal login1 machine1 sdjournal unit util import1" +PACKAGES="activation daemon dbus hostname1 internal/dlopen journal login1 machine1 sdjournal unit util import1" EXAMPLES="activation listen udpconn" function build_source { @@ -17,11 +13,11 @@ function build_tests { rm -rf ./test_bins ; mkdir -p ./test_bins for pkg in ${PACKAGES}; do echo " - ${pkg}" - go test -c -o ./test_bins/${pkg}.test ./${pkg} + go test -c -o "./test_bins/${pkg}.test" "./${pkg}" done for ex in ${EXAMPLES}; do echo " - examples/${ex}" - go build -o ./test_bins/${ex}.example ./examples/activation/${ex}.go + go build -o "./test_bins/${ex}.example" "./examples/activation/${ex}.go" done # just to make sure it's buildable go build -o ./test_bins/journal ./examples/journal/ @@ -32,7 +28,7 @@ function run_tests { sudo -v for pkg in ${PACKAGES}; do echo " - ${pkg}" - sudo -E ./${pkg}.test -test.v + sudo -E "./${pkg}.test" -test.v done popd sudo rm -rf ./test_bins