[debian-runit] Re: Runit-init support in 'init-system-helpers'

  • From: Lorenz <lorenzo.ru.g@xxxxxxxxx>
  • To: debian-runit@xxxxxxxxxxxxx
  • Date: Fri, 26 Apr 2019 00:08:02 +0200

Sorry for the very long delay, i'm short on free time :(

Yes. Shipping 'down' file is nice idea, I like it. But it does not need
'runit-policy' layer.

The are a few (but relevant) services that does not support being restarted
on upgrade: dbus, Slim, Sddm, Lightdm ..
Those are a problem for us, as the first time we introduce runit support we
can't replace the sysv runing service with runit's service (the graphic
session
will crash otherwise).
My idea was the following (example with dbus):
  - ship dbus service with a down file
  - enable in postinst with update-rc.d
  - than do (with dh_runit):    sv d dbus && rm /etc/sv/dbus/down
  - dbus will run as runit's service after a system reboot

Sadly this will not work ( #919296 ); as this was the main reason for
shipping down files, i will put the down file idea on hold for now.
I have another idea: use invoke-run interpreter to achieve the same result.
something like
#!/usr/bin/env /lib/runit/invoke-run -d
with '-d' that means
  * do not stop the sysv service
  * if there is already an instance of the service running, do
        sv d <service>
I will send a patch.

I challenge this assumption. I believe native runit tristate is much
more logical -- service is either:

* disabled
* temporary stopped (down file)
* running

Ok, but i have a problem: disabled means that no symlink exists at all.
When i do 'update-rc.d foo defaults' the code should enable the service
unless
the local admin decides that he/she wants that service disabled.
I think that more than one check is needed to test the local admin
preference in
dh_runit; also the choice of disabling a service by local admin should be
preserved
on service removal (unless purged).
Or maybe i got this part wrong ( local admin choice prevailing over
defaults)?

I read again the runsvdir man page, it says that directories starting
with dot are ignored.
So i have a new proposal (a variation of your's)

What is wrong with following logic (module sanity checking)

* invoke-rc.d <service> <action> := do
   check_rc.policy()
   sv <service> <action>
* update-rc.d <service> remove := do
  clean-up symbolic links ; currently done in runit-helper
* update-rc.d <service> defaults|defaults-disable := noop
* update-rc.d <service> enable  := ln -s /etc/sv/<service> /etc/service
* update-rc.d <service> disable := rm -f /etc/sv/<service>

* update-rc.d <service> enable  := ln -s /etc/sv/<service> /etc/service
         or (if .service link already exists)
                      mv /etc/service/.<service> /etc/service/<service>
* update-rc.d <service> disable := ln -s /etc/sv/<service>
/etc/service/.<service>
         or (if service link already exists)
                       mv /etc/service/<service> /etc/service/.<service>

* update-rc.d <service> defaults := do
      check for existence of .<service>,  if not
      ln -s /etc/sv/<service> /etc/service

* update-rc.d <service> defaults-disable := do
      check for existence of <service>,  if not
      ln -s /etc/sv/<service> /etc/service/.<service>

No need for runit-policy script.

== 72a3fa5 Add support for runit into service script
.......
+if [ -f /run/runit.stopit ] && [ -d "${RUNDIR}/${SERVICE}" ]; then
+   if update-service --check "${SERVICE}"; then
+      exec sv "${ACTION}" "${SERVICE}"
+   else
+      echo "Can't perform ${ACTION}, ${SERVICE} is disabled"
+      exit 1

I'd use [ test -e /etc/sv/${SERVICE} ] directly. Imho, it is much
simplier to understand. I needed to check manual for update-service(8)
to understand this code.

I'm not sure I understand: you don't want me to use 'update-service' or
it's something else? Did you mean[ test -e /etc/service/$ {SERVICE} ] ?

== 8079d0e Add support for runit into invoke-rc.d script

+            elif [ -f /run/runit.stopit ] && [ -d
"/etc/sv/${INITSCRIPTID}" ]; then
+            # fallback on sysv if there is no runit service for
${INITSCRIPTID}
+            # "$@" is always discarded unless is --force-sysv
+                if [ "x$@" = "x--force-sysv" ] && [ "${saction}" =
"stop" ]; then

"x$@" feels wrong, since it can expand to multiple words. Maybe you
meant "x$1" or "x$*"?

My bad; anyway i think i can remove this '--force-sysv' option since
invoke-run interpreter is already capable of doing that.

+                    "${INITDPREFIX}${INITSCRIPTID}" "${saction}" &&
exit 0
+                fi
+                case $saction in
+                    start|restart|try-restart|reload|force-reload)
+                        #No-Op if the service is not enabled
+                        update-service --check "${INITSCRIPTID}" ||
exit 0
+                        #No-Op is the service wanted status is 'down'
+                        [ -f "/etc/sv/${INITSCRIPTID}/down" ] && exit 0
+                        sv "${saction}" "${INITSCRIPTID}" && exit 0

Why `|| exit 0'?

dh_installinit ( --no-enable) create a postinstall script that roughly does

update-rc.d <service> defaults-disabled >/dev/null
invoke-rc.d <service> start || exit 1

I'm not sure if such case exists, a maintainer probably will use
--no-enable combined with --no-start, but it's not mandatory.
 In case of only --no-enable, exiting non zero will break the installation
at postinstall stage.
What do you suggest?

Lorenz












Il giorno dom 17 mar 2019 alle ore 12:14 Dmitry Bogatov <KAction@xxxxxxxxxx>
ha scritto:


[2019-03-13 18:43] Lorenz <lorenzo.ru.g@xxxxxxxxx>

Mmm, I should have spent more time in detailing my proposal: I think
if you look at the code it's quite simple to understand what i'm doing
but it's maybe more complex to understand why i'm doing this or that
thing.  Lets try with more details:

It is easy understand what are you doing, but I failed to understand why.
Now I got better idea, thank you.

Wait. I believe that is what rc.policy for. Am I wrong?

I believe that rc.policy is for some extra policy layer; but there is
also an implicit policy layer applied in both 'invoke-rc.d' and
'update-rc.d':

what i'm trying to do with 'runit-policy' is to port that implicit layer
into runit.
The implicit layer does something like this:
    * invoke-rc.d start|restart ... it's a No-Op if the service is
disabled; also
    * update-rc.d foo defaults|default-disabled it's a No-Op if the
service
is
       already disabled|enabled
Why do I need a dedicated script for that?

In Sysv i can tell if a serivce foo is disabled looking for a 'KNNfoo'
symlink;
in runit when the service is disabled, there is no link, so how can i
tell
if it was a local
admin decision or not?
I would have to combine the absence of a link with a dpkg command like
to check if it's a new install or an upgrade, and then i have to use a
runit dedicated dh sequence..

Why do you need it? Maybe my image of world is totally inadequate, but I
see only one usage of all those init-system-helpers: to abstract
differences of multiple init systems from maintainer scripts.

What is wrong with following logic (module sanity checking)

 * invoke-rc.d <service> <action> := do
     check_rc.policy()
     sv <service> <action>
 * update-rc.d <service> remove := do
    clean-up symbolic links ; currently done in runit-helper
 * update-rc.d <service> defaults|defaults-disable := noop
 * update-rc.d <service> enable  := ln -s /etc/sv/<service> /etc/service
 * update-rc.d <service> disable := rm -f /etc/sv/<service>

No. By default, services are started after installation.

Yes, i'm not changing that!
the reason why i want to ship service directory with a down file
is that, inside a postinst script, i want that 'update-rc.d' to enable
the
service without starting it, and only then, invoke-rc.d will remove the
down file and start or restart the service.
This contorted way allow us to reuse the dh_installinit postinstall as it
is with the 'default' behaviour, and with really small change also the
'default-disabled' sequence and the 'no-stop-on-upgrade' sequence.
This way we don't care of what code the maintainer insert between
update-rc.d and invoke-rc.d: that code may give for granted that the
service is enabled but not yet started|restarted..
I will write some sequence examples in another thread so that it's
easy to understand what i mean.

Yes. Shipping 'down' file is nice idea, I like it. But it does not need
'runit-policy' layer.

I do not understand. If service is disabled, /etc/service/{foo} is not
present, so you can't `sv up {foo}'. Why something else?

I have to account also for the down file: invoke-rc.d is executed in
maintscripts, so if a local admin declares that he/she doesn't want
a service to be automatically started ( update-service --noauto),
than it's not correct to start that service with invoke-rc.d.

Given that in `runit-policy' there is four files
<service>-{en,dis,up,down},
I believe you want to say, that service could be in four different
states [(x, y) ; x <- {en, dis}, y <- {up, down}].  So if
I understood you correctly this time, the whole `runit-policy` required
to make those 4 states distinguishable.

I challenge this assumption. I believe native runit tristate is much
more logical -- service is either:

 * disabled
 * temporary stopped (down file)
 * running

PS. Did not look at code.

Let apart the change i proposed for runit and focus on
init-system-helpers:
please, as you have some time, have a look at the code, starting with
'runit-policy', then 'update-rc.d' and 'invoke-rc.d'.
If you are busy with Buster release, there is no rush, but be sure you
really want to reject this design before uploading new version of
dh-runit and runit-helper to experimental, because otherwise we are
duplicating efforts.

dh_runit=2.8.9 in experimental with very minor fix, and I do not expect
(unless bugs discovered) make changes anytimes soon. I do not foresee
collisions here.

There is social issue. Every change to `init-system-helpers' is quite
slow and painful, at least for me.

Yes i will interact with init-system-helpers maintainers untill they
accept
runit support, even if this require months of pressing, but first i need
to
be able to claim that you are ok with the changes i'm proposing, and for
now you are not

Wonderful.

Commends regarding commits:

== a649bb2 Add runit-policy script
== ea4efba debian: install runit-policy in /usr/sbin
== 5142278 debian: install policy runit directory
== 9614a7d Add the manpage for runit-policy

 As I wrote above, I challenge the very existence of this script. I will
 not mention it in comments to rest of commits.

== 72a3fa5 Add support for runit into service script

Good, very good. Some minor tweak:

+#Runit-init
+# Only perform ${ACTION} when there is a runscript for
+# the service and ${SERVICE} is enabled.
+# If the service is disabled print a message
+# and do not perform ${ACTION}
+# If there is no runscript at all fallback on sysv.
+if [ -f /run/runit.stopit ] && [ -d "${RUNDIR}/${SERVICE}" ]; then
+   if update-service --check "${SERVICE}"; then
+      exec sv "${ACTION}" "${SERVICE}"
+   else
+      echo "Can't perform ${ACTION}, ${SERVICE} is disabled"
+      exit 1
+   fi
+fi
 update_openrc_started_symlinks
 run_via_sysvinit

I'd use [ test -e /etc/sv/${SERVICE} ] directly. Imho, it is much
simplier to understand. I needed to check manual for update-service(8)
to understand this code.

== 8079d0e Add support for runit into invoke-rc.d script

diff --git a/script/invoke-rc.d b/script/invoke-rc.d
index 27c045e..c6159f4 100755
--- a/script/invoke-rc.d
+++ b/script/invoke-rc.d
@@ -549,6 +549,37 @@ if test x${FORCE} != x || test ${RC} -eq 104 ; then
                 esac
          elif [ -n "$is_openrc" ]; then
              rc-service "${INITSCRIPTID}" "${saction}" && exit 0
+            elif [ -f /run/runit.stopit ] && [ -d
"/etc/sv/${INITSCRIPTID}" ]; then
+            # fallback on sysv if there is no runit service for
${INITSCRIPTID}
+            # "$@" is always discarded unless is --force-sysv
+                if [ "x$@" = "x--force-sysv" ] && [ "${saction}" =
"stop" ]; then

"x$@" feels wrong, since it can expand to multiple words. Maybe you
meant "x$1" or "x$*"?

+                    "${INITDPREFIX}${INITSCRIPTID}" "${saction}" &&
exit 0
+                fi
+                case $saction in
+                    start|restart|try-restart|reload|force-reload)
+                        #No-Op if the service is not enabled
+                        update-service --check "${INITSCRIPTID}" ||
exit 0
+                        #No-Op is the service wanted status is 'down'
+                        [ -f "/etc/sv/${INITSCRIPTID}/down" ] && exit 0
+                        sv "${saction}" "${INITSCRIPTID}" && exit 0

Why `|| exit 0'?

== 7a2a1c5 Add make_runit_links sub to update-rc.d

Good.

== 4ab6659 update-rc.d: add runit to the init sequence

Seems fine.

== 27d2bfb Runit: fix a typo in update-rc.d

Happens :) `git rebase -i` is our friend :)

== 88a329b invoke-rc.d/runit: rm down file on start

Fine.
--
        Note, that I send and fetch email in batch, once every 24 hours.
                 If matter is urgent, try https://t.me/kaction

   --


Other related posts: