mirror of
https://github.com/troglobit/finit.git
synced 2026-11-02 13:20:32 +00:00
doc: convert the documentation to the block format
The syntax overview no longer describes a line-based format, since that is not what the rest of the documentation shows. It now covers the grammar, the two naming conventions, the nine aliases, and the leading '-' on a path, and it says plainly that both formats are still read and told apart per file by content. Without that, a reader with an existing configuration is left wondering what happened to it. service-opts.md was a list of modifiers to place between a directive and its command, so it needed rewriting rather than translating: there are no positions left to describe. It is now grouped by what the settings do. conditions.md needed correcting. It presented '!' as a condition prefix alongside '~'. It is neither a condition nor a negation, it is a flag on the block that means one thing on a service and another on a run or task, so it is spelled reload-signal and required here, and the page maps the old form to both. Two things the pages claimed are not true. The kill delay range is 1-300, not 1-60, and stop and reload scripts are no longer run without a timeout. ChangeLog.md keeps its line-based examples. Those sit in historical release entries, and rewriting them in a syntax that did not exist at the time would misdate the format. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
20 files changed
+790
-352
No files matched your search
+160
-70
@@ -19,20 +19,25 @@ the most reliable way to monitor a service.
|
||||
|
||||
However, not all daemons support running in the foreground, or they may
|
||||
start logging to the foreground as well, these are forking daemons and
|
||||
are supported using the same syntax as forking `sysv` services, using
|
||||
the `pid:!/path/to/pidfile.pid` syntax. There is an alternative syntax
|
||||
that may be more intuitive, where Finit can also guess the PID file
|
||||
based on the daemon's command name:
|
||||
are supported using the same syntax as forking `sysv` services, by
|
||||
naming the file to watch with `pidfile`. There is an alternative that
|
||||
may be more intuitive, where Finit can also guess the PID file based on
|
||||
the daemon's command name:
|
||||
|
||||
service type:forking ntpd -- NTP daemon
|
||||
service ntpd {
|
||||
description = "NTP daemon"
|
||||
type = "forking"
|
||||
command = "ntpd"
|
||||
}
|
||||
|
||||
This example lets BusyBox `ntpd` daemonize itself. Finit uses the
|
||||
basename of the binary to guess the PID file to watch for the PID:
|
||||
`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
|
||||
full `pid:!/path/to/file.pid`.
|
||||
`/var/run/ntpd.pid`. If Finit guesses wrong, name the file yourself
|
||||
with `pidfile = "/path/to/file.pid"`.
|
||||
|
||||
With `pid:!/path`, the file belongs to the service: Finit reads it
|
||||
but does not create or remove it. The one exception is *stale*
|
||||
The file belongs to the service: Finit reads it but does not create or
|
||||
remove it. That is the default, and `pidfile-create = true` is what
|
||||
asks Finit to write the file instead. The one exception is *stale*
|
||||
cleanup — if the service dies without removing its own pidfile
|
||||
(SIGKILL, OOM, segfault), and the file still names the just-reaped
|
||||
PID, Finit removes it before the next retry. This prevents daemons
|
||||
@@ -44,56 +49,75 @@ from getting stuck in a crash-restart loop.
|
||||
In the case of `ospfd` (below), we omit the `-d` flag (daemonize) to
|
||||
prevent it from forking to the background:
|
||||
|
||||
service [2345] <pid/zebra> /sbin/ospfd -- OSPF daemon
|
||||
service ospfd {
|
||||
description = "OSPF daemon"
|
||||
runlevel = "2345"
|
||||
conditions = { "pid/zebra" }
|
||||
command = "/sbin/ospfd"
|
||||
}
|
||||
|
||||
`[2345]` denote the runlevels `ospfd` is allowed to run in, they are
|
||||
optional and default to level 2-5 if omitted.
|
||||
|
||||
`<...>` is the condition for starting `ospfd`. In this example Finit
|
||||
`runlevel` denotes the runlevels `ospfd` is allowed to run in, it is
|
||||
optional and defaults to level 2-4 if omitted.
|
||||
|
||||
`conditions` lists what must be asserted before starting `ospfd`. In this example Finit
|
||||
waits for another service, `zebra`, to have created its PID file in
|
||||
`/var/run/quagga/zebra.pid` before starting `ospfd`. Finit watches
|
||||
*all* files in `/var/run`, for each file named `*.pid`, or `*/pid`,
|
||||
Finit opens it and find the matching `NAME:ID` using the PID.
|
||||
|
||||
The condition can be prefixed with `!` and/or `~`:
|
||||
A condition may be prefixed with `~` to propagate a reload of the
|
||||
upstream service to this one, rather than merely pausing and resuming
|
||||
it:
|
||||
|
||||
- `<!pid/zebra>` -- `ospfd` does not support `SIGHUP` (noreload)
|
||||
- `<~pid/zebra>` -- propagate reload from `zebra` to `ospfd`
|
||||
- `<!~pid/zebra>` -- both: noreload and propagate reload
|
||||
conditions = { "~pid/zebra" }
|
||||
|
||||
For details, see the [Finit Conditions](../conditions.md) document.
|
||||
If `ospfd` cannot be reloaded with `SIGHUP` at all, that is a property
|
||||
of `ospfd` and not of the condition, so it is said directly:
|
||||
|
||||
reload-signal = "none"
|
||||
|
||||
The legacy format spells that second one as a `!` leading the condition
|
||||
list, which is not accepted here. For details, see the
|
||||
[Finit Conditions](../conditions.md) document.
|
||||
|
||||
Some services do not maintain a PID file and rather than patching each
|
||||
application Finit provides a workaround. A `pid` keyword can be set
|
||||
to have Finit automatically create (when starting) and later remove
|
||||
(when stopping) the PID file. The file is created in the `/var/run`
|
||||
directory using the `basename(1)` of the service. The default can be
|
||||
modified with an optional `pid:`-argument:
|
||||
application Finit provides a workaround. With `pidfile-create` Finit
|
||||
creates the file when starting and removes it when stopping. The path
|
||||
comes from `pidfile`, which takes three forms:
|
||||
|
||||
pid[:[/path/to/]filename[.pid]]
|
||||
pidfile = true # /var/run/<command basename>.pid
|
||||
pidfile = "bar" # a bare name, /var/run/bar.pid
|
||||
pidfile = "/run/bar.pid" # an explicit path
|
||||
|
||||
For example, by adding `pid:/run/bar.pid` to the service `/sbin/bar`,
|
||||
that PID file will, not only be created and removed automatically, but
|
||||
also be used by the Finit condition subsystem. So a service/run/task
|
||||
can depend on `<pid/bar>`, like this foo will not be started until bar
|
||||
has started:
|
||||
Such a file is also used by the Finit condition subsystem, so another
|
||||
service, run or task can depend on `pid/bar`. Here foo is not started
|
||||
until bar has:
|
||||
|
||||
service pid:/run/bar.pid bar -- Bar Service
|
||||
service <pid/bar> foo -- Foo Service
|
||||
service bar {
|
||||
description = "Bar Service"
|
||||
pidfile = "/run/bar.pid"
|
||||
pidfile-create = true
|
||||
command = "bar"
|
||||
}
|
||||
|
||||
service foo {
|
||||
description = "Foo Service"
|
||||
conditions = { "pid/bar" }
|
||||
command = "foo"
|
||||
}
|
||||
|
||||
Needless to say, it is better if `bar` creates its own PID file when it
|
||||
has completed starting up and is ready for service.
|
||||
|
||||
As an alternative "readiness" notification, Finit supports both systemd
|
||||
and s6 style notification. This can be enabled by using the `notify`
|
||||
option:
|
||||
and s6 style notification. This is enabled with the `notify` key:
|
||||
|
||||
* `notify:systemd` -- tells Finit the service uses the `sd_notify()`
|
||||
* `notify = "systemd"` -- tells Finit the service uses the `sd_notify()`
|
||||
API to signal PID 1 when it has completed its startup and is ready
|
||||
to service events. The [sd_notify()][] API expects `NOTIFY_SOCKET`
|
||||
to be set to the socket where the application can send `"READY=1\n"`
|
||||
when it is starting up or has processed a `SIGHUP`.
|
||||
* `notify:s6` -- puts Finit in s6 compatibility mode. Compared to the
|
||||
* `notify = "s6"` -- puts Finit in s6 compatibility mode. Compared to the
|
||||
systemd notification, [s6 expect][] compliant daemons to send `"\n"`
|
||||
and then close their socket. Finit takes care of "hard-wiring" the
|
||||
READY state as long as the application is running, events across any
|
||||
@@ -101,7 +125,11 @@ option:
|
||||
(must be >3) on then command line, Finit provides the following
|
||||
syntax (`%n` is replaced by Finit with then descriptor number):
|
||||
|
||||
service [S12345789] notify:s6 mdevd -O 4 -D %n
|
||||
service mdevd {
|
||||
runlevel = "S12345789"
|
||||
notify = "s6"
|
||||
command = "mdevd -O 4 -D %n"
|
||||
}
|
||||
|
||||
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
[s6 expect]: https://skarnet.org/software/s6/notifywhenup.html
|
||||
@@ -115,7 +143,13 @@ then service's ready condition which other services can depend on:
|
||||
|
||||
This can be used to synchronize the start of another run/task/service:
|
||||
|
||||
task [S] <service/mdevd/ready> @root:root mdevd-coldplug
|
||||
task mdevd-coldplug {
|
||||
runlevel = "S"
|
||||
conditions = { "service/mdevd/ready" }
|
||||
user = "root"
|
||||
group = "root"
|
||||
command = "mdevd-coldplug"
|
||||
}
|
||||
|
||||
Finit waits for `mdevd` to notify it, before starting `mdevd-coldplug`.
|
||||
Notice how both start in runlevel S, and the coldplug task only runs in
|
||||
@@ -131,20 +165,29 @@ Non-privileged Services
|
||||
-----------------------
|
||||
|
||||
Every `run`, `task`, or `service` can also list the privileges the
|
||||
`/path/to/cmd` should be executed with. Prefix the command with
|
||||
`@USR[:GRP[,SUPP,...]]`, where group and supplementary groups are
|
||||
optional, like this:
|
||||
command should be executed with, using `user`, `group` and
|
||||
`extra-groups`, all optional:
|
||||
|
||||
run [2345] @joe:users logger "Hello world"
|
||||
run hello {
|
||||
runlevel = "2345"
|
||||
user = "joe"
|
||||
group = "users"
|
||||
command = "logger \"Hello world\""
|
||||
}
|
||||
|
||||
Finit reads the user's supplementary group membership from `/etc/group`
|
||||
automatically. Any groups the user belongs to will be inherited by
|
||||
the service.
|
||||
|
||||
To specify additional supplementary groups beyond those in `/etc/group`,
|
||||
append them after the primary group, separated by commas:
|
||||
To specify additional supplementary groups beyond those in
|
||||
`/etc/group`, list them in `extra-groups`:
|
||||
|
||||
service @caddy:caddy,ssl-cert /usr/bin/caddy run
|
||||
service caddy {
|
||||
user = "caddy"
|
||||
group = "caddy"
|
||||
extra-groups = { "ssl-cert" }
|
||||
command = "/usr/bin/caddy run"
|
||||
}
|
||||
|
||||
This runs the `caddy` service as user `caddy`, with primary group
|
||||
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
|
||||
@@ -152,14 +195,22 @@ plus the additional `ssl-cert` group. This is useful when a service
|
||||
needs access to resources owned by groups not listed in `/etc/group`.
|
||||
|
||||
For multiple instances of the same command, e.g. a DHCP client or
|
||||
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
||||
`service` keyword and the command, like this:
|
||||
multiple web servers, add `:ID` to the block title, like this:
|
||||
|
||||
service :80 [2345] httpd -f -h /http -p 80 -- Web server
|
||||
service :8080[2345] httpd -f -h /http -p 8080 -- Old web server
|
||||
service httpd:80 {
|
||||
description = "Web server"
|
||||
runlevel = "2345"
|
||||
command = "httpd -f -h /http -p 80"
|
||||
}
|
||||
|
||||
Without the `:ID` to the service the latter will overwrite the former
|
||||
and only the old web server would be started and supervised.
|
||||
service httpd:8080 {
|
||||
description = "Old web server"
|
||||
runlevel = "2345"
|
||||
command = "httpd -f -h /http -p 8080"
|
||||
}
|
||||
|
||||
Without the `:ID` the latter will overwrite the former and only the old
|
||||
web server would be started and supervised.
|
||||
|
||||
|
||||
Conditional Loading
|
||||
@@ -169,35 +220,58 @@ Finit support conditional loading of stanzas. The following example is
|
||||
take from the `system/hotplug.conf` file in the Finit distribution.
|
||||
Here we only show a simplified subset.
|
||||
|
||||
Starting with the `nowarn` option.
|
||||
Starting with the leading `-` on `command`.
|
||||
|
||||
service nowarn name:udevd pid:udevd /lib/systemd/systemd-udevd
|
||||
service nowarn name:udevd pid:udevd udevd
|
||||
service udevd {
|
||||
pidfile = "udevd"
|
||||
command = "-/lib/systemd/systemd-udevd"
|
||||
}
|
||||
|
||||
When loading the .conf file Finit looks for `/lib/systemd/systemd-udevd`
|
||||
if that is not found Finit automatically logs a warning. The `nowarn`
|
||||
option disables this warning so that the second line can be evaluated,
|
||||
service udevd {
|
||||
pidfile = "udevd"
|
||||
command = "-udevd"
|
||||
}
|
||||
|
||||
When loading the .conf file Finit looks for
|
||||
`/lib/systemd/systemd-udevd`, and if that is not found it logs a
|
||||
warning. The leading `-` says a missing binary is expected here, so
|
||||
the stanza is skipped quietly and the second block can be evaluated,
|
||||
which also provides a service named `udevd`.
|
||||
|
||||
run nowarn if:udevd <pid/udevd> :1 udevadm settle -t 0
|
||||
run udevadm:1 {
|
||||
runlevel = "S"
|
||||
if = "udevd"
|
||||
conditions = { "pid/udevd" }
|
||||
command = "-udevadm settle -t 0"
|
||||
}
|
||||
|
||||
This line is only loaded if we know of a service named `udevd`. Again,
|
||||
we do not warn if `udevadm` is not found, execution will also stop here
|
||||
until the PID condition is asserted, i.e., Finit detecting udevd has
|
||||
started.
|
||||
|
||||
run nowarn conflict:udevd [S] mdev -s -- Populating device tree
|
||||
run mdev {
|
||||
description = "Populating device tree"
|
||||
runlevel = "S"
|
||||
conflicts = { "udevd" }
|
||||
command = "-mdev -s"
|
||||
}
|
||||
|
||||
If `udevd` is not available, we try to run `mdev`, but if that is not
|
||||
found, again we do not warn.
|
||||
|
||||
Conditional loading statements can also be negated, so the previous stanza
|
||||
can also be written as:
|
||||
Conditional loading statements can also be negated, so the previous
|
||||
stanza can also be written as:
|
||||
|
||||
run nowarn if:!udevd [S] mdev -s -- Populating device tree
|
||||
run mdev {
|
||||
description = "Populating device tree"
|
||||
runlevel = "S"
|
||||
if = "!udevd"
|
||||
command = "-mdev -s"
|
||||
}
|
||||
|
||||
The reason for using `conflict` in this example is that a conflict can be
|
||||
resolved. Stanzas marked with `conflict:foo` are rechecked at runtime.
|
||||
resolved. Stanzas naming a conflict are rechecked at runtime.
|
||||
|
||||
|
||||
Conditional Execution
|
||||
@@ -212,21 +286,37 @@ runlevel. E.g., a `task [123]` is qualified to run only in runlevel 1,
|
||||
Conditional execution qualify a run/task/service based on a condition.
|
||||
Consider this (simplified) example from the Infix operating system:
|
||||
|
||||
run [S] name:startup <pid/sysrepo> confd -b --load startup-config
|
||||
run [S] if:<usr/fail-startup> name:failure <pid/sysrepo> confd --load failure-config
|
||||
run startup {
|
||||
runlevel = "S"
|
||||
conditions = { "pid/sysrepo" }
|
||||
command = "confd -b --load startup-config"
|
||||
}
|
||||
|
||||
run failure {
|
||||
runlevel = "S"
|
||||
if = "<usr/fail-startup>"
|
||||
conditions = { "pid/sysrepo" }
|
||||
command = "confd --load failure-config"
|
||||
}
|
||||
|
||||
The two run statements reside in the same .conf file so Finit runs them
|
||||
in true sequence. If loading the file `startup-config` fails confd sets
|
||||
the condition `usr/fail-startup`, thus allowing the next run statement
|
||||
to load `failure-config`.
|
||||
|
||||
Notice the critical difference between the `<pid/sysrepo>` condition and
|
||||
`if:<usr/fail-startup>`. The former is a condition for starting and the
|
||||
latter is a condition to check if a run/task/service is qualified to
|
||||
even be considered.
|
||||
Notice the critical difference between the `conditions` list and `if`.
|
||||
The former is a condition for starting; the latter is a condition to
|
||||
check whether a run/task/service is qualified to even be considered.
|
||||
`if` has a negation of its own, `!`, which is unrelated to anything in
|
||||
the `conditions` list.
|
||||
|
||||
Conditional execution statements can also be negated, so provided the
|
||||
file loaded did the opposite, i.e., set a condition on success, the
|
||||
previous stanza can also be written as:
|
||||
|
||||
run [S] if:<!usr/startup-ok> name:failure <pid/sysrepo> confd ...
|
||||
run failure {
|
||||
runlevel = "S"
|
||||
if = "<!usr/startup-ok>"
|
||||
conditions = { "pid/sysrepo" }
|
||||
command = "confd ..."
|
||||
}
|
||||
Reference in new issue
Block a user