libink was written against the only bus it had, its own, where the
peer on the other end is the client. A broker is not: it routes for
senders it names itself, expects a DESTINATION on anything addressed
through it, and answers on its own schedule rather than next.
Runlevels go on the wire as S and N rather than the digits Finit
keeps internally, since that is what a caller outside Finit means by
one.
The library stays a convenience library, linked into finit and
initctl and installed nowhere: the ABI promise waits until libink is
its own project.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The summary table, the per-service detail, JSON and the quiet and
ident forms all read state Finit already publishes, so they read it
from the bus like everything else rather than through a second path
that has to be kept in step.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Runlevel and version are state, not actions, so they belong behind
org.freedesktop.DBus.Properties rather than another method each.
Finit also claims org.finit on the system bus when it finds one, so
ordinary D-Bus clients can reach it without knowing about
/run/finit/bus. Opportunistic on purpose: no dbus-daemon is a normal
state for the systems Finit runs on, not an error to report.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The bus can already answer questions and change services, so give
initctl the two things it still did another way: watching signals as
they happen, and getting or setting user conditions.
The dbus tests move with it, split by area rather than one file that
grew every time the library did.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Finit had no way to answer the question every service manager gets
asked: what is running, and change it. D-Bus is how the rest of
userspace asks, but linking libdbus, sd-bus or GIO into PID 1 buys a
dependency, an allocator and a main loop we do not control.
So libink: the wire format, an object tree, and a bus of Finit's own
at /run/finit/bus, gated like INIT_SOCKET. It speaks the standard
org.freedesktop.DBus, .Peer, .Introspectable interfaces, and Finit's
own Manager1, Service1 and Cond1 on top. Methods that change
something are marked privileged and answered only for a caller the
kernel vouched for, via SO_PEERCRED.
Server and client both, since initctl is the first thing that needs
to talk to it, and its Start/Stop/Restart/Reload now go over the bus
rather than the legacy socket.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A cgroup holding processes cannot enable controllers for its children,
so init/ had to stay a leaf. The hotplug helpers 10-hotplug.conf.in
places there ended up in groups where cpu.weight and friends could
never be set.
Keeping PID 1 in the root cgroup makes init/ a domain like the others.
It also unbreaks lxc-based runtimes: liblxc bases the container tree on
PID 1's cgroup and only special-cases systemd's init.scope/, so under
Finit it landed containers in init/, with no controllers available.
Issue #497
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The kernel delegation docs require write access on the directory so
the delegatee can mkdir() children.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The line-based format has had the flag since v4.4 (issue #286), where
it prepends -p to the built-in getty, which turns it into login -p and
passes the environment on. The block format was written from the three
documented tty variants and the flags listed in the tty documentation,
and passenv was in neither, so it was left out. Converting a tty line
that used it therefore lost it, with nothing said.
It only reaches the built-in getty. An external getty is handed its
arguments through command, so there is nowhere to put a -p, and the
setting is refused with a warning rather than quietly ignored.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The migration guide told anyone holding the repeated-stanza idiom for a
per-platform service to split the variants across files or stay on the
line-based format, because a block title is an identity and the
variants have to share one barrier. provides is the answer, so the
guide converts that shape now instead of routing around it, and the
header of 10-hotplug.conf.in no longer points at the workaround.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A block title is a service identity, so two variants of one service
gated differently per platform cannot share a title. They do need to
share the barrier condition downstream services wait for, which until
now was spelled by the identity alone and so could not be shared:
service syslogd:udev {
if = "udevd"
conditions = { "run/udevadm:5/success" }
provides = "pid/syslogd"
command = "-syslogd -F"
}
Any namespace is allowed, since the point is publishing a name that
existing configurations already wait on. A claim on a condition that
is already owned is dropped with a warning naming the owner, and the
service still registers: the overlap is a configuration bug, and an
init system is more useful degraded than refusing to boot. A real
identity outranks a claim, pid/<ident> is how Finit tracks its own
services, so it is not up for grabs.
Claims are dropped before each reload re-reads the .conf files. Doing
it per service as it re-registers is not enough, since services are
read in file order and one re-registering would lose to a claim
another had not dropped yet, flipping the owner on every reload.
initctl cond dump asked who owned a condition only for the pid/
namespace and printed 'static' for usr/, which now hides a provider.
It asks first and falls back to what the namespace implies.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A service's condition was computed with mkcond() at each of the six
sites that assert or clear it, and svc_find_by_cond() reimplemented
the reverse lookup a seventh time. maybe_clear_cond() had its own
scan for another service supplying the same condition.
svc_cond_owner() answers who owns a condition, svc_cond_nth() walks
the conditions a service owns, and svc_cond_set()/svc_cond_clear()
apply to all of them. svc_find_by_cond() becomes a wrapper, and
maybe_clear_cond() keeps its rule per condition rather than for the
one it used to compute.
The provides[] storage lands here unused, since svc_cond_nth() reads
num_provides. Nothing sets it yet, so a service still owns exactly
its own pid/<ident> and there is no functional change.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The migration guide covered a stanza at a time, which is the wrong
shape for the two idioms that repeated a whole stanza. One of them,
several candidate binaries for one service, is now a command list.
The other, one service gated differently per platform, has no block
equivalent: those blocks share an identity because they share the
barrier condition downstream services wait for, so they cannot be
given separate titles. For that one the guide says to split the
variants across files, or leave that file in the line-based format,
which Finit still reads.
The udevd example in services.md taught the merge-broken form, and
system/10-hotplug.conf.in pointed readers at it for their syslogd.
Also lists libConfuse among the build dependencies. It has been
mandatory since the new .conf format landed, and build.md still said
two libraries. And corrects the note on variable expansion: it is
${VAR} that libconfuse expands when the file is read, with
${VAR:-default} supported. A plain $VAR reaches the service, which is
what makes `command = "syslogd -F $SYSLOGD_ARGS"` work with envfile.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A daemon known under more than one name had to be declared once per
name, each stanza carrying nowarn so the ones that were not installed
were skipped quietly. That leaned on the line-based format having no
titles. With a title as the service identity the repetition is no
longer available, so the candidates move inside the block:
command = { "/lib/systemd/systemd-udevd", "-udevd" }
Finit starts the first one whose binary resolves. A candidate that is
not installed is expected here, so nothing is logged for the ones that
lose, and only the winner is looked up again by service_register().
The leading '-' keeps its meaning and is read from the candidate that
wins, or from the last one when none resolve.
libconfuse accepts a bare string for a list option, so the common
`command = "prog args"` is unchanged, and whichp() already skips any
arguments to the command, so the candidate goes to it as written.
tty blocks take the same list. Their command is the getty to run, and
it has the same reason to name alternatives. Both block types now
share svc_command(), which also puts the leading '-' rule back in one
place: tty_translate() had its own copy. A tty needs no command at
all, it may name a device or notty instead, so svc_command() returns
NULL for an empty section and each caller decides whether that is an
error.
system/10-hotplug.conf.in returns to one udevd block, which is what it
meant all along.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The block title is the service identity, and libconfuse merges two
sections that share one, without a word. Two blocks titled the same
in one file therefore loaded as a single service holding a mix of both
declarations, with scalars taken from the last block and lists reset
by it.
system/10-hotplug.conf.in is written this way: two udevd blocks, one
per candidate binary, the way the line-based format spelled a
fallback. Only the second survived the merge, so a system that has
/lib/systemd/systemd-udevd but no udevd got no udevd service at all,
and the whole `if = "udevd"` chain behind it went with it.
CFGF_NO_TITLE_DUPES turns the merge into a parse error naming the file
and the title, and the file is then rejected as a whole. The same
title in another file is untouched, that is how an administrator
overrides a system .conf.
Format detection had to stop agreeing with it. is_new_format() probes
by parsing, so a duplicate title made it answer "not block format" and
conf_parse_file() handed the file to the legacy parser, whose errors
buried the real message. The probe now clears the flag on its own
copy of the option array, keeping the verdict syntactic.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The reload-signal translation calls str2sig() and compares against
SIGHUP, but conf.c never included signal.h. glibc pulls it in
transitively, so the omission went unnoticed until a cross-compile
against uClibc-ng in Buildroot failed to build.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The runparts directory and the dbus pidfile and daemon paths are
embedded in double-quoted values of the generated block files. A
literal quote in either ends the value early and libconfuse rejects
the whole file, and a backslash is read as an escape sequence,
silently mangling the path. The legacy one-liners had no quoting, so
neither failure existed before the block conversion.
conf_escape() doubles backslashes and escapes quotes; verified by
round-tripping hostile paths through cfg_parse_buf().
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Finit's own generated services -- watchdogd, keventd, runparts, and
the dbus plugin -- still went through conf_save_service() as legacy
one-liners, so `initctl show keventd` taught the old format on a
system otherwise converted to the new one.
conf_save_service() now takes the block title and a printf-style body
and writes the file itself:
# Generated by finit:conf_save_service()
service keventd {
description = "Finit kernel event daemon"
runlevel = "S12345789"
notify = "none"
cgroup init {}
command = "/libexec/finit/keventd"
}
vfprintf() into the file also removes the fixed-size staging buffers
in the callers, where a long dbus pidfile path could truncate inside
a quoted string and take the whole generated file with it.
Semantics preserved: watch-only pid:! maps to pidfile without
pidfile-create, the watchdog keeps its watchdog:finit identity, and
log:console becomes log { file = "/dev/console" }.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The README.md symlinks exist for GitHub browsing and collide with
index.md when mkdocs renders both; exclude them like TODO.md.
Two links pointed at anchors that never existed: features.md has bold
captions rather than headings, so "Automatic Reload" gets an explicit
attr_list anchor for the link from the front page, and the TTY link
now spells the actual heading, controlling-tty-for-services.
mkdocs build is silent after this.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The new-format reference describes the block format on its own terms,
which is the wrong lookup direction for someone holding a legacy
one-liner. Aaron migrated Finix OS from the PR description, proving
the need for a token-in, key-out mapping in the user guide.
One table per part of a stanza, worked conversions for the shapes
that changed structurally -- cgroup selection and the three tty
variants -- and the dropped tokens listed with their replacements.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The line-based format accepts `service :80 ...`, deriving the name
from the command basename. The block format has no counterpart, the
title carries both name and ID. Implied by the format description,
but anyone converting such a line deserves to find it written down.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The workflows install libuev and libite from source and everything
else from apt, but never libconfuse, so every build job on this
branch dies in configure:
checking for libconfuse >= 3.3... no
Ubuntu ships libconfuse 3.3 with the static library included, which
covers both the static and regular builds. Staying on 3.3 in CI is
deliberate: it exercises the fallback paths marked
"XXX: Workaround for libConfuse <3.4" that a from-source 3.4 would
leave untested.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Aaron Andersen points out in the #492 discussion that the *Directory
settings carry more contract than create-and-chown: per-directory
modes, specific ownership rules, and cleanup toggles. Without them
config-dir was chowned to the service user, which systemd never does,
an existing directory with drifted ownership was left wrong, and the
runtime directory could not survive a restart.
Now matching systemd.exec(5), and where the man page is vague, the
code in setup_exec_directory():
- each directory takes a matching -mode key, octal with the leading
zero, default 0755. The mode of the named directory is locked
down again on every start, also when it already exists
- config-dir is created but never chowned
- the contents of an existing directory are left alone as long as
the owner is right; on drift everything under it is chowned back
- runtime-dir-preserve = no | restart | yes maps
RuntimeDirectoryPreserve=. A service still qualified to run when
the runtime directory would be removed is restarting, not
stopping, which is what svc_enabled() answers
The dir mechanics move to mksubsysd(), taking resolved ids, with
mksubsys() reduced to a name-resolving wrapper for the dbus plugin.
The child resolves uid/gid once for both directory setup and
privilege drop.
The symlink form, RuntimeDirectory=foo:bar, is not adopted.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A service that drops privileges cannot create its own PID file in
/run, root owns it. Finit can create the file with pidfile-create,
but the daemon still cannot touch it to confirm a SIGHUP.
Five new settings, block format only: runtime-dir, state-dir,
cache-dir, logs-dir, and config-dir. The value is a directory name,
resolved under /run, /var/lib, /var/cache, /var/log, and /etc,
respectively. The directory is created before the service starts,
mode 0755 owned by user/group, and the full path is exported to the
process as RUNTIME_DIRECTORY, STATE_DIRECTORY, CACHE_DIRECTORY,
LOGS_DIRECTORY, and CONFIGURATION_DIRECTORY. Mode and ownership are
asserted at creation only, a daemon may tighten them afterwards.
The runtime directory is removed when the unit stops, after any
exec-stop-post script, like systemd with RuntimeDirectoryPreserve=no.
A completed run/task counts as stopped unless remain-after-exit keeps
it up. The other four persist across restarts.
These are the first settings with no legacy token: they are validated
by service_set_dir() and stored on the svc that service_register()
now returns. systemd accepts a list of directories per setting; this
is a single name for now, widening later is compatible since
libconfuse accepts a bare value for a list option.
The test sysroot gains libnss_files.so.2, which ldd cannot see, glibc
dlopen()s it. Without it getpwnam() fails inside the chroot, so
user/group settings never resolved and directory ownership could not
be tested.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
rmrf() is needed outside tmpfiles.c. The move also deduplicates the
nftw callback: the contents-only removal used by tmpfiles 'D' entries
is now rmcontents(), sharing the callback with rmrf().
mksubsys() did nothing at all when the user could not be resolved, no
directory and no message, and callers had no way to tell. Now the
directory is always created, ownership is best effort, and an unknown
user is warned about.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Settings that exist only in the block format have nowhere to go: the
legacy line cannot carry them, and service_register() returned an errno
that no caller ever read, so conf.c had no handle on the service it just
created. Return the svc instead, NULL with errno set on failure, errno
zero when a block is skipped on purpose.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The block format spells conditions as bare strings everywhere else, so
requiring `if = "<usr/foo>"` left one sigil behind, carried over from
the line-based `if:` token. A namespace separator already tells the two
apart: a value with a '/' is a condition, anything else is a service
name.
svc_ifthen() picks its mode from the start of the statement and applies
it to the whole, so a statement naming both kinds cannot be evaluated.
That is now an error, as are the old angle brackets, and either one
skips the block:
/etc/finit.conf: mixed: if: cannot mix a service name with a
condition in 'anchor,usr/enable-me', a statement must be all of
one kind, skipping
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Reference sections kept pointing at the line-based format they no longer
document. `sysv` and `task` sent the reader to Services for "<COND>",
the cgroups chapter opened by listing three legacy directives and then
explained further down that only two of them exist here, and the logging
chapter still gave "log:prio:facility.level,tag:ident" as the full
syntax.
Some claims were wrong independent of the format:
- a sysv is a supervised daemon, grouped with service in
SVC_TYPE_DAEMON, not a variation on task
- restart-max has no upper bound of 255, or any other
- the built-in rescue fallback runs in 12345789, not 12345
- conditional loading quotes system/10-hotplug.conf, not
system/hotplug.conf
- the key spells conflicts, not conflict
- the built-in getty no longer wants TERM last, it is a key
`if` takes either a service name or, in angle brackets, a condition,
decided in svc_ifthen(). Only the examples showed this, so it is now
said.
Terminology follows the split index.md already draws: a block is the new
format, a stanza the line-based one.
src/rescue.conf was still line-based, missed because it sits in src/
rather than system/ or contrib/.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
An ambient capability only reaches the effective set when euid is
non-zero, so a service that pairs `capabilities = { "^cap_..." }` with a
root user gets none of the restriction it asks for, and keeps the full
root set instead. Finit read the list, applied it, and said nothing. A
build without libcap dropped the list on the floor just as quietly.
Both now warn, naming the service:
nginx: ambient capabilities ('^') have no effect as root, use a
non-root user, or '%' and '!' entries
The ambient entries are read back from the parsed IAB value rather than
matched in the text, so inheritable ('%') and bounding ('!') entries stay
silent -- those work fine as root.
The warning repeats when the .conf files are re-read on runlevel change,
as parse warnings here already do.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The block conversion changed the bodies of the reference sections but
left every "**Syntax:**" header spelling the line-based format, so each
page opened by teaching the format it then stopped using. Six files
were missed entirely: runparts, files, capabilities, requirements,
runlevels, and switchroot.
runparts had no block spelling written down anywhere, though the parser
has read `runparts`, `runparts-progress`, and `runparts-sysv` all along.
tty gains a table per variant. Its three syntax lines carried nine
positional fields between them, which no longer describes anything the
parser accepts.
Fixes#148
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The examples people copy from were still written in the line-based
format, so the block format was documented but nowhere demonstrated.
Two names in contrib were accidents of the old format, where the
service name falls out of the command basename: the Alpine and Void
keymap task was called zcat, and Debian's console/keyboard setup tasks
carried a .sh suffix. They now carry the name their file implies.
Nothing referenced the old names.
The mdevd coldplug path keeps the name it has always had. Its legacy
line spelled the name inside the cgroup argument, where it names the
cgroup leaf and not the service, so the barrier condition really is
<run/mdevd-coldplug/success> and not the <run/coldplug/success> the
comment above it promises. Converted as-is so boot ordering does not
change; the discrepancy is now written down where it happens.
A list may not contain comments, the lexer sees the entries after the
'#' regardless:
modules = {
# "fbcon",
"softdog"
}
so the commented-out module candidates sit above the list instead.
setup-sysroot.sh removes 10-hotplug.conf from the test sysroot, so that
file is covered by parsing only, not by make check.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
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>
parse_cgroup() takes two arguments that are not cgroupfs files: the
leaf directory to place the service in, and whether to hand the subtree
over to it. The block format could express neither. 'name' happened
to work, because a free-form key is emitted as name:VALUE and that is
what the parser looks for, but 'delegate' came out as delegate:true and
was filed as a cgroup setting, so it silently did nothing.
Declare both, and emit delegate as the bare flag the parser expects.
Neither means anything on a top-level group definition, so say so there
rather than emitting something that would be written to cgroupfs.
service podman {
cgroup containers { name = "podman" delegate = true }
...
}
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This is what 'initctl create' and 'initctl edit -c' put in front of a
user writing their first .conf file, so it is also the whole of the
"initctl emits the new format" work: neither command generates syntax,
they copy this file and open an editor on it.
The ASCII diagram naming eight positional fields goes with it. A
block has no positions to explain.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Both keys prompt the question they should be answering.
'remain' decides whether a finished run or task keeps existing: without
it the entry is pruned, so the work re-runs on every runlevel entry,
initctl cannot see it, and its post script never fires. With it the
entry stays, is not re-run, and gets a teardown when stopped or when it
leaves its runlevels. That is systemd's RemainAfterExit, and 'remain'
is that name with the informative half cut off.
'manual' says how a service is started but not that it is about
starting at all.
remain -> remain-after-exit
manual -> manual-start
Both keep their old spelling as an alias, which they qualify for twice
over, as abbreviations of the canonical name and as the legacy
spellings.
While here, give sec_getbool() the alias argument its string and list
counterparts already take.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A condition list could be led by '!', which is not a condition and not
a negation. It is a flag on the block, and it means two unrelated
things depending on which block it sits in: a service or sysv does not
handle SIGHUP and must be restarted to reload, while a run or task
must not hold up bootstrap. Writing '<!>' with no condition at all is
legal, which gives away that it was never an operator.
Give each meaning its own key, valid only where it applies:
service foo { reload-signal = "none" } # restart to reload
task bar { required = false } # do not hold up bootstrap
Using either on a block type it does not apply to warns, as does a '!'
left in a conditions list. Both still translate to that same '!',
which is all a legacy line can carry, so reload-signal takes SIGHUP or
none for now; str2sig() already accepts any case and an optional SIG
prefix.
This also clears the way for the conditions list to grow real
operators, '+' and '-' for asserted and deasserted, without '!'
sitting among them meaning something else entirely.
The '~' prefix stays. It belongs to the list: it marks a dependency
whose reload should propagate here.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The initial implementation was done using a naive translator of the
legacy one-liner format key by key, so it inherited encodings that the
block format exists to remove: a timeout packed into a script path, a
small comma-and-colon language inside the log string, sigils standing in
for booleans, and a log key (services) carrying three (!) types.
Settled naming against systemd, OpenRC, FreeBSD rc.subr, s6 and SMF.
Match systemd's semantics, not its naming.
pid -> pidfile, plus pidfile-create for the rare case
where Finit writes the file rather than the daemon
environment -> envfile, since it names a file to source, and the
top-level environment {} block sets variables
pre/post/... -> exec-start-pre, exec-start-ready, exec-stop,
exec-stop-post, exec-reload, exec-cleanup, each
with its own -timeout instead of "SEC,script"
halt, kill -> stop-signal, stop-timeout
restart -> restart for the policy, restart-max for the count
log -> a block with file, priority and identity, where
/dev/null and /dev/console are spelled as paths
group -> group and extra-groups, no longer positional
nowarn -> a leading - on command, as on envfile
List-valued keys take plural names. Aliases are desc, cond, mod,
caps, env, halt and kill; an alias may abbreviate the canonical name
or preserve a legacy spelling, nothing else.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
'make check' refreshes the sysroot through the setup-chroot rule, but
running a test script by hand does not, so the test exercises whichever
finit was installed last and reports on code that is no longer there.
Both a passing and a failing run are then meaningless, and nothing says
so.
Compare the built binary against the installed one at startup and fail
with the command that fixes it.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Both were bounded by killdelay, the delay between the stop signal and
SIGKILL, because service_run_script() had nothing else to reach for.
That conflates two things: how long the daemon may take to die, and
how long its stop script may run.
Give each hook a timeout of its own, defaulting to killdelay when
unset, so the existing behaviour is what you get until you ask for
something else. parse_script() already falls back that way for the
hooks that had one.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A stop: or reload: script written with a timeout killed Finit at
config load:
service stop:5,/bin/true service.sh -- Boom
parse_script() takes the timeout as a pointer and the caller decides
whether it wants one. However, both stop: and reload: scripts so far
have no timeout, i.e., NULL. Guard the branch that reads a leading
number.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Three private ones had grown: fnread() in util.c, flen() behind
pid_cmdline()/pid_cgroup() in cgutil.c, and conf_read_template() in
conf.c. Two of them were also wrong in ways the others were not.
fnread() formatted the path into a char[256] and stat()ed it before
opening, so a longer path was silently truncated and then read from
whichever file the truncation happened to name, and the size could
change between the look and the read. flen() existed because neither
of those approaches works on procfs at all, where stat() reports zero
and the only way to learn the size is to read to EOF.
Add fslurp() to util.[ch], which every tool already links. It opens
first and sizes the fd it holds, treats st_size as a hint, and reads
until EOF, so procfs and regular files take the same path. Paths are
formatted by libite's vfopenf(), which allocates to fit. Callers that
need the byte count, /proc/PID/cmdline embeds NUL, ask for it.
fnread() keeps its signature and becomes a bounded copy out of the
result, so its one caller is unaffected.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A template in the block format registered garbage. conf_parse_file()
routed every file with an '@' in its name straight to the legacy
parser, which read the block line by line: the section header became a
service whose command was the section title, and each key = value line
below it became an environment variable.
service serv:%i { ... } -> service 'serv:eth0' with argument '{'
Substitute %i over the whole file before parsing instead, so format
detection and both parsers see finished text. A bare name@.conf is
still skipped, it is the template rather than an instance of one.
The legacy parser no longer opens the file or substitutes per line, it
is handed the instantiated buffer, so the template convention now has
one implementation instead of two. conf_is_template() applies
basenm(), a directory with an '@' in its name is not a template.
libconfuse cannot name a buffer it parses before 3.4, so a typo in a
template would be reported against "[buf]". Parse through fmemopen()
with the file name preset until the floor moves.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>