- Package:
- runit-services
- Source:
- runit-services
- Submitter:
- Lorenzo Puliti
- Date:
- 2026-01-31 16:31:15 UTC
- Severity:
- normal
- Tags:
(adding Andrew in CC since he sent the MRs that are blocked) There are two MR on Salsa gerbera https://salsa.debian.org/debian/runit-services/-/merge_requests/4 xl2tpd https://salsa.debian.org/debian/runit-services/-/merge_requests/9 that are blocked because we are not able to test for network: the proper solution is to add oneshot capability to runit package, but this requires some design and I'm not sure it will be ready before forky, and blocked MR are already 1 year old. In order to unblock MRs in a reasonable time frame I propose to termporarily add two standard services (to be replaced by oneshots in future) to test network in the check file and wait forever in the run file. I think those services should meet the following conditions: * name should not match any existing sysv script to avoid the shadowing logic that runs at startup and during runtime; name could be 'loopback-is-up' and 'network-is-up' * script are only for testing the network, they do not bring the network up in stage 2. * they shuould work in non-linux port, so if 'ip' is used but is not available in the platform, fallback on ifconfig 'loopback-is-up' and 'network-is-up' I'll send some draft here as soon as I have something. Lorenzo
initial version of the service is here
(loopback probably is not useful)
$ cat /etc/sv/network-is-up/run
#!/bin/sh
#Copyright: 2025 Lorenzo Puliti <plorenzo@disroot.org>
# 2025 Andrew Bower <andrew@bower.uk>
#License: CC0-1.0
exec chpst -L /run/runit/${PWD##*/}.lock chpst -l \
/run/runit/${PWD##*/}.lock true
-----------------------------------------------------
$ cat /etc/sv/network-is-up/check
#!/bin/sh
if [ -x /usr/sbin/ip ]; then
for interf in $(ip addr | grep -Po '^\d+:\s+\K[^:]+') ; do
[ "$interf" = 'Iface' ] && continue
[ "$interf" = 'lo' ] && continue
echo "$interf" | grep docker >/dev/null && continue
echo "$interf" | grep incusbr >/dev/null && continue
echo "$interf" | grep lxcbr >/dev/null && continue
ip link show enp0s31f6 | grep 'UP' >/dev/null && exit 0
# if [ -x /usr/sbin/iwconfig ]; then ...
done
exit 1
elif [ -x /usr/sbin/ifconfig ]; then
for interf in $(ifconfig -s | cut -f1 -d' ') ; do
[ "$interf" = 'Iface' ] && continue
[ "$interf" = 'lo' ] && continue
echo "$interf" | grep docker >/dev/null && continue
echo "$interf" | grep incusbr >/dev/null && continue
echo "$interf" | grep lxcbr >/dev/null && continue
ifconfig "$interf" |grep 'inet' >/dev/null && exit 0
# if [ -x /usr/sbin/iwconfig ]; then ...
done
exit 1
else
#no tools to detect, fail the check
exit 1
fi
----------------------------------
$ cat /etc/sv/network-is-up/finish
#!/bin/sh
chpst -l /run/runit/network-is-up.lock rm /run/runit/network-is-up.lock
-----------------------------
yes the check file can be improved, however this one should be fine for
testing
Lorenzo
Hi Lorenzo, Thanks for adding this support to unblock my service directory contributions! I don't see a problem in principle with using a new service directory as a dependency for services that require networking, but I think there's a better way to the extent that I think we should not do a service directory. Let's remember that there is nothing philosophical or practical about runit that means a service directory's dependency needs to be another service, rather than another type of check that can be performed. A lot of initscripts and systemd units historically claim dependence on 'networking' which are not strictly necessary, probably added as a form of 'cargo culting' and, as the systemd documentation correctly points out [1], there is no single right answer as to what counts as the network being ready. In my contributed service definitions I have entirely omitted network checks that are not needed at all, even if $network was present in the initscript. Instead, I have found out what the real underlying requirement is and implemented that as minimally as possible. If we take the examples of my two pending runit-services merge requests: 1. gerbera - requires global unicast IPv4 address. In this case (quite wrongly in my opinion, via libupnp) the server will not start properly without a global unicast IPv4 address. Neither a global unicast IPv6 address nor a link-local unicast IPv4 address would be sufficient, even though they should be. But actual conectivity is not important. So we can test for this with one line which checks specifically for global unicast IPv4 addresses. [ -n "$(ip -f inet addr show scope global)" ] || exit 1 I don't think a runsv supervisor is necessary or beneficial for this. If it actually caused the network to be configured when it wasn't, like 'default-syslog' does for logging, maybe it would have some merit. 2. xl2tpd - requires a non-loopback address to bind to. We can test for this by looking for any address with wider scope than loopback. If we do this numerically, we can catch scopes from unexpected types of addressing family (in principle), so with (decreasing) scopes going up numerically to 255 and loopback being 254: ip -oneline -Numeric addr show | grep -Evq ' scope 25[45]' || exit 1 So how can we do this generally? First it is important not to look for things which could give us false information, hence porcelain views of output data (like -oneline and -Numeric help to give us) and certainly not checking interface type names or especially not actual interface names. We can't have one size fits all or we end up limiting services unnecessarily. What if we needed xl2tpd to provide a tunneling service so that gerbera could start (OK, not that exact situation because gerbera needs IPv4, but you get the idea!)? So if you need multiple checks you might end up with multiple fake services like network-is-up, each with their own runsv. It doesn't really scale. There may be other services with more stringent requirements. I think the answer is to have a library of shell functions we can call to check things. You could put this in invoke-run but I wonder if that is getting a bit full. So maybe another file, either sourced from invoke-run or directly from the run script. Something like: #... . /lib/runit/network-checks have_network_address scope=global family=inet || exit 1 #... The implementation of have_network_address() could map scope=X onto a scope number first, if specified, loop through supplied addresses and discard any that are numerically higher than num(X). It can then discard any that don't match the family=Y key if specified. Then return true if there are any addresses left. You could have an alternate implementation for Hurd. I wouldn't bother with ifconfig on Linux - net-utils won't be present if iptroute2 is. On Linux you could fall back to /proc/net/dev; I don't know if Hurd has something equivalent. 'ip -oneline' helps you do more robust parsing. Andrew [1] https://systemd.io/NETWORK_ONLINE/
Hi Andrew, thanks for your comprehensive reply yeah, I agree except the advantage of using a service is that sv check waits up to x seconds before giving up. I want to implement this for oneshots checks, without a runit service I'll have to implement this too. I'm aware checks that are in the MR are more accurate, but I hoped that a less accurate and more generic check could be used: I see that probably this idea does not work. ok I'm going to try this, but I have few questions: * why map scope= to a number, if I can filter for the word? do we need to filter for something within each scope (and it can be done with the numeric mapping)? * what to do if scope= and or family= are not passed to the command? just fail or have some default for both? * what if ip (and or ifconfig) are not found in the system? is it safe to assume that network is not configured and fail in this case? Best, Lorenzo
Hi Lorenzo, /usr/bin/timeout comes from coreutils - is that good enough? sorry I've not thought about it from this angle so need to think some more - thought I'd get a reply in to answer your questions below though. The reason was that I was thinking the scope runs theoretically between 0 and 255 and although most aren't used now in any situations I am aware of, they could be, and we should be agnostic to this, so you'd say you need something of at least a certain scope, which works better with a number. It's probably excessive. It might be worth us trying out an example implementation and seeing if it looks sensible or silly. No family would mean any family; missing scope I'm not sure, I think possibly >=link-local or >=global. I'm not particularly wedded to those arguments, by the way, I just came up with it as an example of the sort of thing we could have. That's a good question. Needs further thought. Might be worth noting we're probably doing more than the initscripts would here - they would just wait until networkmanager/dhclient/networking or whatever services have been started and possibly failed. So maybe we're over thinking this and actually need to wait on default-network instead which can launch those like default-rsyslog does. But I think it would be ugly to have a service launch another service to do this - is there a way of having an alias for whichever network setup service is configured?
Hi, ok will try something this weekend I don't find attractive to play with symlinks and alternatives either.. Recently I was thinking: * define a facility "suffix" in runit; for example '-log' for sysloggers (could be '-net' for network) * rename all services that belong to the group appending the common '-log' suffix to the end of the service name. For example rsyslog-log, socklog-unix-log and so on * in run files of services that require one service of the '-log' group check the service with full path and wildcards, for example 'sv check /etc/service/*-log' but I still have to test if it works as expected.. Lorenzo
On Sat, Oct 18, 2025 at 01:58:35AM +0200, Lorenzo wrote:
On Fri, 17 Oct 2025 23:16:23 +0100
[...]
Leaving aside my talk about ip address checks for the moment, your
wildcard suggestion got me thinking.
This seems like a better idea than forcing services to be named a
certain way:
Any service that wants to provide an LSB-style 'facility' could have an
empty file in its service directory called 'provides-<facility>'. I
have mocked this up like this (and remember ifupdown is down in stage 1):
=== /usr/lib/runit/facilities ===
facility() {
for i in $(find "$SVDIR" -iregex "[^.]*/provides-$1" -printf "%h ")
do
basename $i
done
}
sv_if() {
[ $# -gt 1 ] || return
sv "$@"
}
=== /usr/share/runit/sv.src/network-manager/provides-network ===
=== /usr/share/runit/sv.src/dhclient/provides-network ===
=== /usr/share/runit/sv.src/gerbera/run ===
#!/usr/bin/env /lib/runit/invoke-run
exec 2>&1
. /usr/lib/runit/facilities
sv_if start $(facility network) || exit 1
exec chpst -u gerbera:gerbera ##bin## -c /etc/gerbera/config.xml
Hi Andrew, I've just force pushed to git https://salsa.debian.org/debian/runit-services/-/blob/next/debian/extra/netcheck?ref_type=heads call it as (example) /usr/lib/runit-services/netcheck scope=link family=inet scope and family can be omitted (default scope = link, family = any); accepted values are scope=global|link|host; family=inet|inet6. There are issues with ifconfig as especially the scope does not match with ip output; I think ifconfig only provides the scope of the inet6, so for example my interface is of scope=link (instead of scope=global). Wireless is not tested, and still TODO if ip is not installed. Other than that it seems to work. Let me know how it works if you have the chance to test it with gerbera or xl2tpd. I don't find using an include particularly appealing (it feels like sysvinit scripts, I prefer executables and return codes), but anyway I would leave the facilities discussion for another bug; network can be up even if there is no service like network-manager or dhclient, and network-manager or dhclient can fail to bring the network in the desired state even when they are up and running. So I don't think a facility (regardless how it is implemented) is the solution for this bug. Best, Lorenzo
Hi Lorenzo, This is just a courtesy response to apologise for being slow to evaluate and reply. I have some thoughts on this but need to dedicate some attention to it without getting distracted! In some ways there are no easy answers because no one, with any supervision system, seems to have really perfectly solved this problem! Andrew
Control: tags -1 +moreinfo Ok, let's put this on hold for a while. I'm attaching here the last version of the git patch for reference. Lorenzo
Hi Lorenzo, Thanks for your patience! I am uncomfortable with this script as it seems heavy and non-generic for the purposes required. I did wonder, considering how it is going to be used, if, going back to a runit service, it might as well listen on the netlink socket continuously and let the 'check' script do the work of interpreting current state, using a combination of rtmon(8) and ip-monitor(8). However, I tried prototyping that solution a while ago and one problem was that you couldn't get a dump of one of the needed tables (can't remember if it was 'link' or 'address') at start of day to avoid a possible race. There may have been other potential issues, too. Another approach might be to supply hook scripts for the known network configuration managers that raise a common flag file under /run somewhere - this is cruder than what I was trying to achieve with only depending on the minimum service level but it's probably what most systemd services do anyway. Yet another approach, going back to the facilities (we really should have another bug for that, shouldn't we? My latest idea for that was under #1121617, addressing your preference for a process not a shell include) idea would be that each of the services that implement a network facility could install their own hook script and their own check script would check the flag raised by that. We'd need one to cover ifupdown of course, although we'd still want the actual config done in stage 1 I think? Some specific review comments on the script below: Why constrain the names and not just let them match or not naturally? I wonder if it would be nicer to scan /proc/net/dev or something to pick up interface names. Why do we need to check link up and does this do the right thing for logical interfaces that might have non-obvious states? I think that for the applications which really do depend on network readiness, usually it's just to be able to bind to the right addresses and interfaces, rather than being able to pass traffic, in order to start up, although that may not be true for all, of course. In my examples only binding was necessary. I don't like the number of times we are invoking iproute2. As well as starting the tool, each time we are setting up a netlink socket and scanning a lot of the same information, discarding a different amount each time. I would prefer to capture once and then process. (This script might be cleaner written as perl, which is essential anyway, as that is way better for processing inputs.) A shame to need to have the second method but I accept you want a non-Linux solution. Why do we need a wireless section? This script should be agnostic to the type of physical interface. Sorry again for my tardiness and I hope you don't mind my doubts! Andrew