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/)