Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions doc/config/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,20 @@ being where they are installed and if the `initctl` tool can manage them
with the `enable` and `disable` commands. An administrator can always
create files and symlinks manually.

By default, `available/` is only a staging area, its files are *not*
read by Finit. With the [`load-available`](#loading-available-services)
setting enabled, their unit stanzas are registered as *available-only*:
controllable with `initctl start/stop`, but never started automatically
until enabled. This loader state is separate from `manual-start`, and
top-level settings remain inactive.

At bootstrap, and `initctl reload`, all .conf files are read, starting
with `finit.conf`, then `/run/finit/system/*.conf`,
`/lib/finit/system/*.conf`, `finit.d/*.conf`, and finally all
`finit.d/enabled/*.conf` files. Each directory is a unique group,
where files within each group are sorted alphabetically.
`finit.d/enabled/*.conf` files. With `load-available` enabled,
`finit.d/available/*.conf` is read first of all, so a same-named file
in any of the other directories overrides it. Each directory is a
unique group, where files within each group are sorted alphabetically.

**Example:**

Expand Down Expand Up @@ -204,6 +213,50 @@ another set of .conf files, e.g.:
> This directive is only available from the top-level bootstrap .conf
> file, usually `/etc/finit.conf`.

Loading Available Services
--------------------------

**Syntax:** `load-available = true`

By default Finit only reads `finit.d/enabled/*.conf`, so a service that
has been installed in `finit.d/available/` but not enabled is unknown to
Finit and `initctl start` fails with *no such task or service*. This
setting makes Finit read `finit.d/available/*.conf` as well, so such
services can be started and stopped at runtime without being enabled,
i.e., without starting automatically at boot:

load-available = true

Run, task, service, sysv, and tty stanzas from `available/` are marked
available-only by the loader. There is no configuration key for this
state, and includes inherit it. `manual-start` remains an independent
unit option: it does not enable an available-only unit, even when set to
`false`.

Enabling a service (a symlink in `enabled/`, read last) makes its logical
source enabled. The unit then follows its own `manual-start` setting:
`true` remains manual, while `false` or an omitted setting permits normal
automatic start. Disabling it again leaves it loaded and startable with
`initctl start`; a running instance is not stopped merely by reload.

Only units and their file-scope resource limits are loaded from an
available-only file. Top-level settings, such as `environment`,
`cgroup`, `modules`, `mknod`, `runlevel`, and `shutdown`, are ignored
with a warning until the file is enabled. Unit-local settings such as
`envfile`, `log`, `cgroup`, and `rlimit` remain active so the unit can be
started manually.

Units restricted to the bootstrap `S` runlevel retain the normal Finit
lifecycle: they are discarded after bootstrap and cannot be started in
a regular runlevel. Enable them before the next boot to run them.

> [!NOTE]
> Like `rcsd`, this directive is only available from the top-level
> bootstrap .conf file, usually `/etc/finit.conf`, and only in the
> block-based format. A legacy format `finit.conf` can enable the
> feature with `include /path/to.conf`, where the included block-based
> file sets it.

Including Finit Configs
------------------------

Expand Down
6 changes: 6 additions & 0 deletions doc/config/service-opts.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ It can then be started at any time with `initctl start <service>`:
command = "/usr/sbin/lldpd -d"
}

> [!NOTE]
> With [`load-available`](files.md#loading-available-services) enabled,
> units in `finit.d/available/` are prevented from automatic start by
> their loader-derived available-only state. This is independent of
> `manual-start`, which takes effect normally after the unit is enabled.

Other run/task/service settings are:

* `capabilities` -- see the [Linux Capabilities](capabilities.md) section
Expand Down
3 changes: 2 additions & 1 deletion doc/config/tty.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Settings common to all three:
|---|---|---|
| `runlevel` | | Runlevels to run in, e.g. `"12345"` |
| `conditions` | `cond` | Conditions to wait for |
| `manual-start` | `manual` | Register without starting automatically |
| `noclear` | | Do not clear the TTY after each session |
| `nowait` | | Do not wait for Enter before the login prompt |
| `nologin` | | Skip login, give a shell straight away |
Expand Down Expand Up @@ -164,4 +165,4 @@ connected to the TTY device.
runlevel = "S"
tty = "@console"
command = "my-setup-script"
}
}
24 changes: 24 additions & 0 deletions doc/distro.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,30 @@ directory (e.g., `/etc/finit.d/`), `initctl` will ignore it. This allows
you to create system-level overrides that cannot be accidentally disabled
by package management tools.

