methods.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. Copyright 2013 CoreOS Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package dbus
  14. import (
  15. "errors"
  16. "github.com/godbus/dbus"
  17. )
  18. func (c *Conn) initJobs() {
  19. c.jobListener.jobs = make(map[dbus.ObjectPath]chan string)
  20. }
  21. func (c *Conn) jobComplete(signal *dbus.Signal) {
  22. var id uint32
  23. var job dbus.ObjectPath
  24. var unit string
  25. var result string
  26. dbus.Store(signal.Body, &id, &job, &unit, &result)
  27. c.jobListener.Lock()
  28. out, ok := c.jobListener.jobs[job]
  29. if ok {
  30. out <- result
  31. delete(c.jobListener.jobs, job)
  32. }
  33. c.jobListener.Unlock()
  34. }
  35. func (c *Conn) startJob(job string, args ...interface{}) (<-chan string, error) {
  36. c.jobListener.Lock()
  37. defer c.jobListener.Unlock()
  38. ch := make(chan string, 1)
  39. var path dbus.ObjectPath
  40. err := c.sysobj.Call(job, 0, args...).Store(&path)
  41. if err != nil {
  42. return nil, err
  43. }
  44. c.jobListener.jobs[path] = ch
  45. return ch, nil
  46. }
  47. func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
  48. respCh, err := c.startJob(job, args...)
  49. if err != nil {
  50. return "", err
  51. }
  52. return <-respCh, nil
  53. }
  54. // StartUnit enqueues a start job and depending jobs, if any (unless otherwise
  55. // specified by the mode string).
  56. //
  57. // Takes the unit to activate, plus a mode string. The mode needs to be one of
  58. // replace, fail, isolate, ignore-dependencies, ignore-requirements. If
  59. // "replace" the call will start the unit and its dependencies, possibly
  60. // replacing already queued jobs that conflict with this. If "fail" the call
  61. // will start the unit and its dependencies, but will fail if this would change
  62. // an already queued job. If "isolate" the call will start the unit in question
  63. // and terminate all units that aren't dependencies of it. If
  64. // "ignore-dependencies" it will start a unit but ignore all its dependencies.
  65. // If "ignore-requirements" it will start a unit but only ignore the
  66. // requirement dependencies. It is not recommended to make use of the latter
  67. // two options.
  68. //
  69. // Result string: one of done, canceled, timeout, failed, dependency, skipped.
  70. // done indicates successful execution of a job. canceled indicates that a job
  71. // has been canceled before it finished execution. timeout indicates that the
  72. // job timeout was reached. failed indicates that the job failed. dependency
  73. // indicates that a job this job has been depending on failed and the job hence
  74. // has been removed too. skipped indicates that a job was skipped because it
  75. // didn't apply to the units current state.
  76. func (c *Conn) StartUnit(name string, mode string) (string, error) {
  77. return c.runJob("org.freedesktop.systemd1.Manager.StartUnit", name, mode)
  78. }
  79. // StopUnit is similar to StartUnit but stops the specified unit rather
  80. // than starting it.
  81. func (c *Conn) StopUnit(name string, mode string) (string, error) {
  82. return c.runJob("org.freedesktop.systemd1.Manager.StopUnit", name, mode)
  83. }
  84. // ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
  85. func (c *Conn) ReloadUnit(name string, mode string) (string, error) {
  86. return c.runJob("org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
  87. }
  88. // RestartUnit restarts a service. If a service is restarted that isn't
  89. // running it will be started.
  90. func (c *Conn) RestartUnit(name string, mode string) (string, error) {
  91. return c.runJob("org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
  92. }
  93. // TryRestartUnit is like RestartUnit, except that a service that isn't running
  94. // is not affected by the restart.
  95. func (c *Conn) TryRestartUnit(name string, mode string) (string, error) {
  96. return c.runJob("org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
  97. }
  98. // ReloadOrRestart attempts a reload if the unit supports it and use a restart
  99. // otherwise.
  100. func (c *Conn) ReloadOrRestartUnit(name string, mode string) (string, error) {
  101. return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
  102. }
  103. // ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
  104. // flavored restart otherwise.
  105. func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error) {
  106. return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
  107. }
  108. // StartTransientUnit() may be used to create and start a transient unit, which
  109. // will be released as soon as it is not running or referenced anymore or the
  110. // system is rebooted. name is the unit name including suffix, and must be
  111. // unique. mode is the same as in StartUnit(), properties contains properties
  112. // of the unit.
  113. func (c *Conn) StartTransientUnit(name string, mode string, properties ...Property) (string, error) {
  114. return c.runJob("org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
  115. }
  116. // KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
  117. // processes are killed.
  118. func (c *Conn) KillUnit(name string, signal int32) {
  119. c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
  120. }
  121. // ResetFailedUnit resets the "failed" state of a specific unit.
  122. func (c *Conn) ResetFailedUnit(name string) error {
  123. return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
  124. }
  125. // getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
  126. func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
  127. var err error
  128. var props map[string]dbus.Variant
  129. path := unitPath(unit)
  130. if !path.IsValid() {
  131. return nil, errors.New("invalid unit name: " + unit)
  132. }
  133. obj := c.sysconn.Object("org.freedesktop.systemd1", path)
  134. err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
  135. if err != nil {
  136. return nil, err
  137. }
  138. out := make(map[string]interface{}, len(props))
  139. for k, v := range props {
  140. out[k] = v.Value()
  141. }
  142. return out, nil
  143. }
  144. // GetUnitProperties takes the unit name and returns all of its dbus object properties.
  145. func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
  146. return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
  147. }
  148. func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
  149. var err error
  150. var prop dbus.Variant
  151. path := unitPath(unit)
  152. if !path.IsValid() {
  153. return nil, errors.New("invalid unit name: " + unit)
  154. }
  155. obj := c.sysconn.Object("org.freedesktop.systemd1", path)
  156. err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
  157. if err != nil {
  158. return nil, err
  159. }
  160. return &Property{Name: propertyName, Value: prop}, nil
  161. }
  162. func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
  163. return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
  164. }
  165. // GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
  166. // Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
  167. // return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
  168. func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
  169. return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
  170. }
  171. // SetUnitProperties() may be used to modify certain unit properties at runtime.
  172. // Not all properties may be changed at runtime, but many resource management
  173. // settings (primarily those in systemd.cgroup(5)) may. The changes are applied
  174. // instantly, and stored on disk for future boots, unless runtime is true, in which
  175. // case the settings only apply until the next reboot. name is the name of the unit
  176. // to modify. properties are the settings to set, encoded as an array of property
  177. // name and value pairs.
  178. func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
  179. return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
  180. }
  181. func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
  182. return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
  183. }
  184. // ListUnits returns an array with all currently loaded units. Note that
  185. // units may be known by multiple names at the same time, and hence there might
  186. // be more unit names loaded than actual units behind them.
  187. func (c *Conn) ListUnits() ([]UnitStatus, error) {
  188. result := make([][]interface{}, 0)
  189. err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
  190. if err != nil {
  191. return nil, err
  192. }
  193. resultInterface := make([]interface{}, len(result))
  194. for i := range result {
  195. resultInterface[i] = result[i]
  196. }
  197. status := make([]UnitStatus, len(result))
  198. statusInterface := make([]interface{}, len(status))
  199. for i := range status {
  200. statusInterface[i] = &status[i]
  201. }
  202. err = dbus.Store(resultInterface, statusInterface...)
  203. if err != nil {
  204. return nil, err
  205. }
  206. return status, nil
  207. }
  208. type UnitStatus struct {
  209. Name string // The primary unit name as string
  210. Description string // The human readable description string
  211. LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
  212. ActiveState string // The active state (i.e. whether the unit is currently started or not)
  213. 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)
  214. Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
  215. Path dbus.ObjectPath // The unit object path
  216. JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
  217. JobType string // The job type as string
  218. JobPath dbus.ObjectPath // The job object path
  219. }
  220. type LinkUnitFileChange EnableUnitFileChange
  221. // LinkUnitFiles() links unit files (that are located outside of the
  222. // usual unit search paths) into the unit search path.
  223. //
  224. // It takes a list of absolute paths to unit files to link and two
  225. // booleans. The first boolean controls whether the unit shall be
  226. // enabled for runtime only (true, /run), or persistently (false,
  227. // /etc).
  228. // The second controls whether symlinks pointing to other units shall
  229. // be replaced if necessary.
  230. //
  231. // This call returns a list of the changes made. The list consists of
  232. // structures with three strings: the type of the change (one of symlink
  233. // or unlink), the file name of the symlink and the destination of the
  234. // symlink.
  235. func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
  236. result := make([][]interface{}, 0)
  237. err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
  238. if err != nil {
  239. return nil, err
  240. }
  241. resultInterface := make([]interface{}, len(result))
  242. for i := range result {
  243. resultInterface[i] = result[i]
  244. }
  245. changes := make([]LinkUnitFileChange, len(result))
  246. changesInterface := make([]interface{}, len(changes))
  247. for i := range changes {
  248. changesInterface[i] = &changes[i]
  249. }
  250. err = dbus.Store(resultInterface, changesInterface...)
  251. if err != nil {
  252. return nil, err
  253. }
  254. return changes, nil
  255. }
  256. // EnableUnitFiles() may be used to enable one or more units in the system (by
  257. // creating symlinks to them in /etc or /run).
  258. //
  259. // It takes a list of unit files to enable (either just file names or full
  260. // absolute paths if the unit files are residing outside the usual unit
  261. // search paths), and two booleans: the first controls whether the unit shall
  262. // be enabled for runtime only (true, /run), or persistently (false, /etc).
  263. // The second one controls whether symlinks pointing to other units shall
  264. // be replaced if necessary.
  265. //
  266. // This call returns one boolean and an array with the changes made. The
  267. // boolean signals whether the unit files contained any enablement
  268. // information (i.e. an [Install]) section. The changes list consists of
  269. // structures with three strings: the type of the change (one of symlink
  270. // or unlink), the file name of the symlink and the destination of the
  271. // symlink.
  272. func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
  273. var carries_install_info bool
  274. result := make([][]interface{}, 0)
  275. err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
  276. if err != nil {
  277. return false, nil, err
  278. }
  279. resultInterface := make([]interface{}, len(result))
  280. for i := range result {
  281. resultInterface[i] = result[i]
  282. }
  283. changes := make([]EnableUnitFileChange, len(result))
  284. changesInterface := make([]interface{}, len(changes))
  285. for i := range changes {
  286. changesInterface[i] = &changes[i]
  287. }
  288. err = dbus.Store(resultInterface, changesInterface...)
  289. if err != nil {
  290. return false, nil, err
  291. }
  292. return carries_install_info, changes, nil
  293. }
  294. type EnableUnitFileChange struct {
  295. Type string // Type of the change (one of symlink or unlink)
  296. Filename string // File name of the symlink
  297. Destination string // Destination of the symlink
  298. }
  299. // DisableUnitFiles() may be used to disable one or more units in the system (by
  300. // removing symlinks to them from /etc or /run).
  301. //
  302. // It takes a list of unit files to disable (either just file names or full
  303. // absolute paths if the unit files are residing outside the usual unit
  304. // search paths), and one boolean: whether the unit was enabled for runtime
  305. // only (true, /run), or persistently (false, /etc).
  306. //
  307. // This call returns an array with the changes made. The changes list
  308. // consists of structures with three strings: the type of the change (one of
  309. // symlink or unlink), the file name of the symlink and the destination of the
  310. // symlink.
  311. func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
  312. result := make([][]interface{}, 0)
  313. err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
  314. if err != nil {
  315. return nil, err
  316. }
  317. resultInterface := make([]interface{}, len(result))
  318. for i := range result {
  319. resultInterface[i] = result[i]
  320. }
  321. changes := make([]DisableUnitFileChange, len(result))
  322. changesInterface := make([]interface{}, len(changes))
  323. for i := range changes {
  324. changesInterface[i] = &changes[i]
  325. }
  326. err = dbus.Store(resultInterface, changesInterface...)
  327. if err != nil {
  328. return nil, err
  329. }
  330. return changes, nil
  331. }
  332. type DisableUnitFileChange struct {
  333. Type string // Type of the change (one of symlink or unlink)
  334. Filename string // File name of the symlink
  335. Destination string // Destination of the symlink
  336. }
  337. // Reload instructs systemd to scan for and reload unit files. This is
  338. // equivalent to a 'systemctl daemon-reload'.
  339. func (c *Conn) Reload() error {
  340. return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
  341. }
  342. func unitPath(name string) dbus.ObjectPath {
  343. return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
  344. }