initctl reload freezes conditions to the old generation and each
owner re-asserts. Finit's own providers do this in-process; an
external provider whose conditions are generation files, rather than
the oneshot symlinks keventd uses, has no way to know the moment.
Emit Manager1.ConfigReloaded when reconfiguration completes.
keventd needs no subscriber: its conditions are symlinks to the
reconf marker itself, so they read the current generation by
construction and never flux, which the device bus test now pins
down.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
initctl_CPPFLAGS is only assigned under the DBUS conditional, but an
automake per-target variable exists even when its conditional is
false, so a --disable-dbus build dropped AM_CPPFLAGS entirely:
util.c:319:16: error: invalid use of undefined type 'struct FTW'
Assign the base flags unconditionally and append under DBUS.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
All three org.finit signals were invisible to generated proxies, and
org.freedesktop.DBus was missing from the standard interfaces even
though Hello, AddMatch, and RemoveMatch are answered.
Add a link_signal_t table to the vtable, emitted like methods and
properties, declare the Manager1 and Cond1 signals, and complete the
static XML with PropertiesChanged and org.freedesktop.DBus.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The D-Bus methods carried byte-for-byte copies of api.c's static
start/stop/restart helpers. Promote them to service.c alongside
service_reload(), which already serves both callers, and reduce both
sides to svc_parse_jobstr-style adapters.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
initctl -t N reboot arms an emergency bypass timer over the legacy
socket, but the bus methods took no argument, so the timeout was
silently dropped whenever D-Bus was up.
Reboot, Halt, and Poweroff now take a timeout in seconds, 0 for
none, armed via the same shutdown_bypass() the legacy path uses.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Service1 Start/Stop/Restart discarded the action return value, their
Manager1 twins map it to org.finit.Error.Failed. Cond1 Set/Clear
replied success even when the condition symlink operation failed,
where legacy initctl exits 73. Verify the resulting condition state
with cond_get() rather than the noupdate return values, which report
no-change, not failure, and would reject an idempotent re-set.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Manager1.Signal silently skipped stopped services, so the same
command gave different exit codes depending on transport: the legacy
INIT_CMD_SIGNAL path fails when the service is not running. Mirror
the legacy behavior.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The bus method called sm_runlevel() unconditionally. In runlevel 0
and 6 that aborts an in-flight shutdown, which INIT_CMD_RUNLVL
refuses with a warning, and during bootstrap it switches immediately
where the legacy path defers via cfglevel to the end of runlevel S.
Port both. A bad runlevel argument still returns InvalidArgs, where
the legacy protocol acks silently: a typed interface rejects garbage.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The legacy INIT_CMD_SUSPEND is refused in runlevel S, 0, and 6, the
bus method suspended unconditionally, even mid-shutdown. Add the
same guard, replying WrongRunlevel like the reboot family.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A service with a <class/net/eth0>, or any other keventd-provided,
condition is never started when the device appears. keventd asserts
the condition file, but devmon only watches /dev, so no cond_update()
ever reaches affected services. Reload made it worse:
devmon_reconf() clears any registered dev/ condition without a /dev
node behind it.
Watch the dev/, class/, and driver/ condition directories, like the
sys and usr plugins do for their namespaces, and treat an existing
condition file as device presence in devmon_reconf().
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
dev/<X> only fires when a device gets a /dev node, which leaves a lot of
embedded-relevant hardware uncoverable: DSA switch cores, IIO sensors,
LEDs, backlight, PHYs, regulators -- all live purely under sysfs.
Two new condition namespaces:
class/<subsystem>/<sysname> asserted on every sysfs class device add
(e.g. <class/leds/blue>)
driver/<name> asserted while the driver is bound to at
least one device (e.g. <driver/mt7530>)
A driver can bind to several devices, so driver/ conditions are
refcounted: asserted on first bind, cleared when the last device is
unbound.
dev_cond() is generalized into a static cond_emit(prefix, rel, set) so
class_cond() and driver_cond() share the same mkpath + symlink/erase
code. The name avoids colliding with src/cond.h's public cond_path()
helper (unrelated function that returns a condition's filesystem path).
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This is a backwards compatible mode for users upgrading and not noticing
that keventd is now build by default.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
After keventd processes a uevent (creating device nodes, loading
modules, etc.), rebroadcast the original event to netlink group
0x4 so that libudev-zero consumers -- graphical applications,
Wayland/X11 compositors, libinput, and anything else using libudev
to monitor device hotplug -- can receive device events.
Rebroadcast is enabled by default. Use -g to override the target
netlink group mask, or -G to disable rebroadcast entirely. Bit 0
(kernel group) is always masked out to prevent feedback loops.
Ref: https://github.com/finit-project/finit/issues/451#issuecomment-3817233886
See: https://github.com/illiliti/libudev-zero
Suggested-by: Aaron Andersen <aaron@fosslib.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Evolve keventd from a power_supply-only monitor into a full device
manager capable of replacing mdev/mdevd on embedded systems. This
is the first step towards Finit v5.0 where keventd absorbs devmon.
New capabilities:
- Parse all uevent actions (add, remove, change, bind, unbind)
- Create and remove /dev nodes with subsystem-aware permissions
- Create persistent symlinks in /dev/disk/by-{id,path} and
/dev/input/by-{id,path}, tracked for cleanup on device removal
- Load firmware from /lib/firmware/ via the sysfs loading protocol
- Spawn modprobe for MODALIAS events (async, non-blocking)
- Coldplug support via -c flag (walks /sys/devices to replay events)
- Set dev/* conditions for Finit's service dependency system
The original power_supply monitoring and sys/pwr/ac condition are
preserved.
New files: keventd.h (structures/API), uevent.c (all device logic).
The receive buffer is increased to 8K with a 1MB socket buffer to
reduce event loss during coldplug bursts.
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>
A call is parked until the resolver says who sent it, and an outbound
call sits in a pending slot until its reply lands. Neither had a way
to give up. A broker that answers GetConnectionUnixUser slowly, or
not at all, leaves the caller waiting forever and keeps the slot; four
of those and every later privileged call is refused with
LimitsExceeded until Finit restarts.
libink cannot time itself out, it has no event loop, so the deadline
is the embedder's to keep. One sweep per connection covers both, and
the ordering between them stays in the library rather than in each
embedder: calls first, because one timing out usually resolves the
park it was made for, and AccessDenied tells that caller more than a
bare timeout.
The sweep is armed when a resolve is deferred and stops rearming as
soon as nothing is outstanding, so a system that never meets a broker
never wakes up for it.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Parked calls and outbound calls awaiting a reply only ever happen on a
connection talking to a broker, but the parked array sat on the server
and the pending array on every connection. A server with no broker
carried 4 KiB of slots it could never fill, and both were reachable
from code paths that have no business in them.
Move both behind one struct, allocated on the first park or call and
freed with the connection. link_server_t goes from 4400 to 168 bytes;
link_connection_t barely moves, its buffers dominate, but an ordinary
peer no longer carries reply-tracking it never uses.
Tokens are now per bus rather than per server, so link_uid_resolved()
takes the connection the answer is about. Every resolver already has
it: it is the first argument to both the resolver and the reply
callback.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A peer can be dropped from inside its own read loop: a handler emits a
signal, the write to that very peer fails, and the drop lands while
link_connection_process() still holds the connection and will touch
its rx buffer on the way out. Freeing there pulls the ground out from
under it. Unlink the peer and let the event loop free it once the
stack has unwound.
The work has to be scheduled with a non-zero delay. A uev timer armed
with zero is a disarmed timer, so the queue would never run and the
connections would leak instead.
Losing a peer is also not a warning. It is what shutdown looks like
from here, and every reboot said so on the console.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The legacy socket logs a line per command under initctl debug; the bus
logged nothing, so the transport that now carries most of initctl was
the one you could not watch.
libink gets a logger hook rather than a dependency on Finit's: it
passes the emitting function and a formatted message, and dbus.c hands
both to logit() so the two sources read alike. Trace points cover the
connection lifecycle, every inbound call, and why a call was refused.
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>
libink was written peer to peer, where one connection is one client
and one principal. Attaching to a message bus breaks both halves of
that, and two things followed from it.
Signals never reached the system bus. Fan-out is gated on the peer
having sent AddMatch, but a broker subscribes for its own clients and
never sends us one, so every ServiceStateChanged was dropped on the
floor. A connection attached with LINK_ATTACH_BROKER gets them all.
Hello, AddMatch and RemoveMatch write per-connection state. Shared by
every caller behind a broker, that lets one sender exhaust the match
cap or drop another's rule, so we leave all three to the bus, whose
job they are.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The attach is best effort and its diagnostics were tuned for the case
where no broker exists, so a broker that answers but refuses us was
reported as a bare rc=1 at debug level. Chasing that meant reading
the header builder to find out what the number meant.
Failures now quote the error name the broker sent, and the one for a
name we could not claim says which of the three ways it went wrong.
Repeats stay quiet. The probe runs on every service and condition
change, and before syslog is up each line is an open, write and close
on /dev/kmsg, so a broker that keeps refusing would otherwise flood
the console during boot.
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>
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>
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>
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>
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>