package budget import ( "errors" "strings" "github.com/godbus/dbus/v5" ) const api = "org.freedesktop.systemd1" const noUnit = api + ".NoSuchUnit" const noPID = "org.freedesktop.DBus.Error.UnixProcessIdUnknown" type unit struct { Name string Description string Load string Active string Sub string Following string Path dbus.ObjectPath Job uint32 JobType string JobPath dbus.ObjectPath } type unitState struct { Active string Group string } type unitCtl interface { Units() ([]unit, error) Group(string) (string, error) Set(string, []property) error Start(string, []property) error State(string) (unitState, error) Stop(string) error } type manager struct { conn *dbus.Conn obj dbus.BusObject } type auxiliary struct { Name string Properties []property } func connect() (*manager, error) { conn, err := dbus.ConnectSessionBus() if err != nil { return nil, err } return &manager{conn: conn, obj: conn.Object(api, "/org/freedesktop/systemd1")}, nil } func busError(err error, name string) bool { var e dbus.Error return errors.As(err, &e) && e.Name == name } func (m *manager) Units() ([]unit, error) { var units []unit err := m.obj.Call(api+".Manager.ListUnits", 0).Store(&units) return units, err } func (m *manager) object(name string) (dbus.BusObject, error) { var path dbus.ObjectPath if err := m.obj.Call(api+".Manager.GetUnit", 0, name).Store(&path); err != nil { return nil, err } return m.conn.Object(api, path), nil } func (m *manager) Group(name string) (string, error) { obj, err := m.object(name) if err != nil { return "", err } kind := name[strings.LastIndex(name, ".")+1:] kind = strings.ToUpper(kind[:1]) + kind[1:] var group string err = obj.StoreProperty(api+"."+kind+".ControlGroup", &group) return group, err } func (m *manager) Set(name string, values []property) error { return m.obj.Call(api+".Manager.SetUnitProperties", 0, name, true, values).Err } func (m *manager) Start(name string, values []property) error { return m.obj.Call(api+".Manager.StartTransientUnit", 0, name, "fail", values, []auxiliary{}).Err } func (m *manager) State(name string) (unitState, error) { obj, err := m.object(name) if err != nil { return unitState{}, err } var state unitState if err = obj.StoreProperty(api+".Unit.ActiveState", &state.Active); err != nil { return unitState{}, err } err = obj.StoreProperty(api+".Scope.ControlGroup", &state.Group) return state, err } func (m *manager) Stop(name string) error { return m.obj.Call(api+".Manager.StopUnit", 0, name, "replace").Err }