From 0c56571fd6a41e984c6def6905d3ae8d4c300ca0 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 --- hostname1/dbus.go | 302 +++++++++++++++++++++++++++++++++++++++++ hostname1/dbus_test.go | 81 +++++++++++ scripts/ci-runner.sh | 12 +- 3 files changed, 387 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..73dbd6a --- /dev/null +++ b/hostname1/dbus_test.go @@ -0,0 +1,81 @@ +// 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 ( + "crypto/rand" + "encoding/hex" + "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 TestSetHostname(t *testing.T) { + originalHostname, err := exec.Command("hostname").CombinedOutput() + if err != nil { + t.Fatal(err) + } + + var randomBytes [5]byte + if _, err := rand.Read(randomBytes[:]); err != nil { + t.Fatal(err) + } + randomHostname := "newhostname" + hex.EncodeToString(randomBytes[:]) + + h, err := New() + if err != nil { + t.Fatal(err) + } + + if err := h.SetHostname(randomHostname, false); err != nil { + t.Fatal(err) + } + defer func() { + if err := h.SetHostname(string(originalHostname), false); err != nil { + t.Fatal(err) + } + }() + + if hostname, err := h.Hostname(); err != nil { + t.Fatal(err) + } else if hostname != randomHostname { + t.Fatalf("expected %s, got %s", randomHostname, 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