123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- // 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 dbus
- import (
- "errors"
- "path"
- "strconv"
- "github.com/godbus/dbus"
- )
- func (c *Conn) jobComplete(signal *dbus.Signal) {
- var id uint32
- var job dbus.ObjectPath
- var unit string
- var result string
- dbus.Store(signal.Body, &id, &job, &unit, &result)
- c.jobListener.Lock()
- out, ok := c.jobListener.jobs[job]
- if ok {
- out <- result
- delete(c.jobListener.jobs, job)
- }
- c.jobListener.Unlock()
- }
- func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
- if ch != nil {
- c.jobListener.Lock()
- defer c.jobListener.Unlock()
- }
- var p dbus.ObjectPath
- err := c.sysobj.Call(job, 0, args...).Store(&p)
- if err != nil {
- return 0, err
- }
- if ch != nil {
- c.jobListener.jobs[p] = ch
- }
- // ignore error since 0 is fine if conversion fails
- jobID, _ := strconv.Atoi(path.Base(string(p)))
- return jobID, nil
- }
- // StartUnit enqueues a start job and depending jobs, if any (unless otherwise
- // specified by the mode string).
- //
- // Takes the unit to activate, plus a mode string. The mode needs to be one of
- // replace, fail, isolate, ignore-dependencies, ignore-requirements. If
- // "replace" the call will start the unit and its dependencies, possibly
- // replacing already queued jobs that conflict with this. If "fail" the call
- // will start the unit and its dependencies, but will fail if this would change
- // an already queued job. If "isolate" the call will start the unit in question
- // and terminate all units that aren't dependencies of it. If
- // "ignore-dependencies" it will start a unit but ignore all its dependencies.
- // If "ignore-requirements" it will start a unit but only ignore the
- // requirement dependencies. It is not recommended to make use of the latter
- // two options.
- //
- // If the provided channel is non-nil, a result string will be sent to it upon
- // job completion: one of done, canceled, timeout, failed, dependency, skipped.
- // done indicates successful execution of a job. canceled indicates that a job
- // has been canceled before it finished execution. timeout indicates that the
- // job timeout was reached. failed indicates that the job failed. dependency
- // indicates that a job this job has been depending on failed and the job hence
- // has been removed too. skipped indicates that a job was skipped because it
- // didn't apply to the units current state.
- //
- // If no error occurs, the ID of the underlying systemd job will be returned. There
- // does exist the possibility for no error to be returned, but for the returned job
- // ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
- // should not be considered authoritative.
- //
- // If an error does occur, it will be returned to the user alongside a job ID of 0.
- func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
- }
- // StopUnit is similar to StartUnit but stops the specified unit rather
- // than starting it.
- func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
- }
- // ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
- func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
- }
- // RestartUnit restarts a service. If a service is restarted that isn't
- // running it will be started.
- func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
- }
- // TryRestartUnit is like RestartUnit, except that a service that isn't running
- // is not affected by the restart.
- func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
- }
- // ReloadOrRestart attempts a reload if the unit supports it and use a restart
- // otherwise.
- func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
- }
- // ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
- // flavored restart otherwise.
- func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
- }
- // StartTransientUnit() may be used to create and start a transient unit, which
- // will be released as soon as it is not running or referenced anymore or the
- // system is rebooted. name is the unit name including suffix, and must be
- // unique. mode is the same as in StartUnit(), properties contains properties
- // of the unit.
- func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
- return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
- }
- // KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
- // processes are killed.
- func (c *Conn) KillUnit(name string, signal int32) {
- c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
- }
- // ResetFailedUnit resets the "failed" state of a specific unit.
- func (c *Conn) ResetFailedUnit(name string) error {
- return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
- }
- // getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
- func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
- var err error
- var props map[string]dbus.Variant
- path := unitPath(unit)
- if !path.IsValid() {
- return nil, errors.New("invalid unit name: " + unit)
- }
- obj := c.sysconn.Object("org.freedesktop.systemd1", path)
- err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
- if err != nil {
- return nil, err
- }
- out := make(map[string]interface{}, len(props))
- for k, v := range props {
- out[k] = v.Value()
- }
- return out, nil
- }
- // GetUnitProperties takes the unit name and returns all of its dbus object properties.
- func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
- return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
- }
- func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
- var err error
- var prop dbus.Variant
- path := unitPath(unit)
- if !path.IsValid() {
- return nil, errors.New("invalid unit name: " + unit)
- }
- obj := c.sysconn.Object("org.freedesktop.systemd1", path)
- err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
- if err != nil {
- return nil, err
- }
- return &Property{Name: propertyName, Value: prop}, nil
- }
- func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
- return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
- }
- // GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
- // Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
- // return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
- func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
- return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
- }
- // SetUnitProperties() may be used to modify certain unit properties at runtime.
- // Not all properties may be changed at runtime, but many resource management
- // settings (primarily those in systemd.cgroup(5)) may. The changes are applied
- // instantly, and stored on disk for future boots, unless runtime is true, in which
- // case the settings only apply until the next reboot. name is the name of the unit
- // to modify. properties are the settings to set, encoded as an array of property
- // name and value pairs.
- func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
- return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
- }
- func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
- return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
- }
- // ListUnits returns an array with all currently loaded units. Note that
- // units may be known by multiple names at the same time, and hence there might
- // be more unit names loaded than actual units behind them.
- func (c *Conn) ListUnits() ([]UnitStatus, error) {
- result := make([][]interface{}, 0)
- err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
- if err != nil {
- return nil, err
- }
- resultInterface := make([]interface{}, len(result))
- for i := range result {
- resultInterface[i] = result[i]
- }
- status := make([]UnitStatus, len(result))
- statusInterface := make([]interface{}, len(status))
- for i := range status {
- statusInterface[i] = &status[i]
- }
- err = dbus.Store(resultInterface, statusInterface...)
- if err != nil {
- return nil, err
- }
- return status, nil
- }
- type UnitStatus struct {
- Name string // The primary unit name as string
- Description string // The human readable description string
- LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
- ActiveState string // The active state (i.e. whether the unit is currently started or not)
- SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
- Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
- Path dbus.ObjectPath // The unit object path
- JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
- JobType string // The job type as string
- JobPath dbus.ObjectPath // The job object path
- }
- type LinkUnitFileChange EnableUnitFileChange
- // LinkUnitFiles() links unit files (that are located outside of the
- // usual unit search paths) into the unit search path.
- //
- // It takes a list of absolute paths to unit files to link and two
- // booleans. The first boolean controls whether the unit shall be
- // enabled for runtime only (true, /run), or persistently (false,
- // /etc).
- // The second controls whether symlinks pointing to other units shall
- // be replaced if necessary.
- //
- // This call returns a list of the changes made. The list consists of
- // structures with three strings: the type of the change (one of symlink
- // or unlink), the file name of the symlink and the destination of the
- // symlink.
- func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
- result := make([][]interface{}, 0)
- err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
- if err != nil {
- return nil, err
- }
- resultInterface := make([]interface{}, len(result))
- for i := range result {
- resultInterface[i] = result[i]
- }
- changes := make([]LinkUnitFileChange, len(result))
- changesInterface := make([]interface{}, len(changes))
- for i := range changes {
- changesInterface[i] = &changes[i]
- }
- err = dbus.Store(resultInterface, changesInterface...)
- if err != nil {
- return nil, err
- }
- return changes, nil
- }
- // EnableUnitFiles() may be used to enable one or more units in the system (by
- // creating symlinks to them in /etc or /run).
- //
- // It takes a list of unit files to enable (either just file names or full
- // absolute paths if the unit files are residing outside the usual unit
- // search paths), and two booleans: the first controls whether the unit shall
- // be enabled for runtime only (true, /run), or persistently (false, /etc).
- // The second one controls whether symlinks pointing to other units shall
- // be replaced if necessary.
- //
- // This call returns one boolean and an array with the changes made. The
- // boolean signals whether the unit files contained any enablement
- // information (i.e. an [Install]) section. The changes list consists of
- // structures with three strings: the type of the change (one of symlink
- // or unlink), the file name of the symlink and the destination of the
- // symlink.
- func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
- var carries_install_info bool
- result := make([][]interface{}, 0)
- err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
- if err != nil {
- return false, nil, err
- }
- resultInterface := make([]interface{}, len(result))
- for i := range result {
- resultInterface[i] = result[i]
- }
- changes := make([]EnableUnitFileChange, len(result))
- changesInterface := make([]interface{}, len(changes))
- for i := range changes {
- changesInterface[i] = &changes[i]
- }
- err = dbus.Store(resultInterface, changesInterface...)
- if err != nil {
- return false, nil, err
- }
- return carries_install_info, changes, nil
- }
- type EnableUnitFileChange struct {
- Type string // Type of the change (one of symlink or unlink)
- Filename string // File name of the symlink
- Destination string // Destination of the symlink
- }
- // DisableUnitFiles() may be used to disable one or more units in the system (by
- // removing symlinks to them from /etc or /run).
- //
- // It takes a list of unit files to disable (either just file names or full
- // absolute paths if the unit files are residing outside the usual unit
- // search paths), and one boolean: whether the unit was enabled for runtime
- // only (true, /run), or persistently (false, /etc).
- //
- // This call returns an array with the changes made. The changes list
- // consists of structures with three strings: the type of the change (one of
- // symlink or unlink), the file name of the symlink and the destination of the
- // symlink.
- func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
- result := make([][]interface{}, 0)
- err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
- if err != nil {
- return nil, err
- }
- resultInterface := make([]interface{}, len(result))
- for i := range result {
- resultInterface[i] = result[i]
- }
- changes := make([]DisableUnitFileChange, len(result))
- changesInterface := make([]interface{}, len(changes))
- for i := range changes {
- changesInterface[i] = &changes[i]
- }
- err = dbus.Store(resultInterface, changesInterface...)
- if err != nil {
- return nil, err
- }
- return changes, nil
- }
- type DisableUnitFileChange struct {
- Type string // Type of the change (one of symlink or unlink)
- Filename string // File name of the symlink
- Destination string // Destination of the symlink
- }
- // Reload instructs systemd to scan for and reload unit files. This is
- // equivalent to a 'systemctl daemon-reload'.
- func (c *Conn) Reload() error {
- return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
- }
- func unitPath(name string) dbus.ObjectPath {
- return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
- }
|