[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[GSoC Report] Week 4 - Systemd Unit Translator



Hello, everyone!

It has been a tremendous 4th week after 3 weeks of gaining and losing momentum. As described in my proposal's schedule, as of June 28, the translator is capable of handling service, socket and timer units. The translator is completely written in bash. It currently depends on `gawk` and `sed` but reducing external dependencies is a long term goal as bash has decent filtering capabilities itself and it makes much more sense to leverage all in-built functionality before using an external tool.

SOURCE: https://git.kayg.org/gsoc-2020/translator.git
USAGE:  bash main.sh <systemd-unit.type> <directory-for-resulting-scripts>

An overview of how the translator works till now:

Parsing:
  A generic parsing mechanism has been defined for parsing all sections. [Unit] and [Install] are common to all whereas [Service], [Socket] and [Timer] are unique. Two arrays are defined for storing key / value pairs of the aforementioned sections while an associative array is defined to store (*associate*) the keys and values. Each section is filtered with the help of `sed` and key / value pairs are fed to the associative array in a for loop.

  To handle cases where there are multiple occurrences of `=`, a gawk dependency was introduced (replacing `cut`) which can easily handle this with a little regular expression magic.

Service Units:
  Units are first fed to the parser which builds an array for other functions to do their thing. Then the unit's dependencies are matched against a predefined set of dependencies. Currently, `network*.target` and `dbus.target` are handled. Once the mapping is complete, the translator checks for the existence of resulting files and if they do exist, a prompt for confirming the overwrite is shown to the user. This does not yet check for an interactive shell which is in the list of TODOs.

  Generation of the resulting openrc script begins: a shebang of value `#!/sbin/openrc-run` is added. The translator creates two directories under the base directory passed as the second argument to the script (USAGE can be referred above). Depending on the presence of the key: `Environment`, a `conf.d/service` script is generated, from which OpenRC gets environment variables for the initscript's (`init.d/service`) `command`.

  Thereafter, variables like `command` and `command_args` are written to the initscript. Details about how values are filtered are described in comments next to the concerned code. One small thing of note here is the existence of `Description` and `Documentation` keys in the systemd service unit while only a single variable (`description`) exists for OpenRC. Hence both of them are inserted in `description` in the resulting script. Intricacies involving PIDFILE is still a pending task.

  Function generation is pretty simple as the script does not yet try to define its own start / stop / status / restart functions. Those will be conditionally generated, and implemented later in due course of this project as the translator is tested with more units. For now, `depend()` is the only function to be generated and hard / soft dependencies are handled with `need` and `use` while ordering of dependencies are handled with `before` / `after`.

TESTED WITH: acpid.service, fstrim.service

Socket Units:
  Since OpenRC itself doesn't handle sockets, `xinetd` has been used instead which acts as a *superserver*. Every `.socket` type should have a corresponding `.service` type. The absence of which will result in an error.

  After the usual parsing is done, the unit's keys are matched against a predefined set of formats that systemd sockets follow in relation to `listen$Type`: ports, IPs, filesystem, etc. where $Type is either Stream, Datagram or Sequential Packets. They're mapped to their respective equivalents in `xinetd`. Currently, only ports are handled.

  `Datagram` and `SequentialPackets` types transfer data over UDP while `Stream` transfers over TCP. For UDP (Datagram and Sequential Packets), a `wait` value of `no` is defined and it is set to `yes` for TCP transports (Stream) as recommended by the manpage (https://linux.die.net/man/5/xinetd.conf). The user is assumed to be root if no user has been defined in the source unit. An additional `disable = no` is required for the service to be enabled. The resulting file is stored as `xinetd.d/service`.

  Note: `xinetd` itself has to be started by OpenRC. With a change in its configuration or service files, it needs to be restarted.

TESTED WITH: rsyncd.socket

Timer Units:
  Since OpenRC itself doesn't have in-built process scheduling, cron is used instead. Debian uses a fork of Vixie Cron that has been patched to stay up-to-date. As integration with Debian is a later topic in my schedule, it makes sense to stay in bounds of what is already installed on Debian. The cron format described by the translator follows that ideology.

  Similar to sockets, every `.timer` unit should have a corresponding `.service` unit. After both of the units are parsed, the systemd timestamp format is converted to the cron format. Currently handled values are calendar events. Thereafter, the `ExecStart` is paired along with the cron timestamp to form the resulting cron file. It is stored as `cron.d/service`.

TESTED WITH: fstrim.timer

Regards,
K Gopal Krishna
(https://kayg.org/)


Reply to: