When started with -c (the default when keventd is the device manager),
gate the pidfile on the kernel's uevent_seqnum having been stable for
200ms. Up to now ready signaling with the pidfile was done right after
coldplug() triggered the kernel to re-emit events, but before uev_run()
had drained any of them, so <pid/keventd> really only meant "listening
on netlink".
With the gate, services that depend on <pid/keventd> can now assume /dev
is populated and persistent symlinks are live.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A pass over the whole branch before merge, mostly in libink since
that is the new code and the part exposed to the wire. Grouped here
rather than scattered so the review is easy to read in one place.
libink parser and dispatch:
- Bound reader lengths so a 32-bit size_t can't wrap a wire length
past the guard and read out of bounds. Reachable pre-auth on any
bus, so it matters on the 32-bit targets Finit runs on.
- Drop a peer when a reply send fails instead of limping on with a
half-written frame; a built-in whose send failed used to fall
through and put a second frame on the wire.
initctl:
- Copy a D-Bus error name out of the reply before closing the client;
the reply points into memory the close frees. Both error paths now
share one helper so this can't creep back.
Authorization:
- Take the caller's groups from the kernel (SO_PEERCRED plus
SO_PEERGROUPS) rather than getpwuid()/getgrouplist(), which go
through NSS and can block PID 1 on a slow LDAP or SSSD backend.
The check is now a lookup against the group resolved once at init,
with no NSS and no 256 KiB array on the stack. A caller reaching
us through a broker carries no group set, so system-bus privileged
methods are root-only; the local bus keeps group support. See
libink/README.md for the note on lifting that.
Shutdown:
- Call dbus_exit() from the shutdown path so the server, its peers,
and the socket are let go cleanly. The teardown existed but nobody
called it.
Tests, CI, docs:
- A fuzz target for the message parser, run as a quick sweep in the
suite and properly under libFuzzer in CI, with the corpus carried
between runs. The -as-uid tests drop groups the way a login does
so SO_PEERGROUPS sees the right set, and widen the test socket to
reach the per-method check behind the 0660 gate. Bring the GitHub
actions up to versions that run on Node 24, and tidy a few small
things a /simplify pass turned up.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
__msg_parse() turns bytes off a socket into pointers, before anything
has vouched for the peer, and it is the only place in libink that
does. It had no test of its own beyond whatever the other tests
happened to send it, all of it well-formed.
The target checks the parser's contract, not merely that it survived.
A header field must point into the header field array, and terminate
inside it, and the parse must never claim more bytes than it was
handed. Crash-only would pass a parser that walked into the body and
returned fields from there, since those bytes were handed over too.
The expected bounds are derived from the raw header rather than from
the parser, so the two have to agree independently.
Every input is copied into an allocation sized to it first. Reading
past the end of a roomy buffer stays inside the allocation and the
sanitizer never sees it; against an exact one the same read is a
fault, which is where the sharpest findings come from.
Under libFuzzer it is an ordinary fuzz target and named files replay,
which is how a find gets reproduced. With no arguments it runs a
fixed sweep -- every truncation, every single-byte corruption, every
value of the length that decides where the header ends, and seeded
garbage -- so the suite covers the same contract on every build,
without clang or a corpus in the tree. It takes 40 ms.
CI fuzzes it properly on every pull request, keeps the crashers, and
carries the corpus between runs so it reaches deeper over time than
any single run can. Note that clang links the fuzzer runtime against
the newest GCC tree it finds, so the libstdc++ headers have to match
that one and not the default compiler, which is worth saying since
installing the obvious package leaves you exactly where you started.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A rule carrying sender, destination, or eavesdrop was refused whole,
and a peer whose AddMatch fails gets no signals at all. That is a
poor trade for keys clients attach as a matter of course: better a
filter wider than asked for than a subscription that never happened.
They are accepted and ignored rather than honoured. Widening costs
nothing here since Finit is the only sender on this bus, and what it
emits through the match table is state any peer that got this far may
already read.
argN and argNpath still take the whole rule down. Honouring them
means parsing message bodies, and nothing asks for them yet.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The other dbus-*.sh tests drive libink's own client, so the wire format
was only ever checked against the implementation that wrote it, and the
broker path had no coverage at all. Every bug found in it so far was
found by hand on a target.
Let the dbus plugin bring up a real dbus-daemon, wait for Finit to
claim org.finit, then talk to Finit with dbus-send, which shares no
code with us. The privileged call is the interesting one: it can only
be answered by parking the call and asking the broker who sent it.
The bus reads the policy Finit installs, so a malformed org.finit.conf
fails here rather than on a target.
Tests no longer build --with-libsystemd. Our replacement carries the
real soname but only the sd_notify() symbols, so in the test root it
shadowed the libsystemd the host's libdbus-1 wants and dbus-daemon
died on a missing sd_is_socket. Nothing under test needs the shared
library: serv is the only consumer and it compiles sd-daemon.c
straight in, which it now does regardless of the flag so notify.sh
keeps testing notify:systemd either way.
Staged from the host by lib/sysroot.mk like any other binary, and
skipped when the host has neither program.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
On the local bus SO_PEERCRED says who is calling and the kernel is the
one saying it. Behind a broker one connection carries every caller,
so that credential describes dbus-daemon and nothing else, and every
privileged method was refused there, root included.
Ask the bus driver instead. libink parks the call and hands us the
sender; we ask GetConnectionUnixUser and answer when the reply lands,
through the same event loop as everything else. Nothing blocks:
blocking in PID 1 is why libuEv exists. That needs calls libink can
make on a connection it already has, so it gained those too.
Answers are cached, since a bus never reuses a unique name while it
runs. Not across a restart though: a new dbus-daemon numbers from
scratch and :1.7 becomes somebody else, so the cache goes when the
broker does. A sender name too long to key on is refused rather than
truncated, two callers sharing a truncated key would share an
identity.
Privilege is no longer uid 0 alone. The socket is already owned by
the --with-group group, so refusing its members every method that
changes anything left a wheel user able to open the bus and unable to
reboot. Both gates now say the same thing.
Group membership needs NSS, which the C library loads with dlopen(),
so the lookup is compiled out where Finit is built to link statically.
That leaves such a build root-only, which is worth saying out loud
rather than leaving to be discovered.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The D-Bus socket was bound world read/write, on the reasoning that
SO_PEERCRED authorizes each method anyway. That leaves the read-only
surface open to every local user, and it quietly ignores --with-group:
a system that restricts initctl to the wheel group still handed the
same service state to anyone who asked over the bus.
Bind it 0660 and chown it to the configured group, the same gate the
fallback socket has always had. libink takes the mode as an argument
rather than assuming one, since who may connect is the embedder's
policy, not the library's.
The mode is applied at bind(), so there is no window where the socket
is more permissive than intended.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The last assertion sampled bar's state one step after asking Finit to
stop foo, but bar passes through stopped on its way to waiting, so the
test failed roughly one run in eight.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
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>
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>
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 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>
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>
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>
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>
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>
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>
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>
The one-liner format has grown crowded and very wide, and every new
service option makes it worse.
Add a second, block-based format, parsed with libconfuse:
service sshd {
description = "OpenSSH daemon"
runlevel = "2345"
command = "/usr/sbin/sshd -D $SSHD_OPTS"
}
Both formats keep the .conf extension and are detected per file by
content. Try-parse strictly with libconfuse; on a parse error,
re-parse leniently to tell a block file with a typo from a one-liner
file. Only a one-liner file reaches the legacy parser, a typo is
reported with its file and line.
Each block is translated to the canonical one-liner and registered
through the existing entry points, so the two formats cannot drift.
The one-liner parser is frozen at the 4.x feature set, new options
land only in the block schema. libconfuse 3.3 or later is required,
CFGF_KEYSTRVAL does not exist before it.
Covers service, task, run, sysv and tty blocks, the static directives,
and the cgroup, rlimit, set and log blocks. Templating and the
documentation rewrite are still to come.
The regression test covers translation of a service block to the
one-liner, a block-format /etc/finit.conf booting with set {} applied
at bootstrap, both formats side by side, and rejection of a typo at
block and at root level.
A rejected file must not fall through to the legacy parser, which
registers a bogus unstartable service per line. assert_num_children
cannot see that, the bogus service has no children either, so the
check is assert_num_services.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Every test left a stray `sleep 300` behind, reparented to PID 1, where
it lingered for up to five minutes after the test had finished.
wdstart() runs the watchdog in a subshell, so $! is the pid of the
subshell, not of the sleep it forks. wdkill() killed the subshell and
orphaned the sleep.
Kill the child first, killing the subshell puts the sleep beyond the
reach of pkill -P. Neither kill is sure to match, and wdkill() runs
from the EXIT trap under set -e, so both must tolerate failure. Also
return early when wdpid is unset, for failures before wdstart() runs.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Cover the scenario fixed in "service: clean stale pidfile after
unclean daemon exit": a daemon with a pid:!/path config dies via
SIGKILL, leaving its pidfile behind, and the next instance must
still come up.
Add a 'serv -x' flag (refuse to start when the pidfile already
exists, dbus-style) so the test actually exercises the cleanup --
without it, plain 'serv' would happily overwrite the file and the
test would pass with or without the fix.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A service that is reloaded should not trigger dependants to be reloaded
unless the new <~cond> is used. Which is reserve for tightly coupled
services.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Conditions in Finit are dependencies: if A is asserted, service B is
allowed to run. When A goes through FLUX (e.g., upstream reloads),
dependents are PAUSED and then simply resumed when the condition is
reasserted -- this is the correct behavior for barrier-style deps
like <pid/syslogd>.
However, some setups have tightly coupled services where dependents
must be reloaded/restarted when an upstream service reloads, not just
resumed. E.g., the FRR routing stack on Infix OS:
netd <pid/mgmtd> ← zebra <!pid/netd> ← {staticd,ripd} <!pid/zebra>
When netd reloads (SIGHUP), zebra and its dependents must be restarted
to pick up the new configuration.
The new '~' condition prefix marks a dependency as flux-sensitive:
service <!~pid/netd> name:zebra ...
When the upstream condition goes FLUX and returns to ON, the dependent
is reloaded (SIGHUP) or restarted (noreload '!') instead of merely
resumed. Transitivity follows naturally through the condition chain.
Closes#416Closes#476
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
When 'initctl reload' is called after marking a service in a dependency
chain dirty, Finit fails to restart (unfreeze) affected services.
This patch updates the pidfile plugin to watch for IN_ATTRIB changes,
e.g. when a process uses utimensat() to update its pidfile, and adds
service_step_all() at end of reload cycle to guarantee convergence
after conditions are reasserted.
Issue #476
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Device conditions tracked by devmon were lost on `initctl reload`
because the reconf path did not re-assert them. Add devmon_reconf()
to iterate all tracked device nodes and set or clear their conditions
based on current device presence.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
Verify that 'initctl reload foo' properly triggers dependent
services by checking that bar gets a new PID after the reload.
Also change the second test case from service/foo/running to
service/foo/ready which is the actual condition set by pidfile.so.
Fix a race in slay where the target process could exit between
the PID lookup and kill -9, causing spurious test failures in
tight kill loops (e.g., start-kill-service.sh).
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Finit now requires being able to query at least for the root user and
group before starting any services.
Also, add support for using libraries installed in /usr/local
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A following compilation error was observed:
| libsystemd/sd-daemon.c:64: undefined reference to `strlcpy'
fix it by include the required libite dependency.
Signed-off-by: Ming Liu <liu.ming50@gmail.com>
This commit introduces a bare-bones replacement for libsystemd:
- Build .so file and add --with-libsystemd to configure
- Add capabilities support to test/src/serv.c
- Update tests to account for a Finit built w/o libsystemd support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A systemd service should only use NOTIFY_SOCKET and an s6 style
service reads its notify descript from the command line. For
details, see notify.sh
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>