diff --git a/hostname1/dbus.go b/hostname1/dbus.go new file mode 100644 index 0000000..e78d730 --- /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 http://www.freedesktop.org/wiki/Software/systemd/hostnamed/ +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(".SetHostname", 0, hostname, interactive).Err +} + +func (c *Conn) SetStaticHostname(hostname string, interactive bool) error { + return c.object.Call(".SetStaticHostname", 0, hostname, interactive).Err +} + +func (c *Conn) SetPrettyHostname(hostname string, interactive bool) error { + return c.object.Call(".SetPrettyHostname", 0, hostname, interactive).Err +} + +func (c *Conn) SetIconName(iconName string, interactive bool) error { + return c.object.Call(".SetIconName", 0, iconName, interactive).Err +} + +func (c *Conn) SetChassis(chassis string, interactive bool) error { + return c.object.Call(".SetChassis", 0, chassis, interactive).Err +} + +func (c *Conn) SetDeployment(deployment string, interactive bool) error { + return c.object.Call(".SetDeployment", 0, deployment, interactive).Err +} + +func (c *Conn) SetLocation(location string, interactive bool) error { + return c.object.Call(".SetLocation", 0, location, interactive).Err +} + +func (c *Conn) GetProductUUID(interactive bool) ([]byte, error) { + var uuid []byte + err := c.object.Call(".GetProductUUID", 0, interactive).Store(&uuid) + return uuid, err +} + +func (c *Conn) GetHardwareSerial() (string, error) { + var serial string + err := c.object.Call(".GetHardwareSerial", 0).Store(&serial) + return serial, err +} + +func (c *Conn) Describe() (string, error) { + var description string + err := c.object.Call(".Describe", 0).Store(&description) + return description, err +} + +func (c *Conn) Hostname() (string, error) { + out, err := c.object.GetProperty(".Hostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) StaticHostname() (string, error) { + out, err := c.object.GetProperty(".StaticHostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) PrettyHostname() (string, error) { + out, err := c.object.GetProperty(".PrettyHostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) DefaultHostname() (string, error) { + out, err := c.object.GetProperty(".DefaultHostname") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) HostnameSource() (string, error) { + out, err := c.object.GetProperty(".HostnameSource") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) IconName() (string, error) { + out, err := c.object.GetProperty(".IconName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) Chassis() (string, error) { + out, err := c.object.GetProperty(".Chassis") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) Deployment() (string, error) { + out, err := c.object.GetProperty(".Deployment") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) Location() (string, error) { + out, err := c.object.GetProperty(".Location") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) KernelName() (string, error) { + out, err := c.object.GetProperty(".KernelName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) KernelRelease() (string, error) { + out, err := c.object.GetProperty(".KernelRelease") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) KernelVersion() (string, error) { + out, err := c.object.GetProperty(".KernelVersion") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) OperatingSystemPrettyName() (string, error) { + out, err := c.object.GetProperty(".OperatingSystemPrettyName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) OperatingSystemCPEName() (string, error) { + out, err := c.object.GetProperty(".OperatingSystemCPEName") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) OperatingSystemSupportEnd() (uint64, error) { + out, err := c.object.GetProperty(".OperatingSystemSupportEnd") + if err != nil { + return 0, err + } + return out.Value().(uint64), nil +} + +func (c *Conn) HomeURL() (string, error) { + out, err := c.object.GetProperty(".HomeURL") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) HardwareVendor() (string, error) { + out, err := c.object.GetProperty(".HardwareVendor") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) HardwareModel() (string, error) { + out, err := c.object.GetProperty(".HardwareModel") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) FirmwareVersion() (string, error) { + out, err := c.object.GetProperty(".FirmwareVersion") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) FirmwareVendor() (string, error) { + out, err := c.object.GetProperty(".FirmwareVendor") + if err != nil { + return "", err + } + return out.Value().(string), nil +} + +func (c *Conn) FirmwareDate() (uint64, error) { + out, err := c.object.GetProperty(".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..58f9cc7 --- /dev/null +++ b/hostname1/dbus_test.go @@ -0,0 +1,192 @@ +// 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 ( + "context" + "fmt" + "os/user" + "regexp" + "testing" + "time" +) + +// TestNew ensures that New() works without errors. +func TestNew(t *testing.T) { + _, err := New() + if err != nil { + t.Fatal(err) + } +} + +func TestListSessions(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + sessions, err := c.ListSessions() + if err != nil { + t.Fatal(err) + } + + if len(sessions) > 0 { + for _, s := range sessions { + lookup, err := user.Lookup(s.User) + if err != nil { + t.Fatal(err) + } + if fmt.Sprint(s.UID) != lookup.Uid { + t.Fatalf("expected uid '%d' but got '%s'", s.UID, lookup.Uid) + } + + validPath := regexp.MustCompile(`/org/freedesktop/login1/session/_[0-9]+`) + if !validPath.MatchString(fmt.Sprint(s.Path)) { + t.Fatalf("invalid session path: %s", s.Path) + } + } + } +} + +func TestListUsers(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + users, err := c.ListUsers() + if err != nil { + t.Fatal(err) + } + + if len(users) > 0 { + for _, u := range users { + lookup, err := user.Lookup(u.Name) + if err != nil { + t.Fatal(err) + } + if fmt.Sprint(u.UID) != lookup.Uid { + t.Fatalf("expected uid '%d' but got '%s'", u.UID, lookup.Uid) + } + + validPath := regexp.MustCompile(`/org/freedesktop/login1/user/_[0-9]+`) + if !validPath.MatchString(fmt.Sprint(u.Path)) { + t.Fatalf("invalid user path: %s", u.Path) + } + } + } +} + +func TestConn_GetSessionPropertiesContext(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + sessions, err := c.ListSessions() + if err != nil { + t.Fatal(err) + } + + for _, s := range sessions { + func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + + props, err := c.GetSessionPropertiesContext(ctx, s.Path) + if err != nil { + t.Fatal(err) + } + if len(props) == 0 { + t.Fatal("no properties returned") + } + }() + } +} + +func TestConn_GetSessionPropertyContext(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + sessions, err := c.ListSessions() + if err != nil { + t.Fatal(err) + } + + for _, s := range sessions { + func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + + _, err := c.GetSessionPropertyContext(ctx, s.Path, "Remote") + if err != nil { + t.Fatal(err) + } + }() + } +} + +func TestConn_GetUserPropertiesContext(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + users, err := c.ListUsers() + if err != nil { + t.Fatal(err) + } + + for _, u := range users { + func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + + props, err := c.GetUserPropertiesContext(ctx, u.Path) + if err != nil { + t.Fatal(err) + } + if len(props) == 0 { + t.Fatal("no properties returned") + } + }() + } +} + +func TestConn_GetUserPropertyContext(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + users, err := c.ListUsers() + if err != nil { + t.Fatal(err) + } + + for _, u := range users { + func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + + _, err := c.GetUserPropertyContext(ctx, u.Path, "State") + if err != nil { + t.Fatal(err) + } + }() + } +}