From fdb4421e00cc5115cffb55aac79c709a3a5108dd Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 3 Dec 2016 14:06:55 +0100 Subject: [PATCH] README_distro_proposal.txt: writeup about runit adoption Signed-off-by: Denys Vlasenko --- .../var_service/README_distro_proposal.txt | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 examples/var_service/README_distro_proposal.txt diff --git a/examples/var_service/README_distro_proposal.txt b/examples/var_service/README_distro_proposal.txt new file mode 100644 index 000000000..9b3fe04ba --- /dev/null +++ b/examples/var_service/README_distro_proposal.txt @@ -0,0 +1,291 @@ + A distro which already uses runit + +I installed Void Linux, in order to see what do they have. +Xfce desktop looks fairly okay, network is up. +ps tells me they did put X, dbus, NM and udev into runsvdir-supervised tree: + + 1 ? 00:00:01 runit + 623 ? 00:00:00 runsvdir + 629 ? 00:00:00 runsv + 650 tty1 00:00:00 agetty + 630 ? 00:00:00 runsv + 644 ? 00:00:09 NetworkManager + 1737 ? 00:00:00 dhclient + 631 ? 00:00:00 runsv + 639 tty4 00:00:00 agetty + 632 ? 00:00:00 runsv + 640 ? 00:00:00 sshd + 1804 ? 00:00:00 sshd + 1809 pts/3 00:00:00 sh + 1818 pts/3 00:00:00 ps + 633 ? 00:00:00 runsv + 637 tty5 00:00:00 agetty + 634 ? 00:00:00 runsv + 796 ? 00:00:00 dhclient + 635 ? 00:00:00 runsv + 649 ? 00:00:00 uuidd + 636 ? 00:00:00 runsv + 647 ? 00:00:00 acpid + 638 ? 00:00:00 runsv + 652 ? 00:00:00 console-kit-dae + 641 ? 00:00:00 runsv + 651 tty6 00:00:00 agetty + 642 ? 00:00:00 runsv + 660 tty2 00:00:00 agetty + 643 ? 00:00:00 runsv + 657 ? 00:00:02 dbus-daemon + 645 ? 00:00:00 runsv + 658 ? 00:00:00 cgmanager + 648 ? 00:00:00 runsv + 656 tty3 00:00:00 agetty + 653 ? 00:00:00 runsv + 655 ? 00:00:00 lxdm-binary + 698 tty7 00:00:14 Xorg + 729 ? 00:00:00 lxdm-session + 956 ? 00:00:00 sh + 982 ? 00:00:00 xfce4-session + 1006 ? 00:00:04 nm-applet + 654 ? 00:00:00 runsv + 659 ? 00:00:00 udevd + +Here is a link to Vod Linux's wiki: + + https://wiki.voidlinux.eu/Runit + +Void Linux packages install their services as subdirectories of /etc/rc, +such as /etc/sv/sshd, with a script file, "run", and a link +"supervise" -> /run/runit/supervise.sshd + +For sshd, "run" contains: + + #!/bin/sh + ssh-keygen -A >/dev/null 2>&1 # generate host keys if they don't exist + [ -r conf ] && . ./conf + exec /usr/bin/sshd -D $OPTS + +That's it from the POV of the packager. + +This is pretty minimalistic, and yet, it is already distro-specific: +the link to /run/runit/* is conceptually wrong, it requires packagers +to know that /etc/rc should not be mutable and thus they need to use +a different location in filesystem for supervise/ directory. + +I think a good thing would be to require just one file: the "run" script. +The rest should be handled by distro tooling, not by packager. + +A similar issue is arising with logging. It would be ideal if packagers +would not need to know how a particular distro manages logs. +Whatever their daemons print to stdout/stderr, should be automagically logged +in a way distro prefers. + +* * * * * * * * + + Proposed "standard" on how distros should use runit + +The original idea of services-as-directories belongs to D.J.Bernstein (djb), +and his project to implement it is daemontools: https://cr.yp.to/daemontools.html + +There are several reimplementations of daemontools: +- runit: by Gerrit Pape, http://smarden.org/runit/ + (busybox has it included) +- s6: by Laurent Bercot, http://skarnet.org/software/s6/ + + +It is not required that a specific clone should be used. Let evolution work. + + Terminology + +daemon: any long running background program. Common examples are sshd, getty, +ntpd, dhcp client... + +service: same as "daemon" + +service directory: a directory with an executable file (script) named "run" +which (usually) execs daemon (possibly after some preparatory steps). +It should start it not as a child or daemonized process, but by exec'ing it +(inheriting the same PID and the place in the process tree). + +service monitor: a tool which watches a set of service directories. +In daemontools package, it is called "svscan". In runit, it is called +"runsvdir". In s6, it is called "s6-svscan". +Service monitor starts a supervisor for each service directory. +If it dies, it restarts it. If service directory disappears, +service monitor will not be restarted if it dies. +runit's service monitor (runsvdir) sends SIGTERM to supervisors +whose directories disappeared. + +supervisor: a tool which monitors one service directory. +It runs "run" script as its child. It restarts it if it dies. +It can be instructed to start/top/signal its child. +In daemontools package, it is called "supervise". In runit, it is called +"runsv". In s6, it is called "s6-supervise". + +Conceptually, a daemontools clone can be designed such that it does not *have* +the supervisor component: service monitor can directly monitor all its daemons +(for example, this may be a good idea for memory-constrained systems). +However all three existing projects (daemontools/runit/s6) do have a per-service +supervisor process. + +log service: a service which is exclusively tasked with logging +the output of another service. It is implemented as log/ subdirectory +in a service directory. It has the same structure as "normal" +service dirs: it has a "run" script which starts a logging tool. + +If log service exists, stdout of its "main" service is piped +to log service. Stops/restarts of either of them do not sever the pipe +between them. + +If log service exists, daemontools and s6 run a pair of supervisors +(one for the daemon, one for the logger); runit runs only one supervisor +per service, which is handling both of them (presumably this is done +to use fewer processes and thus, fewer resources). + + + User API + +"Users" of service monitoring are authors of software which has daemons. +They need to package their daemons to be installed as services at package +install time. And they need to do this for many distros. +The less distros diverge, the easier users' lives are. + +System-wide service dirs reside in a distro-specific location. +The recommended location is /var/service. (However, since it is not +a mandatory location, avoid depending on it in your run scripts). + +The install location for service dirs is /etc/rc: +when e.g. ntpd daemon is installed, it creates the /etc/rc/ntpd +directory with (minimally) one executable file (script) named "run" +which starts ntpd daemon. It can have other files there. + +At boot, distro should copy /etc/rc/* to a suitable writable +directory (common choice are /var/service, /run/service etc). +It should create log/ directories in each subdirectory +and create "run" files in them with suitable (for this particular distro) +logging tool invocation, unless this directory chose to channel +all logging from all daemons through service monitor process +and log all of them into one file/database/whatever, +in which case log/ directories should not be created. + +It is allowable for a distro to directly use /etc/rc/ as the only +location of its service directories. (For example, +/var/service may be a symlink to /etc/rc). +However, it poses some problems: + +(1) Supervision tools will need to write to subdirectories: +the control of running daemons is implemented via some files and fifos +in automatically created supervise/ subdirectory in each /etc/rc/DIR. + +(2) Creation of a new service can race with the rescanning of /etc/rc/ +by service monitor: service monitor may see a directory with only some files +present. If it attempts to start the service in this state, all sorts +of bad things may happen. This may be worked around by various +heuristics in service monitor which give new service a few seconds +of "grace time" to be fully populated; but this is not yet +implemented in any of three packages. + +Daemons' output file descriptors are handled somewhat awkwardly +by various daemontools implementations. For example, for runit tools, +daemons' stdout goes to wherever runsdir's stdout was directied; +stderr goes to runsvdir, which in turn "rotates" it on its command line +(which is visible in ps output). + +Hopefully this get changed/standardized; while it is not, the "run" file +should start with a + + exec 2>&1 + +command, making stderr equivalent to stdout. +An especially primitive service which does not want its output to be logged +with standard tools can do + + exec >LOGFILE 2>&1 + +or even + + exec >/dev/null 2>&1 + +To prevent creation of distro-specific log/ directory, a service directory +in /etc/rc can contain an empty "log" file. + + + Controlling daemons + +The "svc" tool is available for admins and scripts to control services. +In particular, often one service needs to control another: +e.g. ifplugd can detect that the network cable was just plugged in, +and it needs to (re)start DHCP service for this network device. + +The name of this tool is not standard either, which is an obvious problem. +I propose to fix this by implementing a tool with fixed name and API by all +daemontools clones. Lets use original daemontools name and API. Thus: + +The following form must work: + + svc -udopchaitkx DIR + +Options map to up/down/once/STOP/CONT/HUP/ALRM/INT/TERM/KILL/exit +commands to the daemon being controlled. + +The form with one option letter must work. If multiple-option form +is supported, there is no guarantee in which order they take effect: +svc -it DIR can deliver TERM and INT in any order. + +If more than one DIR can be specified (which is not a requirement), +there is no guarantee in which order commands are sent to them. + +If DIR has no slash and is not "." or "..", it is assumed to be +relative to the system-wide service directory. + +The "svok DIR" tool exits 0 if service is running, and nonzero if not. + +The "svstat DIR1 DIR2..." prints one human-readable line for each directory, +saying whether supervise is successfully running in that directory, +and reporting the status information maintained by supervise. + +Other tools with different names and APIs may exist; however +for portability scripts should use the above tools. + +Creation of a new service on a running system should be done atomically. +To this end, first create and populate a new /etc/rc/DIR. + +Then "activate" it by running ??????? - this copies (or symlinks, +depending on the distro) its files to the "live" service directory, +whereever it is located on this distro. + +Removal of the service should be done as follows: +svc -d DIR [DIR/log], then remove the service directory +(this makes service monitor SIGTERM per-directory supervisors +(if they exist in the implementation)) + + + Implementation details + +Top-level service monitor program name is not standardized. +[svscan, runsvdir, s6-svscan ...] + +It may run one per-directory supervisor, or two supervisors +(one for DIR/ and one for DIR/log/); for memory-constrained systems +an implementation is possible which itself controls all services, without +intermediate supervisors. +[runsvdir runs one "runsv DIR" per DIR, runsv handles DIR/log/ if that exists] +[svscan runs a pair of "superwise DIR" and "superwise DIR/log"] + +Directores are remembered by device+inode numbers, not names. Renaming a directory +does not affect the running service (unless it is renamed to a .dotdir). + +Removal (or .dotdiring) of a directory sends SIGTERM to any running services. + +Standard output of non-logged services goes to standard output of service monitor. +Standard output of logger services goes to standard output of service monitor. +Standard error of them always goes to standard error of service monitor. + +If you want to log standard error of your logged service along with its stdout, use +"exec 2>&1" in the beginning of your "run" script. + +Whether stdout/stderr of service monitor is discarded (>/dev/null) +or logged in some way is system-dependent. + + + Containers + +[What do containers need?] -- 2.25.1