blob: b1da5bd1b485258f1b7e0543468a8b889e3b98cf [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package session interacts with session_manager.
package session
import (
"context"
"github.com/godbus/dbus"
"github.com/golang/protobuf/proto"
"github.com/shirou/gopsutil/process"
"chromiumos/policy/enterprise_management"
"chromiumos/tast/errors"
"chromiumos/tast/local/dbusutil"
)
const (
dbusName = "org.chromium.SessionManager"
dbusPath = "/org/chromium/SessionManager"
dbusInterface = "org.chromium.SessionManagerInterface"
)
// GetSessionManagerPID returns the PID of the session_manager.
func GetSessionManagerPID() (int, error) {
const exePath = "/sbin/session_manager"
all, err := process.Pids()
if err != nil {
return -1, err
}
for _, pid := range all {
if proc, err := process.NewProcess(pid); err != nil {
// Assume that the process exited.
continue
} else if exe, err := proc.Exe(); err == nil && exe == exePath {
return int(pid), nil
}
}
return -1, errors.New("session_manager process not found")
}
// SessionManager is used to interact with the session_manager process over
// D-Bus.
// For detailed spec of each D-Bus method, please find
// src/platform2/login_manager/dbus_bindings/org.chromium.SessionManagerInterface.xml
type SessionManager struct { // NOLINT
conn *dbus.Conn
obj dbus.BusObject
}
// NewSessionManager connects to session_manager via D-Bus and returns a SessionManager object.
func NewSessionManager(ctx context.Context) (*SessionManager, error) {
conn, obj, err := dbusutil.Connect(ctx, dbusName, dbusPath)
if err != nil {
return nil, err
}
return &SessionManager{conn, obj}, nil
}
// EnableChromeTesting calls SessionManager.EnableChromeTesting D-Bus method.
func (m *SessionManager) EnableChromeTesting(
ctx context.Context,
forceRelaunch bool,
extraArguments []string,
extraEnvironmentVariables []string) (filepath string, err error) {
c := m.call(ctx, "EnableChromeTesting",
forceRelaunch, extraArguments, extraEnvironmentVariables)
if err := c.Store(&filepath); err != nil {
return "", err
}
return filepath, nil
}
// HandleSupervisedUserCreationStarting calls
// SessionManager.HandleSupervisedUserCreationStarting D-Bus method.
func (m *SessionManager) HandleSupervisedUserCreationStarting(
ctx context.Context) error {
return m.call(ctx, "HandleSupervisedUserCreationStarting").Err
}
// HandleSupervisedUserCreationFinished calls
// SessionManager.HandleSupervisedUserCreationFinished D-Bus method.
func (m *SessionManager) HandleSupervisedUserCreationFinished(
ctx context.Context) error {
return m.call(ctx, "HandleSupervisedUserCreationFinished").Err
}
// RetrieveSessionState calls SessionManager.RetrieveSessionState D-Bus method.
func (m *SessionManager) RetrieveSessionState(ctx context.Context) (string, error) {
c := m.call(ctx, "RetrieveSessionState")
var state string
if err := c.Store(&state); err != nil {
return "", err
}
return state, nil
}
// StartSession calls SessionManager.StartSession D-Bus method.
func (m *SessionManager) StartSession(ctx context.Context, accountID, uniqueIdentifier string) error {
return m.call(ctx, "StartSession", accountID, uniqueIdentifier).Err
}
// StorePolicy calls SessionManager.StorePolicy D-Bus method.
func (m *SessionManager) StorePolicy(ctx context.Context, policy *enterprise_management.PolicyFetchResponse) error {
return m.callProtoMethod(ctx, "StorePolicy", policy, nil)
}
// RetrievePolicy calls SessionManager.RetrievePolicy D-Bus method.
func (m *SessionManager) RetrievePolicy(ctx context.Context) (*enterprise_management.PolicyFetchResponse, error) {
ret := &enterprise_management.PolicyFetchResponse{}
if err := m.callProtoMethod(ctx, "RetrievePolicy", nil, ret); err != nil {
return nil, err
}
return ret, nil
}
// RetrieveActiveSessions calls SessionManager.RetrieveActiveSessions D-Bus method.
func (m *SessionManager) RetrieveActiveSessions(ctx context.Context) (map[string]string, error) {
c := m.call(ctx, "RetrieveActiveSessions")
var ret map[string]string
if err := c.Store(&ret); err != nil {
return nil, err
}
return ret, nil
}
// call is thin wrapper of CallWithContext for convenience.
func (m *SessionManager) call(ctx context.Context, method string, args ...interface{}) *dbus.Call {
return m.obj.CallWithContext(ctx, dbusInterface+"."+method, 0, args...)
}
// callProtoMethod is thin wrapper of CallProtoMethod for convenience.
func (m *SessionManager) callProtoMethod(ctx context.Context, method string, in, out proto.Message) error {
return dbusutil.CallProtoMethod(ctx, m.obj, dbusInterface+"."+method, in, out)
}
// WatchSessionStateChanged returns a SignalWatcher to observe
// "SessionStateChanged" signal for the given state. If state is empty, it
// matches with any "SessionStateChanged" signals.
func (m *SessionManager) WatchSessionStateChanged(ctx context.Context, state string) (*dbusutil.SignalWatcher, error) {
spec := dbusutil.MatchSpec{
Type: "signal",
Path: dbusPath,
Interface: dbusInterface,
Member: "SessionStateChanged",
Arg0: state,
}
return dbusutil.NewSignalWatcher(ctx, m.conn, spec)
}
// WatchPropertyChangeComplete returns a SignalWatcher to observe
// "PropertyChangeComplete" signal.
func (m *SessionManager) WatchPropertyChangeComplete(ctx context.Context) (*dbusutil.SignalWatcher, error) {
spec := dbusutil.MatchSpec{
Type: "signal",
Path: dbusPath,
Interface: dbusInterface,
Member: "PropertyChangeComplete",
}
return dbusutil.NewSignalWatcher(ctx, m.conn, spec)
}
// WatchSetOwnerKeyComplete returns a SignalWatcher to observe
// "SetOwnerKeyComplete" signal.
func (m *SessionManager) WatchSetOwnerKeyComplete(ctx context.Context) (*dbusutil.SignalWatcher, error) {
spec := dbusutil.MatchSpec{
Type: "signal",
Path: dbusPath,
Interface: dbusInterface,
Member: "SetOwnerKeyComplete",
}
return dbusutil.NewSignalWatcher(ctx, m.conn, spec)
}