Configuring services with systemd
In this article we will explore what systemd is, what problems it solves in the embedded systems field and how we can write a configuration file for our application.
We should start by saying that systemd is a service manager for Linux. By service, we mean any program running on the system. Systemd is compatible with SysV init scripts but offers advanced features such as:
- the starting of services in parallel
- the activation of services via socket or Dbus
- the managing of dependencies between different services
- the process isolation via cgroups
These features make it possible to simplify the configuration of services when booting an embedded system as well as allowing precise control over each individual service.
Consider for example a GUI application that during its operation needs to spawn other processes, such as a music player. In a normal system these processes have their own lifetime with respect to the program that spawned them. So, in order to ensure that the GUI application is fully stopped, it is necessary to kill all the processes that the GUI can spawn.
With systemd of this is necessary, since the processes spawned by the GUI are part of the same systemd service. Therefore, all that is required is to stop the GUI service, after which systemd will know which processes must be stopped.
Each service is described by a configuration file, which has a format very similar to INI files.
The simplest file consists of 3 sections: Unit, Service and Install.
It contains the basic information about the service. The most important fields of this section are:
- Description: a line describing the service.
- Wants: a list of services required for this service. If your program uses socket or dbus activation, systemd automatically manages dependencies and this field is not required.
- After: systemd will wait for the services contained in this field to start before starting this process. This field can also be omitted if activation via socket or dbus is being used.
This section specifies which file should be executed and how it will be executed. The most important fields are:
- ExecStart: the absolute path of the executable file.
it is used to set the environment variables for the process. For
Environment=EGLFS_HIDE_CURSOR=1 “POINTERCAL_FILE=/path/with spaces/pointercal”
Note that if the variable contains spaces, the name of the variable must also be placed in quotation mark.
- Type: it is used by systemd to understand which process is the “main process”. It is useful when there are daemons that fork a few times before starting; conversely, it is not particularly useful in the case of a single application to be launched. Just set it to simple.
- Restart: it configures how and when the service must be started. For embedded systems that must always be on, the recommended value is always.
- SuccessExitStatus: a list of signals or numbers (other than 0) that indicate correct termination of the program.
Multiple forks are widely used to create daemons from the shell. The documentation in this regard is very detailed.
This section is useful for deciding when a service should be enabled or disabled. In systemd jargon, a service is enabled if it is started automatically at boot; this is usually the intended behaviour for embedded systems. In short:
Here is a complete example of a configuration file for a GUI application to be started at boot.
[Unit] Description=Description for myprogram Wants=rpcbind.service After=rpcbind.service [Service] Environment=“MY_ENV_VAR=/some/path/to/file with space.ini” ExecStart=/usr/local/myprogram Type=simple [Install] WantedBy=multi-user.target