Discussion:
RFE and DISCUSSION: Custom commands in systemd services
(too old to reply)
Introoter
2017-08-31 19:16:55 UTC
Permalink
Raw Message
Hello,
As discussed in https://github.com/systemd/systemd/issues/6690, there's a need to add an option to the specific systemd services to be able to execute arbitrary actions in the context of that particular service (think service postgres initdb). Although, I do know that the current way to get this working isn't too bad either, my making `foobar-action.service` like oneshot units to do custom actions and split it into unit files, as these are the basic constituent logic units that participate in the dependency graph. Though, there are times when a service particularly needs many actions which may After reflecting enough on the idea of units, I have something else to add. (I cannot post to the mailing list because none of my email providers have a way to send plain text mails, though this will be my last comment here, and probably the closing one before this is taken to the mailing list for discussion).

THE CURRENT SITUATION:
systemctl mainly conforms to the LSB standard for service actions, and is not supposed to support custom actions. Though, again, nothing stops it from separating the standard service actions namespace from the generic actions namespace.

The way systemd sets up dependency graphs, they're mainly done as dependencies between units, participating in a boot transaction graph. Thus, adding custom actions would mean there should be a way to depend on these custom defined additions, which really does not fit into the working unit model which is a really superior way of handling things. The boot is divided into these logical units. The better way to do this is to have a foobar-action.service to the desired action as a oneshot, but there still is a way to create these custom actions as something that have dependencies in the unit itself as I showed earlier, which aptly fits the use case where the action is too weak to lie in a separate unit (again, very specific to the requirement). This model is not perfect and I'll be happy to receive feedback on the ML. I'm also aware that this will a lot of changes if we are to add them in the units, as there would be change in the parsing engine, and the transaction and dependency graph generator. After reflecting enough, it is also possible that the [Action] may not quite fit the requirements of the custom action, for example, if it depends on external units (which would lie outside of the dependency scope of the block), in which case, a separate unit makes more sense.

As an immediate measure, you can use separate units to do your graceful-restarts and logrotate and maybe add them as dependencies if required, or configure your controlling client (or write a small script) to call these units on the desired action, which is very neat tbh.

The current proposal needs some more polishing and *time*need to rely on a certain action already strongly typed in the service like ExecStart= or ExecStop= (or there Pre and Post) or ExecReload, or even before the stop+start logic of restart (in simple words). Here, I am putting up some ideas I had about the basic prototype of something that fits this particular need in services.

WHAT I HAVE IN MIND:
Something along the lines of systemctl action=logrotate foobar.service (actually, the action could lie after the service name so the bash/zsh completion script could parse the unit and provide suggestions), and something like Name= and then Exec=.... (and maybe still have the ExecPre=... and ExecPost=... options for the Exec= action) in the service files, with these custom commands in their own context block, like [Action] to be able to depend on the generic ExecStart=, ExecStop=, ExecReload= commands, because then the reopening of log files can be subjected with a restart or reload or w/e. (When forming dependencies with a certain action, that action itself can have a parameter action to call the custom command.) The order of execution will be defined by the Before= or After= dependency of the command on the generic action.

Example for doing a job:

[Unit]
...

[Service]
...
ExecStart=...
ExecStop=...
ExecReload=...
...

[Action]
Name=logrotate
# queue it before ExecStartPre or ExecStartPost, or After, as required.
Before=ExecStart
# Pre post for the Exec= directive
ExecPre=...
Exec=...
ExecPost=..

and then,

systemctl action=logrotate foobar.service
or
systemctl start action=logrotate foobar.service # would mean the same thing

Support could also be added to queue them as dependencies when being invoked from the command line when a dependency isn't specified in the unit or cannot be.

systemctl restart before-action=logrotate foobar.service
or
systemctl restart after-action=logrotate foobar.service

This though does have some use cases, there are times when the custom action doesn't have to be queued before a generic action, and therefore it could be something like this

[Unit]
...

[Service]
...

[Action]
Name=jobfoo
Exec=...
...
and then,
systemctl action=jobfoo foobar.service

We have the Exec= directive free, and it would make total sense to put it in a ambiguous block and be used as required for blocks defining custom actions.

The Exec= thing being synchronous or asynchronous can be handled by the command/script/binary being invoked.

As everyone must have noticed, this is something more complex to fit into the current Unit specification scheme, but it is possible to fit in quite well, and would really make the simple .ini model of the service files to be more powerful. The proposal probably is not a very suitable scheme of integrating custom commands into the unit, and may have a lot of deficiencies, suggestions welcome. This really needs to be thought out well and be consistent with the current unit file specification.

Also, right, the user should really not be able to redefine what restart (i.e. start + stop) does, the behavior is correct.

All in all, custom commands do have a very strong use case, but they mustn't lie in the generic namespace, instead be invoked with a parameter to an argument instead. The issue can still be solved by making separate units for custom actions, but, adding support to define custom service actions will really be a plus and will be clean. Again, as proposed earlier, you could divide the naming namespaces into two sections, the generic namespace and unit specific namespace (which can be invoked with something like action). Although trivial, there could be a file to define custom global actions that can be used in all unit files created by the admin.

The idea really needs more refinement, polishing, and lots of time to make it into systemd and be usable, but I was just hoping if there was any interest in such a scheme for custom actions. I'd be happy to provide and receive more feedback and discussion on this.

Regards,
Introoter
<***@protonmail.com>
----------------------------------------------------------------------------------------------------------
Loading...