### Runtime Control Without Enabling

By default, a `.conf` that only exists in `available/` is not loaded at
all, so `initctl start` cannot control it -- enabling (load) and starting
at boot are one and the same switch. Products that need the systemd-like
separation between "starts at boot" and "can be started/stopped at
runtime" can enable the `load-available` setting in `/etc/finit.conf`:

```
load-available = true
```

With this setting, unit stanzas in `available/` are loaded with an
internal available-only source state: they show up in `initctl status`
and can be started and stopped freely, yet they do not start
automatically until enabled. This state is independent of the unit's
`manual-start` option. Top-level settings in an available-only file
remain inactive. Enabling the file activates those settings and makes
the unit follow `manual-start`; disabling it no longer makes a service
uncontrollable (a running instance is not stopped by `initctl reload`,
mirroring `systemctl disable`). See
[config/files.md](config/files.md#loading-available-services) for
details.

## Customizing Directory Paths

Distributions can customize the directory names and locations at build time
Expand Down
22 changes: 21 additions & 1 deletion man/finit.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.Dd July 10, 2025
.Dd October 29, 2026
.Dt FINIT.CONF 5 SMM
.Os Linux
.Sh NAME
Expand Down Expand Up @@ -62,6 +62,26 @@ file. Please use the
.Cm initctl
tool to manage these symlinks to ensure proper operation.
.Pp
By default, files in
.Pa /etc/finit.d/available/
are not loaded; a service must be enabled to be known to
.Nm Finit .
With the
.Cm load-available = true
setting in
.Pa /etc/finit.conf
(new block-based format only), unit stanzas in
.Pa available/
are registered with an internal available-only source state and are
controllable with
.Cm initctl start/stop ,
but are not started automatically until enabled. This state is separate
from the unit's
.Cm manual-start
option. Top-level settings in those files remain inactive until enabled.
See the Finit documentation for details and the bootstrap-only runlevel
limitation.
.Pp
.Sy NOTE:
Previous versions of
.Nm Finit
Expand Down
3 changes: 2 additions & 1 deletion plugins/modules-load.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ static int modules_load(const char *file, int index)
snprintf(cmd, sizeof(cmd), SERVICE_LINE, mod, index++, lvl, modprobe_path, mod, args);

dbg("task %s", cmd);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL);
service_register(SVC_TYPE_TASK, cmd, global_rlimit, NULL,
SVC_SOURCE_ENABLED);
num++;
next:
free(line);
Expand Down
18 changes: 7 additions & 11 deletions plugins/pidfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,6 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)

nm = svc_ident(svc, NULL, 0);
dbg("Found svc %s for %s with pid %d", nm, fn, svc->pid);
if (svc_is_stopping(svc)) {
dbg("Ignoring pidfile death rattles while service %s is %s", nm, svc_status(svc));
goto sneaky_zebra;
}

if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
/*
Expand All @@ -126,16 +122,13 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
pid_t pid;

pid = pid_file_read(fn);
if (pid == -1) {
/* handle case when service dies immediately */
if (pid <= 1) {
/* handle missing, empty, or otherwise invalid pidfiles */
if (!fexist(fn))
cond_clear(cond);
cond_clear(cond);
return;
}

/* Service is not crashing, let's tell state machine! */
service_forked(svc);

if (pid != svc->pid) {
dbg("Forking service %s (cmd %s) changed PID from %d to %d",
svc_ident(svc, NULL, 0), svc->cmd, svc->pid, pid);
Expand All @@ -145,14 +138,17 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
cgroup_move_svc(svc);
}

/* Service is not crashing, let's tell state machine! */
if (!service_forked(svc))
return;
}

if (svc->notify == SVC_NOTIFY_PID)
svc_cond_set(svc);
if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE)
service_ready(svc, 1);
} else if (mask & IN_DELETE) {
sneaky_zebra:
svc_cond_clear(svc);
if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc, 0);
Expand Down
Loading