Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

login1: add methods to get session/user properties #409

Merged
merged 1 commit into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 68 additions & 14 deletions login1/dbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import (
)

const (
dbusDest = "org.freedesktop.login1"
dbusInterface = "org.freedesktop.login1.Manager"
dbusPath = "/org/freedesktop/login1"
dbusDest = "org.freedesktop.login1"
dbusManagerInterface = "org.freedesktop.login1.Manager"
dbusSessionInterface = "org.freedesktop.login1.Session"
dbusUserInterface = "org.freedesktop.login1.User"
dbusPath = "/org/freedesktop/login1"
)

// Conn is a connection to systemds dbus endpoint.
Expand Down Expand Up @@ -166,7 +168,7 @@ func userFromInterfaces(user []interface{}) (*User, error) {
// GetActiveSession may be used to get the session object path for the current active session
func (c *Conn) GetActiveSession() (dbus.ObjectPath, error) {
var seat0Path dbus.ObjectPath
if err := c.object.Call(dbusInterface+".GetSeat", 0, "seat0").Store(&seat0Path); err != nil {
if err := c.object.Call(dbusManagerInterface+".GetSeat", 0, "seat0").Store(&seat0Path); err != nil {
return "", err
}

Expand Down Expand Up @@ -242,7 +244,7 @@ func (c *Conn) GetSessionDisplay(sessionPath dbus.ObjectPath) (string, error) {
// GetSession may be used to get the session object path for the session with the specified ID.
func (c *Conn) GetSession(id string) (dbus.ObjectPath, error) {
var out interface{}
if err := c.object.Call(dbusInterface+".GetSession", 0, id).Store(&out); err != nil {
if err := c.object.Call(dbusManagerInterface+".GetSession", 0, id).Store(&out); err != nil {
return "", err
}

Expand All @@ -262,7 +264,7 @@ func (c *Conn) ListSessions() ([]Session, error) {
// ListSessionsContext returns an array with all current sessions.
func (c *Conn) ListSessionsContext(ctx context.Context) ([]Session, error) {
out := [][]interface{}{}
if err := c.object.CallWithContext(ctx, dbusInterface+".ListSessions", 0).Store(&out); err != nil {
if err := c.object.CallWithContext(ctx, dbusManagerInterface+".ListSessions", 0).Store(&out); err != nil {
return nil, err
}

Expand All @@ -285,7 +287,7 @@ func (c *Conn) ListUsers() ([]User, error) {
// ListUsersContext returns an array with all currently logged-in users.
func (c *Conn) ListUsersContext(ctx context.Context) ([]User, error) {
out := [][]interface{}{}
if err := c.object.CallWithContext(ctx, dbusInterface+".ListUsers", 0).Store(&out); err != nil {
if err := c.object.CallWithContext(ctx, dbusManagerInterface+".ListUsers", 0).Store(&out); err != nil {
return nil, err
}

Expand All @@ -300,36 +302,56 @@ func (c *Conn) ListUsersContext(ctx context.Context) ([]User, error) {
return ret, nil
}

// GetSessionPropertiesContext takes a session path and returns all of its dbus object properties.
func (c *Conn) GetSessionPropertiesContext(ctx context.Context, sessionPath dbus.ObjectPath) (map[string]dbus.Variant, error) {
return c.getProperties(ctx, sessionPath, dbusSessionInterface)
}

// GetSessionPropertyContext takes a session path and a property name and returns the property value.
func (c *Conn) GetSessionPropertyContext(ctx context.Context, sessionPath dbus.ObjectPath, property string) (*dbus.Variant, error) {
return c.getProperty(ctx, sessionPath, dbusSessionInterface, property)
}

// GetUserPropertiesContext takes a user path and returns all of its dbus object properties.
func (c *Conn) GetUserPropertiesContext(ctx context.Context, userPath dbus.ObjectPath) (map[string]dbus.Variant, error) {
return c.getProperties(ctx, userPath, dbusUserInterface)
}

// GetUserPropertyContext takes a user path and a property name and returns the property value.
func (c *Conn) GetUserPropertyContext(ctx context.Context, userPath dbus.ObjectPath, property string) (*dbus.Variant, error) {
return c.getProperty(ctx, userPath, dbusUserInterface, property)
}

// LockSession asks the session with the specified ID to activate the screen lock.
func (c *Conn) LockSession(id string) {
c.object.Call(dbusInterface+".LockSession", 0, id)
c.object.Call(dbusManagerInterface+".LockSession", 0, id)
}

// LockSessions asks all sessions to activate the screen locks. This may be used to lock any access to the machine in one action.
func (c *Conn) LockSessions() {
c.object.Call(dbusInterface+".LockSessions", 0)
c.object.Call(dbusManagerInterface+".LockSessions", 0)
}

// TerminateSession forcibly terminate one specific session.
func (c *Conn) TerminateSession(id string) {
c.object.Call(dbusInterface+".TerminateSession", 0, id)
c.object.Call(dbusManagerInterface+".TerminateSession", 0, id)
}

// TerminateUser forcibly terminates all processes of a user.
func (c *Conn) TerminateUser(uid uint32) {
c.object.Call(dbusInterface+".TerminateUser", 0, uid)
c.object.Call(dbusManagerInterface+".TerminateUser", 0, uid)
}

// Reboot asks logind for a reboot optionally asking for auth.
func (c *Conn) Reboot(askForAuth bool) {
c.object.Call(dbusInterface+".Reboot", 0, askForAuth)
c.object.Call(dbusManagerInterface+".Reboot", 0, askForAuth)
}

// Inhibit takes inhibition lock in logind.
func (c *Conn) Inhibit(what, who, why, mode string) (*os.File, error) {
var fd dbus.UnixFD

err := c.object.Call(dbusInterface+".Inhibit", 0, what, who, why, mode).Store(&fd)
err := c.object.Call(dbusManagerInterface+".Inhibit", 0, what, who, why, mode).Store(&fd)
if err != nil {
return nil, err
}
Expand All @@ -350,5 +372,37 @@ func (c *Conn) Subscribe(members ...string) chan *dbus.Signal {

// PowerOff asks logind for a power off optionally asking for auth.
func (c *Conn) PowerOff(askForAuth bool) {
c.object.Call(dbusInterface+".PowerOff", 0, askForAuth)
c.object.Call(dbusManagerInterface+".PowerOff", 0, askForAuth)
}

func (c *Conn) getProperties(ctx context.Context, path dbus.ObjectPath, dbusInterface string) (map[string]dbus.Variant, error) {
if !path.IsValid() {
return nil, fmt.Errorf("invalid object path (%s)", path)
}

obj := c.conn.Object(dbusDest, path)

var props map[string]dbus.Variant
err := obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
if err != nil {
return nil, err
}

return props, nil
}

func (c *Conn) getProperty(ctx context.Context, path dbus.ObjectPath, dbusInterface, property string) (*dbus.Variant, error) {
if !path.IsValid() {
return nil, fmt.Errorf("invalid object path (%s)", path)
}

obj := c.conn.Object(dbusDest, path)

var prop dbus.Variant
err := obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, dbusInterface, property).Store(&prop)
if err != nil {
return nil, err
}

return &prop, nil
}
104 changes: 104 additions & 0 deletions login1/dbus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
package login1

import (
"context"
"fmt"
"os/user"
"regexp"
"testing"
"time"
)

// TestNew ensures that New() works without errors.
Expand Down Expand Up @@ -87,3 +89,105 @@ func TestListUsers(t *testing.T) {
}
}
}

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)
}
}()
}
}