Skip to content

cbowdon/daemons.el

Repository files navigation

daemons.el https://melpa.org/packages/daemons-badge.svg https://stable.melpa.org/packages/daemons-badge.svg

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:

  • systemd (RHEL7, Fedora, openSUSE, Debian, Ubuntu, Arch, etc.)
  • SysVinit (RHEL6)
  • brew services (macOS)
  • GNU Shepherd (GuixSD)
  • OpenRC (Gentoo)

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:

CommandKey in *daemons* buffer
daemons-statusRET
daemons-starts
daemons-stopS
daemons-reloadr
daemons-restartR
daemons-enablee
daemons-disabled

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 MELPA with M-x package-install daemons. See 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 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.:

(use-package daemons :pin melpa-stable)

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:
(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))

Have a look at any of the existing submodules for inspiration. 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.