troglobit / finit

Fast init for Linux. Cookies included
https://troglobit.com/projects/finit/
MIT License
622 stars 61 forks source link

initctl cond reload? #326

Closed hongkongkiwi closed 1 year ago

hongkongkiwi commented 1 year ago

Is it possible to have a command to tell finit to recheck it's conditions?

It can be achieved with initctl reload, but that also sends the reload signal to all processes (which I don't want in this case).

I'm setting some conditions using the init method e.g. ln -s ../../reconf /run/finit/cond/dev/mydev/exist in an mdev script, however I find sometimes (and it seems to be a race condition because it doesn't always happen), finit seems unaware of these conditions that are set. It seems a bit random when this happens.

An initctl reload fixes the issue.

So I was hoping to get a command, for example initctl cond reload which simply just rechecks finit's conditions. I'll run this each time the mdev script is run to ensure that finit is aware of the conditions when they are set.

hongkongkiwi commented 1 year ago

I suppose this is the case because they are init conditions and normally they are not expected to be set at other points.

What about a better solution, rather than a reload is to add a flag to initctl cond set which allows any kind of conditions. For example:

initctl cond -i set 'dev/blah/exist'
initctl cond -i clear 'dev/blah/exist'

-i or --init means that it doesn't assume a usr condition and instead specifically allows setting "a/b/c" conditions. After a condition is set or cleared, it reloads the conditions internally.

Then we don't need to manually symlink and can instead use initctl cond -i set .... so that conditions are picked up immediately.

troglobit commented 1 year ago

The short answer is; no, custom conditions like yours can only be rechecked by initctl reload, but read on.

Long answer:

  1. the condition engine requires plugins today to act as interpreters. For instance the usr.so plugin has an inotify watcher for /run/finit/cond/usr/ (one level, not recursively) and has a semantic that's the closest to your use-case. As you probably know, I'm working on #185, which has taken a bit of time to get right (need to restart that job because I got stuck with a messy implementation). It will do what you want (support two levels, recursively).
  2. An initctl reload does not send reload signal to all services. It tells finit to check both service configuration files for modifications since last reload, and changes to conditions. Finit tracks /etc/finit.conf and /etc/finit.d/* for changes using inotify, since we cannot rely on time being correct we only track that we've seen events since last time. Only services that have had their foo.conf file changed/added/removed are affected. So for your case I believe initctl reload is sufficient.
hongkongkiwi commented 1 year ago

Okay, got it, so I've made a wrapper script, I think I'll just add an initctl -q reload here should help.

I think I misunderstood, I thought that initctl reload sends a SIGHUP to all running services and initctl reload sends a sighup to that specific service.

So your saying that actually initctl reload only asks finit to reload it's service state and not all services right?

troglobit commented 1 year ago

Correct. Basically initctl reload asks finit to check by running through the big state machine:

  1. Any services removed since last time? => Stop and remove them.
  2. Any services modified since last time? => Schedule reload or restart (after 1)
  3. Any new services added since last time? => Schedule start of them (along with 2)
  4. Wait for old services to stop (may disturb 2 and 3 otherwise)
  5. Start, restart, or reload services and in the process check their conditions.

In step 5 we also check any services since last time that were pending a condition, if the condition now has been asserted we start it along with the others.

hongkongkiwi commented 1 year ago

Thanks make sense, looking forward to /dev support when it's ready. In the meantime, I'll just share my simple shell wrapper incase anybody else reads this.

P.S. This allows us to set multiple conditions at once, I'm not sure if that's useful but I feel it might be a nice addition to initctl cond set & initctl cond clear.

finit-set-cond a/b/c d/e/f

#!/bin/sh -u
set_finit_cond() {
  case "${1-}" in
    *'/'*'/'*) ;;
    *) echo >&2 "ERROR: invalid condition '${1-}' must be in format A/B/C"; return 1;;
  esac
  mkdir -p "/run/finit/cond/${1%/*}"
  [ ! -L "/run/finit/cond/${1-}" ] && ln -s "../../reconf" "/run/finit/cond/${1-}"
  return 0
}
[ $# -gt 0 ] || { echo >&2 "ERROR: must pass condition to set"; exit 255; }
RELOAD=""
while [ $# -gt 0 ]; do
  set_finit_cond "${1-}"; RELOAD=1; shift
done
[ -n "$RELOAD" ] && nohup initctl -q reload >/dev/null 2>&1 &
exit 0

finit-clear-cond a/b/c d/e/f

#!/bin/sh -u
clear_finit_cond() {
  case "${1-}" in
    *'/'*'/'*) ;;
    *) echo >&2 "ERROR: invalid condition '${1-}' must be in format A/B/C"; return 1;;
  esac
  [ -L "/run/finit/cond/${1-}" ] && rm -Rf "/run/finit/cond/${1-}"
  return 0
}
[ $# -gt 0 ] || { echo >&2 "ERROR: must pass condition to clear"; exit 255; }
RELOAD=""
while [ $# -gt 0 ]; do
  clear_finit_cond "${1-}"; RELOAD=1; shift
done
[ -n "$RELOAD" ] && nohup initctl -q reload >/dev/null 2>&1 &
exit 0
troglobit commented 1 year ago

Thanks, and great idea btw! Created the #329