mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b9cebbfd3 | ||
|
|
38791616e0 | ||
|
|
fede3fd2e1 | ||
|
|
a46a33238d | ||
|
|
afe0488bf2 | ||
|
|
ec5e42694d | ||
|
|
2e73563eea |
+8
-1
@@ -4,7 +4,7 @@ Change Log
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[4.5][] - 2023-09-24
|
||||
[4.5][] - 2023-10-03
|
||||
--------------------
|
||||
|
||||
### Changes
|
||||
@@ -16,6 +16,9 @@ All relevant changes are documented in this file.
|
||||
- Add `initctl -f` to force-skip asking Finit for existing services
|
||||
when creating new services during bootstrap, e.g. `/etc/rc.local`
|
||||
- `initctl runlevel` now returns `N S` instead of `N 10` in bootstrap
|
||||
- `initctl runlevel N` during bootstrap is now allowed. It changes
|
||||
the next runlevel to go to when bootstrap has completed. Effectively
|
||||
overriding the `runlevel N` statement in `/etc/finit.conf`
|
||||
- Improved logging on failure to `execvp()` a run/task/service, now
|
||||
with `errno`, e.g., "No such file or directory" when the command
|
||||
is missing from `$PATH`
|
||||
@@ -30,6 +33,10 @@ All relevant changes are documented in this file.
|
||||
- Increased MAX path for commands, and arguments: 64 -> 256
|
||||
- The bundled watchdog daemon no longer tries to log at startup, because
|
||||
syslog is not available yet, any startup message leak to console
|
||||
- Extend `if:` option with runtime evaluation of conditions. E.g., start
|
||||
a task only `if:<run/foo/failure>` (here the run task 'foo' failed)
|
||||
- Document new `if:`, `conflict:`, `nowarn` options for run/task/service
|
||||
first introduced in v4.4
|
||||
|
||||
### Fixes
|
||||
- Fix #227: believed to have been fixed in v4.3, the root cause was
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
AC_INIT([Finit],[4.5-rc3],[https://github.com/troglobit/finit/issues],[finit],[https://troglobit.com/projects/finit/])
|
||||
AC_INIT([Finit], [4.5-rc4], [https://github.com/troglobit/finit/issues],
|
||||
[finit], [https://troglobit.com/projects/finit/])
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
+78
-8
@@ -164,14 +164,19 @@ everything is OK.
|
||||
task [S] /lib/console-setup/console-setup.sh
|
||||
service [S12345] env:-/etc/default/rsyslog rsyslogd -n $RSYSLOGD_ARGS
|
||||
|
||||
After runlevel S, Finit proceeds to runlevel 2. (This can be changed in
|
||||
`/etc/finit.conf` using the `runlevel N` directive.) Before starting
|
||||
the services in runlevel 2, Finit first stops everything that is not
|
||||
allowed to run in 2, and then brings up networking. Networking is
|
||||
expected to be available in all runlevels except: S, 1 (single user
|
||||
level), 6, and 0. Networking is enabled either by the `network script`
|
||||
directive, or if you have an `/etc/network/interfaces` file, Finit calls
|
||||
`ifup -a` -- at the very least the loopback interface is brought up.
|
||||
After runlevel S, Finit proceeds to runlevel 2. This can be changed in
|
||||
`/etc/finit.conf` using the `runlevel N` directive, or by a script
|
||||
running in runlevel S that calls, e.g., `initctl runlevel 9`. The
|
||||
latter is useful if startup scripts detect problems outside of Finit's
|
||||
control, e.g., critical services/devices missing or hardware problems.
|
||||
|
||||
Before starting the services in runlevel 2, Finit first stops everything
|
||||
that is not allowed to run in 2, and then brings up networking.
|
||||
Networking is expected to be available in all runlevels except: S, 1
|
||||
(single user level), 6, and 0. Networking is enabled either by the
|
||||
`network script` directive, or if you have an `/etc/network/interfaces`
|
||||
file, Finit calls `ifup -a` -- at the very least the loopback interface
|
||||
is brought up.
|
||||
|
||||
> **Note:** when moving from runlevel S to 2, all run/task/services that
|
||||
> were constrained to runlevel S only are dropped from bookkeeping. So
|
||||
@@ -839,6 +844,71 @@ These script actions *must terminate*, so they have a default execution
|
||||
time of 3 seconds before they are SIGKILLed, this can be adjusted using
|
||||
the above `kill:SEC` syntax.
|
||||
|
||||
#### Conditional Loading
|
||||
|
||||
Finit support conditional loading of stanzas. The following example is
|
||||
take from the `system/hotplug.conf` file in the Finit distribution.
|
||||
Here we only show a simplified subset.
|
||||
|
||||
Starting with the `nowarn` option.
|
||||
|
||||
service nowarn name:udevd pid:udevd /lib/systemd/systemd-udevd
|
||||
service nowarn name:udevd pid:udevd udevd
|
||||
|
||||
When loading the .conf file Finit looks for `/lib/systemd/systemd-udevd`
|
||||
if that is not found Finit automatically logs a warning. The `nowarn`
|
||||
option disables this warning so that the second line can be evaluated,
|
||||
which also provides a service named `udevd`.
|
||||
|
||||
run nowarn if:udevd <pid/udevd> udevadm settle -t 0
|
||||
|
||||
This line is only loaded if we know of a service named `udevd`. Again,
|
||||
we do not warn if `udevadm` is not found, Execution will also stop here
|
||||
until the PID condition is asserted, i.e., Finit detecting udevd has
|
||||
started.
|
||||
|
||||
run nowarn conflict:udevd [S] mdev -s -- Populating device tree
|
||||
|
||||
If `udevd` is not available, we try to run `mdev`, but if that is not
|
||||
found, again we do not warn.
|
||||
|
||||
Conditional loading statements can also be negated, so the previous stanza
|
||||
can also be written as:
|
||||
|
||||
run nowarn if:!udevd [S] mdev -s -- Populating device tree
|
||||
|
||||
The reason for using `confict` in this example is that a conflict can be
|
||||
resolved. Stanzas marked with `conflict:foo` are rechecked at runtime.
|
||||
|
||||
#### Conditional Execution
|
||||
|
||||
Similar to conditional loading of stanzas there is conditional runtime
|
||||
execution. This can be confusing at first, since Finit already has a
|
||||
condition subsystem, but this is more akin to the qualification to a
|
||||
runlevel. E.g., a `task [123]` is qualified to run only in runlevel 1,
|
||||
2, and 3. It is not considered for other runlevels.
|
||||
|
||||
Conditional execution qualify a run/task/service based on a condition.
|
||||
Consider this (simplified) example from the Infix operating system:
|
||||
|
||||
run [S] name:startup <pid/sysrepo> confd -b --load startup-config
|
||||
run [S] if:<usr/fail-startup> name:failure <pid/sysrepo> confd --load failure-config
|
||||
|
||||
The two run statements reside in the same .conf file so Finit runs them
|
||||
in true sequence. If loading the file `startup-config` fails confd sets
|
||||
the condition `usr/fail-startup`, thus allowing the next run statement
|
||||
to load `failure-config`.
|
||||
|
||||
Notice critical the difference between `<pid/sysrepo>` condition and
|
||||
`if:<usr/fail-startup>`. The former is a condition for starting and the
|
||||
latter is a condition to check if a run/task/service is qualified to
|
||||
even be considered.
|
||||
|
||||
Conditional execution statements can also be negated, so provided the
|
||||
file loaded did the opposite, i.e., set a condition on success, the
|
||||
previous stanza can also be written as:
|
||||
|
||||
run [S] if:<!usr/startup-ok> name:failure <pid/sysrepo> confd ...
|
||||
|
||||
### Run-parts Scripts
|
||||
|
||||
|
||||
@@ -240,6 +240,11 @@ Show top-like listing based on cgroups.
|
||||
List installed plugins.
|
||||
.It Nm Ar runlevel Op Ar 0-9
|
||||
Show or set runlevel: 0 halt, 6 reboot.
|
||||
.Pp
|
||||
If called at boot (runlevel S) to set the runlevel, Finit only schedules
|
||||
the change, effectively overriding the configured runlevel from
|
||||
.Pa /etc/finit.conf .
|
||||
Useful to trigger a fail-safe mode, or similar.
|
||||
.It Nm Ar reboot
|
||||
Reboot system, default if
|
||||
.Cm reboot
|
||||
|
||||
@@ -367,7 +367,6 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
}
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
case INIT_CMD_RELOAD:
|
||||
case INIT_CMD_START_SVC:
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
@@ -378,7 +377,7 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
case INIT_CMD_POWEROFF:
|
||||
case INIT_CMD_SUSPEND:
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||
warnx("Unsupported command in runlevel S and 6.");
|
||||
warnx("Unsupported command in runlevel S and 6/0.");
|
||||
goto leave;
|
||||
}
|
||||
default:
|
||||
@@ -387,6 +386,14 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
/* Allow changing cfglevel in runlevel S */
|
||||
if (IS_RESERVED_RUNLEVEL(runlevel)) {
|
||||
if (runlevel != INIT_LEVEL) {
|
||||
warnx("Cannot abort runlevel 6/0.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (rq.runlevel) {
|
||||
case 's':
|
||||
case 'S':
|
||||
@@ -400,7 +407,12 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
halt = SHUT_OFF;
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(lvl);
|
||||
|
||||
/* User requested change in next runlevel */
|
||||
if (runlevel == INIT_LEVEL)
|
||||
cfglevel = lvl;
|
||||
else
|
||||
service_runlevel(lvl);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ static void devmon_cb(uev_t *w, void *arg, int events)
|
||||
continue;
|
||||
}
|
||||
|
||||
dbg("Got %s", ev->name);
|
||||
// dbg("Got %s", ev->name);
|
||||
if (ev->mask & (IN_CREATE | IN_DELETE))
|
||||
devmon_update_conds(iwp->path, ev->name, ev->mask);
|
||||
}
|
||||
|
||||
+1
-1
@@ -288,7 +288,7 @@ static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
/* Stop watcher, callback may close descriptor on us ... */
|
||||
uev_io_stop(w);
|
||||
|
||||
dbg("Calling I/O %s from runloop...", basename(p->name));
|
||||
// dbg("Calling I/O %s from runloop...", basename(p->name));
|
||||
p->io.cb(p->io.arg, w->fd, events);
|
||||
|
||||
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||
|
||||
+11
-7
@@ -836,7 +836,7 @@ static int service_start(svc_t *svc)
|
||||
|
||||
fail:
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
if (do_progress)
|
||||
if (do_progress && !run_block_pid)
|
||||
print_result(result);
|
||||
|
||||
return result;
|
||||
@@ -1847,11 +1847,6 @@ void service_monitor(pid_t lost, int status)
|
||||
return;
|
||||
}
|
||||
|
||||
if (lost == run_block_pid) {
|
||||
svc_mark_clean(svc); /* done, regardless of exit status */
|
||||
run_block_pid = 0;
|
||||
}
|
||||
|
||||
switch (svc->state) {
|
||||
case SVC_CLEANUP_STATE:
|
||||
case SVC_SETUP_STATE:
|
||||
@@ -1900,6 +1895,13 @@ done:
|
||||
/* No longer running, update books. */
|
||||
svc->start_time = svc->pid = 0;
|
||||
cont:
|
||||
if (lost == run_block_pid) {
|
||||
svc_mark_clean(svc); /* done, regardless of exit status */
|
||||
run_block_pid = 0;
|
||||
if (svc->desc[0])
|
||||
print_result(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
|
||||
}
|
||||
|
||||
if (!service_step(svc)) {
|
||||
/* Clean out any bootstrap tasks, they've had their time in the sun. */
|
||||
if (svc_clean_bootstrap(svc))
|
||||
@@ -1955,7 +1957,7 @@ void service_mark_unavail(void)
|
||||
if (!svc->ifstmt[0])
|
||||
continue;
|
||||
|
||||
if (!svc_ifthen(svc_ident(svc, buf, sizeof(buf)), svc->ifstmt))
|
||||
if (!svc_ifthen(1, svc_ident(svc, buf, sizeof(buf)), svc->ifstmt))
|
||||
svc_mark(svc);
|
||||
}
|
||||
}
|
||||
@@ -2294,9 +2296,11 @@ restart:
|
||||
svc_set_state(svc, SVC_WAITING_STATE);
|
||||
else {
|
||||
if (svc_is_conflict(svc)) {
|
||||
#if 0
|
||||
logit(svc->nowarn ? LOG_DEBUG : LOG_INFO,
|
||||
"%s in conflict with %s, checking again ...",
|
||||
svc_ident(svc, NULL, 0), svc->conflict);
|
||||
#endif
|
||||
if (!svc_conflicts(svc))
|
||||
svc_unblock(svc);
|
||||
}
|
||||
|
||||
@@ -634,17 +634,30 @@ int svc_enabled(svc_t *svc)
|
||||
if (!svc)
|
||||
return 0;
|
||||
|
||||
if (!svc_in_runlevel(svc, runlevel))
|
||||
if (!svc_in_runlevel(svc, runlevel)) {
|
||||
// dbg("Not in runlevel");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc)) {
|
||||
// dbg("removed (%d) / blocked (%d)", svc->removed, svc->block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (svc_is_missing(svc))
|
||||
if (svc_is_missing(svc)) {
|
||||
// dbg("missing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (svc_is_tty(svc) && bootstrap)
|
||||
if (!svc_ifthen(0, NULL, svc->ifstmt)) {
|
||||
// dbg("ifthen:%s>", svc->ifstmt[0] ? svc->ifstmt : "<");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (svc_is_tty(svc) && bootstrap) {
|
||||
// dbg("is tty in bootstrap");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -688,13 +701,14 @@ int svc_conflicts(svc_t *svc)
|
||||
|
||||
/**
|
||||
* svc_ifthen - Check if: statement in declaration
|
||||
* @is_conf: Set when called by service_mark_unavail()
|
||||
* @ident: svc_t identififcation (name:id)
|
||||
* @stmt: if: statement from .conf file
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) yes, use this svc_t, %FALSE(0) prune
|
||||
*/
|
||||
int svc_ifthen(const char *ident, char *stmt)
|
||||
int svc_ifthen(int is_conf, const char *ident, char *stmt)
|
||||
{
|
||||
int not = 0;
|
||||
svc_t *svc;
|
||||
@@ -707,6 +721,9 @@ int svc_ifthen(const char *ident, char *stmt)
|
||||
char *ptr = ++stmt;
|
||||
int i = 0;
|
||||
|
||||
if (is_conf)
|
||||
return 1;
|
||||
|
||||
if (stmt[0] == '!') {
|
||||
stmt++;
|
||||
not++;
|
||||
@@ -717,17 +734,16 @@ int svc_ifthen(const char *ident, char *stmt)
|
||||
ptr[i] = 0;
|
||||
|
||||
cond = cond_get(stmt);
|
||||
if (not && cond == COND_ON) {
|
||||
logit(LOG_NOTICE, "skipping %s, cond %s is on.", ident, stmt);
|
||||
if (not && cond == COND_ON)
|
||||
return 0;
|
||||
}
|
||||
if (!not && cond == COND_OFF) {
|
||||
logit(LOG_NOTICE, "skipping %s, cond %s is off.", ident, stmt);
|
||||
if (!not && cond == COND_OFF)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!is_conf)
|
||||
return 1;
|
||||
|
||||
if (stmt[0] == '!') {
|
||||
stmt++;
|
||||
not++;
|
||||
|
||||
@@ -241,7 +241,7 @@ void svc_prune_bootstrap (void);
|
||||
void svc_enable (svc_t *svc);
|
||||
int svc_enabled (svc_t *svc);
|
||||
int svc_conflicts (svc_t *svc);
|
||||
int svc_ifthen (const char *ident, char *ifstm);
|
||||
int svc_ifthen (int is_conf, const char *ident, char *stmt);
|
||||
|
||||
int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user