cbowdon / daemons.el

An Emacs UI for managing init system services
GNU General Public License v3.0
102 stars 12 forks source link
daemons emacs services systemd sysvinit

This is an Emacs mode to give you a UI for managing init system daemons (services). I wrote this after getting tired of typing out =sudo service my_thing reload= all the time. It's also nice to have a consistent UI over different init systems.

N.B. This package was briefly known as =services-mode=.

** System support

There is support for the following init systems and service managers:

It's designed to be easily extensible (see below) so if your system isn't supported please consider contributing.

Managing daemons on local and remote systems is supported.

** Usage

You can open the daemons list with =M-x daemons=. If started from a TRAMP buffer then the daemons for that remote system will be listed and manageable, otherwise it will be all the daemons on your local system.

Navigate the list with =n= and =p=. Refresh the list with =g= (it's just a =tabulated-list-mode= buffer). You can sort the buffer with =tabulated-list-sort= or by clicking a header.

The following commands are available for each daemon:

| Command | Key in =daemons= buffer | |-----------------+-------------------------| | =daemons-status= | RET | | =daemons-start= | s | | =daemons-stop= | S | | =daemons-reload= | r | | =daemons-restart= | R | | =daemons-enable= | e | | =daemons-disable= | d |

/Sorry I've only implemented =enable= and =disable= for systemd so far - raise an issue if you want support for your system!/

Results of commands are displayed in a =special-mode= buffer, in which the same commands are available for the selected daemon. So you can (for example) keep reloading the same daemon with =r= without having to re-select it.

You can dismiss either buffer with =q=.

Of course you can also call the commands interactively, e.g. =M-x daemons-start= and enter the daemon's name at the prompt. The prompt has completion, so (for example) you can type =do= to narrow it down to =docker=, or =sys= to narrow it down to any of the 193 =systemd= services.

It looks something like this for systemd:

[[./img/daemons-systemd-demo.png]]

and something like this for SysVinit:

[[./img/daemons-sysvinit-demo.png]]

** Installation

You can install this from [[https://melpa.org][MELPA]] with =M-x package-install daemons=. See [[https://melpa.org/#/getting-started][here]] for how to get started with MELPA.

If for some reason you are unable or prefer not to use MELPA, you can also do this:

  1. Download the [[https://github.com/cbowdon/daemons.el/releases][latest release distribution]] and extract.
  2. Install with =M-x package-install-file= - at the prompt, pass the path to the extracted directory, e.g. =~/Downloads/daemons.el-VERSION/=.

** Configuration

Please see =M-x customize-mode=.

For ~eldoc~ support (currently ~systemd~ only), you can use ~(add-hook 'daemons-mode-hook eldoc-mode)~.

** Troubleshooting

Note that the MELPA Stable release is updated very conservatively, so if you have any problem that can't be solved by the below advice, please try reverting the stable version, e.g.:

+BEGIN_SRC emacs-lisp

(use-package daemons :pin melpa-stable)

+END_SRC

Of course also feel free to raise an issue or submit a PR. :-)

*** Sudo permissions

If you are having trouble with permissions for SysV or GNU Shepherd then setting the =daemons-always-sudo= custom variable may be what you need.

Alternatively you can navigate to a TRAMP path as a sudoer such as by doing =M-x cd /sudo::= and then call =M-x daemons=. This method also works for remote systems e.g. visit =/ssh:me@remotehost|sudo:remotehost:= then run =M-x daemons=. The buffer name will show what user and host you are on.

Please +drink+ use sudo responsibly.

*** Password caching

Have a look at =M-x customize-group password= for this. You could also use the sudo tricks above.

*** Emacs version support

This package requires Emacs 25.1 or higher. Users are successfully using it with Emacs 26 and 27, but things may yet get broken by ongoing development. If you spot such a thing please raise an issue.

** Extending

To extend =daemons= to support another init system, there are two steps:

  1. Add your extension name (e.g. =daemons-example=) to the =daemons-init-system-submodules= custom variable.
  2. Write a package that defines the extension using the =daemons-define-submodule= macro:

+BEGIN_EXAMPLE emacs-lisp

(daemons-define-submodule daemons-example "Daemons submodule for Example system, where 'ex-sys' is the daemons manager (like 'systemctl' or 'service')."

;; This is an expression to evaluate that will return true if this submodule works on this system:
:test
(and (eq system-type 'gnu/linux)
     (equal 0 (daemons--shell-command "which ex-sys")))

;; This is a map of user commands (see `daemons--commands-alist'):
:commands
'((status . (lambda (name) (format "ex-sys status %s" name)))
  (start . (lambda (name) (format "ex-sys start %s" name)))
  (stop . (lambda (name) (format "ex-sys stop %s" name)))
  (reload . (lambda (name) (format "ex-sys reload %s" name)))
  (restart . (lambda (name) (format "ex-sys restart %s" name)))
  (enable . (lambda (name) (format "ex-sys enable %s" name)))
  (disable . (lambda (name) (format "ex-sys disable %s" name))))

;; This is expression to get the daemons list (see `daemons--list'):
:list
(daemons-example--list)

;; This is an expression to get the list headers (see `daemons--list-headers'):
:headers
(daemons-example--list-headers))

+END_EXAMPLE

Have a look at any of the existing submodules for inspiration. [[./daemons-shepherd.el][The submodule for GNU Shepherd]] is a short and sweet example - there's a definition at the top, a couple of helper functions for parsing command output and that's it.