mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 05:22:48 +07:00
Apply a PAM session to run/task/sysv/services Finit starts, pam_limits
above all, so a service running as a given user picks up that user's
limits the way a login does.
Add a new `pam` setting for the new block format (only), like the
per-service directories, naming a file in /etc/pam.d:
service weston {
user = "weston"
pam = "weston-autologin"
command = "/usr/bin/weston --continue-without-input"
}
pam_close_session() has to be called by a process still holding the
handle, and the handle does not survive exec(). Hence the keeper: it
holds the handle, drops to the service's credentials, and waits for a
parent-death signal before closing the session. Same shape as
systemd's (sd-pam), for the same reason, and one per fork, so the
script hooks open and close their own.
The keeper closes the descriptors it inherited from Finit and only
those. Closing everything would also take out what pam_open_session()
opened for itself, a keyring fd or a lock file, and leave the modules
to close a session with those pulled out from under them. Closing
nothing, as (sd-pam) does, would leave it holding the write end of the
notify pipe for the service's whole lifetime and starve notify = "s6"
services of their ready signal. So the fds open before pam_start()
are snapshotted and exactly those are closed, while the ones PAM opens
after are marked close-on-exec so the daemon does not inherit them
either.
A refused value, a denied account stack, an uninstalled pam.d file,
and a build without PAM support all keep the service from starting
rather than running it with the stacks skipped: one that quietly loses
pam_limits and its private /tmp, with nothing said. Capabilities a
module like pam_cap.so granted are merged into the IAB Finit applies
instead of being replaced by it, which only helps a service that also
sets capabilities, the other arm being a plain setuid() with nothing
left to restore once permitted is empty.
The test sysroot gains pam_permit.so, pam_deny.so and pam_limits.so,
which ldd cannot see, libpam dlopen()s them, and the test skips when
the host has none to stage. The negative cases pin the exit status
rather than only asserting crashed, which serv reports for any early
exit, so a bad command or an unwritable pidfile cannot pass for a
rejected session.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
268 lines
12 KiB
Markdown
268 lines
12 KiB
Markdown
Syntax Migration Guide
|
|
======================
|
|
|
|
Finit reads both configuration formats. Every file is still `*.conf`,
|
|
and the format is detected per file, so a system can be migrated one
|
|
file at a time -- a file is one format or the other, never a mix.
|
|
|
|
This guide maps each part of a line-based stanza to its block key.
|
|
|
|
The shape of the change:
|
|
|
|
service [S12345] <pid/syslogd> env:-/etc/default/klogd name:klogd klogd -n $KLOGD_OPTS -- Kernel log daemon
|
|
|
|
becomes
|
|
|
|
service klogd {
|
|
description = "Kernel log daemon"
|
|
runlevel = "S12345"
|
|
conditions = { "pid/syslogd" }
|
|
envfile = "-/etc/default/klogd"
|
|
command = "klogd -n $KLOGD_OPTS"
|
|
}
|
|
|
|
The block title is the service identity, shown by `initctl`. Multiple
|
|
instances spell the ID in the title: `service sshd:1 { ... }`. The
|
|
legacy bare-ID form, `service :80 ...`, has no equivalent -- the title
|
|
carries both name and ID.
|
|
|
|
Positional parts
|
|
----------------
|
|
|
|
| Line-based | Block key |
|
|
|----------------------------------|-------------------------------------------------------|
|
|
| `service`, `task`, `run`, `sysv` | same, as block type |
|
|
| `[S12345]` | `runlevel = "S12345"` |
|
|
| `<pid/a,net/b>` | `conditions = { "pid/a", "net/b" }` |
|
|
| `<!pid/a>` on service/sysv | `conditions = { "pid/a" }` + `reload-signal = "none"` |
|
|
| `<!pid/a>` on run/task | `conditions = { "pid/a" }` + `required = false` |
|
|
| `@user:group,extra` | `user`, `group`, `extra-groups = { "extra" }` |
|
|
| `name:foo :1` | block title `foo:1` |
|
|
| the command and arguments | `command = "..."` |
|
|
| `-- Description text` | `description = "..."` |
|
|
|
|
The `!` was a flag on the stanza, not a negation, and meant different
|
|
things for daemons and one-shots; each meaning is now its own key. A
|
|
`~` prefix on a condition is unchanged: `conditions = { "~pid/a" }`.
|
|
|
|
Service options
|
|
---------------
|
|
|
|
| Line-based | Block key |
|
|
|----------------------------------|--------------------------------------------------|
|
|
| `env:[-]/path` | `envfile = "[-]/path"` |
|
|
| `pid:/path` | `pidfile = "/path"` + `pidfile-create = true` |
|
|
| `pid:!/path` | `pidfile = "/path"` |
|
|
| `pid` | `pidfile = true` + `pidfile-create = true` |
|
|
| `log` | `log { }` |
|
|
| `log:/path` | `log { file = "/path" }` |
|
|
| `log:null`, `log:console` | `log { file = "/dev/null" }`, `"/dev/console"` |
|
|
| `log:prio:p,tag:t` | `log { priority = "p" identity = "t" }` |
|
|
| `notify:systemd` | `notify = "systemd"` |
|
|
| `type:forking` | `type = "forking"` |
|
|
| `manual:yes` | `manual-start = true` |
|
|
| `remain:yes` | `remain-after-exit = true` |
|
|
| `restart:always` / `restart:NUM` | `restart = "always"` / `restart-max = NUM` |
|
|
| `restart_sec:SEC` | `restart-sec = SEC` |
|
|
| `norestart` | `restart = "never"` |
|
|
| `respawn` | `respawn = true` |
|
|
| `oncrash:reboot` | `oncrash = "reboot"` |
|
|
| `halt:SIG` | `stop-signal = "SIG"` |
|
|
| `kill:SEC` | `stop-timeout = SEC` |
|
|
| `pre:[TMO,]/script` | `exec-start-pre`, `exec-start-pre-timeout` |
|
|
| `ready:[TMO,]/script` | `exec-start-ready`, and its `-timeout` |
|
|
| `stop:[TMO,]/script` | `exec-stop`, and its `-timeout` |
|
|
| `post:[TMO,]/script` | `exec-stop-post`, and its `-timeout` |
|
|
| `reload:[TMO,]/script` | `exec-reload`, and its `-timeout` |
|
|
| `cleanup:[TMO,]/script` | `exec-cleanup`, and its `-timeout` |
|
|
| `caps:^cap_a,%cap_b` | `capabilities = { "^cap_a", "%cap_b" }` |
|
|
| `conflict:a,b` | `conflicts = { "a", "b" }` |
|
|
| `if:svc` / `if:<cond>` | `if = "svc"` / `if = "cond"`, no angle brackets |
|
|
| `tty:/dev/x` | `tty = "/dev/x"` |
|
|
| `nowarn` | leading `-` on `command` or `envfile` |
|
|
| `restarttmo:` | dropped, was already an alias for `restart_sec:` |
|
|
|
|
Repeated stanzas
|
|
----------------
|
|
|
|
The line-based format has no titles, so the same service could be
|
|
declared more than once and Finit would sort out which line applied.
|
|
A block title *is* the identity, and two blocks sharing one in the
|
|
same file are rejected. The two shapes that relied on the repetition
|
|
convert differently.
|
|
|
|
**Several candidate binaries for one service.** The daemon is known
|
|
under more than one name and the stanzas were repeated once per name,
|
|
each with `nowarn` so the ones that were not installed were skipped:
|
|
|
|
service nowarn pid:!/run/udevd.pid [S12345789] /lib/systemd/systemd-udevd -- Device event daemon
|
|
service nowarn pid:!/run/udevd.pid [S12345789] udevd -- Device event daemon
|
|
|
|
The candidates now go in one block, and Finit starts the first one it
|
|
finds:
|
|
|
|
service udevd {
|
|
description = "Device event daemon"
|
|
runlevel = "S12345789"
|
|
pidfile = "/run/udevd.pid"
|
|
command = { "/lib/systemd/systemd-udevd", "-udevd" }
|
|
}
|
|
|
|
**One service gated differently per platform.** The command is the
|
|
same every time, only `if:` and the conditions differ, e.g. a syslog
|
|
daemon that has to wait for whichever hotplug daemon the system has:
|
|
|
|
service if:udevd nowarn env:-/etc/default/sysklogd <run/udevadm:5/success> \
|
|
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
|
|
service if:mdev nowarn env:-/etc/default/sysklogd <run/coldplug/success> \
|
|
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
|
|
|
|
All of these are one service, `syslogd`, and everything downstream
|
|
waits for the one `pid/syslogd` barrier it provides. That barrier is
|
|
the reason the repetition existed: the title alone spells it, so
|
|
separate titles would rename it.
|
|
|
|
Give each variant its own title and name the shared barrier with
|
|
`provides`:
|
|
|
|
service syslogd:udev {
|
|
description = "System log daemon"
|
|
runlevel = "S0123456789"
|
|
if = "udevd"
|
|
conditions = { "run/udevadm:5/success" }
|
|
provides = "pid/syslogd"
|
|
envfile = "-/etc/default/sysklogd"
|
|
command = "-syslogd -F $SYSLOGD_ARGS"
|
|
}
|
|
service syslogd:mdev {
|
|
description = "System log daemon"
|
|
runlevel = "S0123456789"
|
|
if = "mdev"
|
|
conditions = { "run/coldplug/success" }
|
|
provides = "pid/syslogd"
|
|
envfile = "-/etc/default/sysklogd"
|
|
command = "-syslogd -F $SYSLOGD_ARGS"
|
|
}
|
|
|
|
Whichever variant `if` qualifies asserts `pid/syslogd` on top of its
|
|
own `pid/syslogd:udev`, so downstream blocks need no change. The `if`
|
|
statements are meant to be mutually exclusive; if two of them do
|
|
qualify, the second claim is refused with a warning and only the first
|
|
variant supplies the barrier. See [Provided
|
|
Conditions](service-opts.md#provided-conditions).
|
|
|
|
`initctl` now knows the variants apart, `initctl status syslogd:udev`,
|
|
and a bare `initctl status syslogd` still lists them all.
|
|
|
|
Splitting the variants across files also works, since the ban on
|
|
duplicate titles is per file, as does leaving that one file in the
|
|
line-based format. Neither is needed for this shape any more.
|
|
|
|
Cgroups
|
|
-------
|
|
|
|
The standalone `cgroup.NAME` line selected a group for every stanza
|
|
after it in the file, and `cgroup:opts` applied settings to whichever
|
|
group was current. Neither survives: every block names its own group,
|
|
so nothing depends on what came earlier in the file.
|
|
|
|
cgroup.maint
|
|
service [2345] cgroup:cpu.weight:250 foo -- Foo daemon
|
|
|
|
becomes
|
|
|
|
service foo {
|
|
description = "Foo daemon"
|
|
runlevel = "2345"
|
|
cgroup maint { cpu.weight = 250 }
|
|
command = "foo"
|
|
}
|
|
|
|
Top-level group declarations keep their name and settings:
|
|
|
|
cgroup system cpu.weight:9700 mem.max:10M
|
|
|
|
becomes
|
|
|
|
cgroup system {
|
|
cpu.weight = 9700
|
|
memory.max = 10M
|
|
}
|
|
|
|
TTYs
|
|
----
|
|
|
|
The three positional variants become three key choices:
|
|
|
|
tty [12345] /dev/ttyS0 115200 noclear vt220
|
|
tty [12345] /sbin/getty -L ttyS0 115200 vt100
|
|
tty [12345] notty
|
|
|
|
become
|
|
|
|
tty ttyS0 {
|
|
runlevel = "12345"
|
|
device = "/dev/ttyS0"
|
|
baud = 115200
|
|
term = "vt220"
|
|
noclear = true
|
|
}
|
|
tty getty {
|
|
runlevel = "12345"
|
|
command = "/sbin/getty -L ttyS0 115200 vt100"
|
|
}
|
|
tty shell {
|
|
runlevel = "12345"
|
|
notty = true
|
|
}
|
|
|
|
The bare flags keep their names as booleans: `noclear`, `nowait`,
|
|
`nologin`, `passenv`, `rescue`, e.g. `passenv` becomes
|
|
`passenv = true`.
|
|
|
|
A `tty` block is named by the device, not the title, so all three above
|
|
show up as `tty:ttyS0` and the like in `initctl`. The title still has
|
|
to be unique within the file.
|
|
|
|
Top-level directives
|
|
--------------------
|
|
|
|
| Line-based | Block key |
|
|
|----------------------------------|----------------------------------------------------------|
|
|
| `host NAME`, `hostname NAME` | `hostname = "NAME"` |
|
|
| `module foo args` (repeated) | `modules = { "foo args", ... }` |
|
|
| `network script args` | `network = "script args"` |
|
|
| `runlevel N` | `runlevel = N` |
|
|
| `rcsd /path` | `rcsd = "/path"` |
|
|
| `runparts [progress] [sysv] DIR` | `runparts = "DIR"`, `runparts-progress`, `runparts-sysv` |
|
|
| `set KEY=VAL` (repeated) | `environment { KEY = "VAL" }` |
|
|
| `log size:100k count:4` | `log { size = 100k count = 4 }` |
|
|
| `rlimit [hard\|soft] RES LIM` | `rlimit { hard.res = LIM }` |
|
|
| `readiness none` | `readiness = "none"` |
|
|
| `reboot-delay N` | `reboot-delay = N` |
|
|
| `reboot-watchdog on` | `reboot-watchdog = true` |
|
|
| `service-interval SEC` | `service-interval = SEC` |
|
|
| `shutdown script` | `shutdown = "script"` |
|
|
| `mknod /dev/x c 1 2` | `mknod = { "/dev/x c 1 2" }` |
|
|
| `include /path` | `include("/path")` |
|
|
|
|
Worth knowing
|
|
-------------
|
|
|
|
* Templates work unchanged: `%i` is replaced before the file is
|
|
parsed, so `service serv:%i { ... }` in a `serv@.conf` behaves
|
|
exactly like its legacy counterpart.
|
|
* A list cannot hold comments; the lexer reads entries after a `#`
|
|
regardless. Put commented-out candidates above the list.
|
|
* `${VAR}` in a value is expanded when the file is read, against
|
|
Finit's environment, and `${VAR:-default}` works too. An unset
|
|
variable expands to nothing. A plain `$VAR` is left alone and
|
|
reaches the service, which is what keeps `command = "syslogd -F
|
|
$SYSLOGD_ARGS"` working with an `envfile`.
|
|
* New settings only appear in the block format. The first are the
|
|
[per-service directories](service-opts.md#service-directories),
|
|
`runtime-dir` and friends, and [`pam`](pam.md).
|
|
|
|
For the full description of every key, see the rest of the
|
|
[Configuration](index.md) section.
|