Add Lua-ported s6/s6-rc service definitions
Ports the community s6/s6-rc service catalog (early-boot mounts, udev,
dbus, session/power, networking, display managers, peripheral daemons)
to Lua-based service definitions.
- src/lua/s6lua.lua: shared helper library (exec, logging, fstab
parsing, oneshot shell runner, readiness notification)
- src/native/s6exec.c: minimal execvp() binding so longrun `run`
scripts can replace their own process image with the real daemon,
since stock Lua has no exec() and os.execute("exec ...") does not
solve it (the interpreter stays alive as an unsupervised parent)
- tools/catalog.lua + gen-services.lua: table-driven generator for
the structurally identical -srv/-log daemon pairs and agetty ttys
- tools/lint.lua, check-syntax.ps1, set-exec-bits.ps1: local
verification (no live s6/Linux target available this session)
- services/: 50 service directories (14 early-boot oneshots, 12
daemon srv/log pairs, 6 agetty ttys, boot/mount/setup/ttys/
graphical/default bundles)
Authored and syntax/structurally validated only. Native module is
unbuilt and daemon flags are unverified against Artix's real catalog
(gitea was unreachable during research) -- see docs/CONVENTIONS.md.
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# s6-lua
|
||||||
|
|
||||||
|
A port of the community s6/s6-rc service catalog (the kind Artix Linux/Obarun use in place of
|
||||||
|
systemd units) into Lua-based service definitions.
|
||||||
|
|
||||||
|
Stock Lua has no `exec()` binding, and `os.execute("exec daemon")` doesn't solve the problem
|
||||||
|
either — it forks a shell that execs, but the Lua process itself stays alive blocked in
|
||||||
|
`waitpid()`, so s6 ends up supervising the interpreter instead of the daemon. `longrun` services
|
||||||
|
here hand off to the real daemon through a tiny vendored C module (`src/native/s6exec.c`,
|
||||||
|
`execvp()`); `oneshot` and `finish` scripts don't need it since they run to completion rather than
|
||||||
|
persisting. See `docs/CONVENTIONS.md` for the full rationale and naming rules.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
services/ the s6-rc source-definition tree (deploys verbatim to /etc/s6/sv)
|
||||||
|
src/lua/ shared helper library (s6lua.lua) every service script requires
|
||||||
|
src/native/ native exec binding (s6exec.c) + Makefile, Linux-only build
|
||||||
|
tools/ generator, structural linter, syntax checker, exec-bit fixer, installer
|
||||||
|
docs/ conventions and naming rules
|
||||||
|
```
|
||||||
|
|
||||||
|
`services/default` is the top-level bundle (boots to graphical target with SDDM); `services/boot`
|
||||||
|
is the multi-user/console-only target.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Authored and syntax-checked only — there is no live s6/s6-rc install in this environment to boot-
|
||||||
|
test against. See `docs/CONVENTIONS.md` for known unverified gaps (daemon flags reconstructed from
|
||||||
|
convention, not confirmed against Artix's actual catalog) and the recommended next step (install
|
||||||
|
s6/s6-rc/s6-linux-init on a Linux host — Arch is the closest packaging match — and boot-test the
|
||||||
|
`boot` bundle before enabling `graphical`).
|
||||||
|
|
||||||
|
## Local tooling
|
||||||
|
|
||||||
|
Requires `lua55`/`luac55` (this repo was authored against Lua 5.5, written to a subset valid under
|
||||||
|
5.4 too — see `docs/CONVENTIONS.md`).
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
lua55 tools/gen-services.lua # regenerate the -srv/-log pairs + agetty ttys from tools/catalog.lua
|
||||||
|
lua55 tools/lint.lua # structural validation of services/
|
||||||
|
tools/check-syntax.ps1 # luac55 -p over every script
|
||||||
|
tools/set-exec-bits.ps1 # fix the git executable bit before committing (Windows checkout)
|
||||||
|
```
|
||||||
|
|
||||||
|
`tools/install.sh` deploys everything to a real Linux target (untested this session).
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Conventions
|
||||||
|
|
||||||
|
## Lua version target
|
||||||
|
|
||||||
|
Author against a subset valid under **both Lua 5.4 and 5.5**: Arch/Artix currently package Lua
|
||||||
|
5.4, while this repo is authored/syntax-checked locally with `lua55`. Avoid 5.5-only stdlib
|
||||||
|
additions. `docs/CONVENTIONS.md` (this file) is the one place a Windows-side checker and a
|
||||||
|
Linux-side interpreter could silently diverge — if you're unsure whether something is 5.4-safe,
|
||||||
|
don't use it.
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
Service directories under `services/` follow Artix's own `s6-scripts` catalog convention:
|
||||||
|
|
||||||
|
- A daemon's directory is named `<name>-srv` (e.g. `dbus-srv`).
|
||||||
|
- Its logger is named `<name>-log` (e.g. `dbus-log`), wired via `producer-for`/`consumer-for`.
|
||||||
|
- Logs land in `/var/log/<name>` (not `/var/log/<name>-srv`).
|
||||||
|
- Early-boot oneshots (category 1) and one-off oneshots (`coldplug`, `net-lo`, etc.) are named
|
||||||
|
after the action they perform, no suffix (e.g. `mount-cgroups`, `hostname`).
|
||||||
|
- Bundles are named after the target they represent (`boot`, `mount`, `setup`, `ttys`,
|
||||||
|
`graphical`, `default`), not suffixed.
|
||||||
|
|
||||||
|
## Adding a new `-srv`/`-log` daemon pair
|
||||||
|
|
||||||
|
Don't hand-author a new service directory pair. Add a row to `tools/catalog.lua` and rerun:
|
||||||
|
|
||||||
|
```
|
||||||
|
lua55 tools/gen-services.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
This regenerates every entry in the catalog (idempotent — safe to rerun after editing one row).
|
||||||
|
|
||||||
|
## Swapping the default display manager
|
||||||
|
|
||||||
|
`services/default/contents.d/` currently includes `sddm-srv`/`sddm-log` (Artix's own flagship
|
||||||
|
display manager). To switch to GDM or LightDM, remove the `sddm-*` entries from
|
||||||
|
`services/default/contents.d/` and add `gdm-srv`/`gdm-log` or `lightdm-srv`/`lightdm-log` instead
|
||||||
|
— all three are already generated and ready to enable.
|
||||||
|
|
||||||
|
## Exec strategy
|
||||||
|
|
||||||
|
- `longrun` `run` scripts **must** end with `s6.exec({...})` (from `src/lua/s6lua.lua`, backed by
|
||||||
|
the native `s6exec.so` module) as their last statement. This is what makes the supervised
|
||||||
|
process the real daemon rather than a lingering Lua interpreter — see the root README and the
|
||||||
|
plan history for why `os.execute("exec ...")` does not work here.
|
||||||
|
- `oneshot` `up`/`down` scripts and longrun `finish` scripts do **not** need `s6.exec` — they run
|
||||||
|
to completion and exit, so `s6.run`/`os.execute` is safe and preferred (simpler, no native
|
||||||
|
dependency).
|
||||||
|
|
||||||
|
## Git executable bit
|
||||||
|
|
||||||
|
Windows checkouts don't set the tree's executable-bit metadata on `git add`. Run
|
||||||
|
`tools/set-exec-bits.ps1` before committing any new `run`/`up`/`down`/`finish`/`reload`/`check`
|
||||||
|
file — the syntax and lint checks won't catch a missing exec bit, but `s6-supervise` will fail
|
||||||
|
silently on the real target.
|
||||||
|
|
||||||
|
## Known unverified gaps
|
||||||
|
|
||||||
|
Artix's `s6-scripts` gitea repo blocked automated fetches during research, so the daemon
|
||||||
|
argv/flags in `tools/catalog.lua` (chronyd, bluetoothd, acpid, cupsd in particular) are
|
||||||
|
reconstructed from each daemon's documented foreground-mode flag rather than confirmed
|
||||||
|
byte-for-byte against a reference script. Spot-check before relying on this in production.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
acpid-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
acpid
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/acpid")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/acpid" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("acpid", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
acpid-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/sbin/acpid", "-f" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/sbin/agetty", "tty1", "38400", "linux" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/sbin/agetty", "tty2", "38400", "linux" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/sbin/agetty", "tty3", "38400", "linux" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/sbin/agetty", "tty4", "38400", "linux" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/sbin/agetty", "tty5", "38400", "linux" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/sbin/agetty", "tty6", "38400", "linux" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bluez-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bluetoothd
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/bluez")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/bluez" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("bluetoothd", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bluez-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/lib/bluetooth/bluetoothd", "-n" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bundle
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
chronyd-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
chronyd
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/chronyd")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/chronyd" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("chronyd", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
chronyd-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/bin/chronyd", "-n" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
oneshot
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
|
||||||
|
s6.run("find /tmp -mindepth 1 -xdev -delete")
|
||||||
|
s6.run_or_die("chmod 1777 /tmp")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
oneshot
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
|
||||||
|
s6.run_or_die("udevadm trigger --action=add")
|
||||||
|
s6.run_or_die("udevadm settle")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
oneshot
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
|
||||||
|
-- Loads the console font from /etc/vconsole.conf's FONT= line.
|
||||||
|
local f = io.open("/etc/vconsole.conf", "r")
|
||||||
|
if not f then
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
for line in f:lines() do
|
||||||
|
local font = line:match('^FONT=(.+)$')
|
||||||
|
if font then
|
||||||
|
font = font:gsub('^"(.*)"$', "%1")
|
||||||
|
s6.run(string.format("setfont %q", font))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
cupsd-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
cupsd
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/cupsd")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/cupsd" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("cupsd", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
cupsd-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/sbin/cupsd", "-f" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dbus-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dbus
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/dbus")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/dbus" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("dbus", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
dbus-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/bin/dbus-daemon", "--system", "--nofork", "--nopidfile" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bundle
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
elogind-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
elogind
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/elogind")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/elogind" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("elogind", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
elogind-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/lib/elogind/elogind" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
gdm-srv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
gdm
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.mkdirp("/var/log/gdm")
|
||||||
|
s6.exec({ "/usr/bin/s6-log", "-b", "n20", "s10000000", "/var/log/gdm" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.log("gdm", "exited (code=%s signal=%s)", arg[1], arg[2])
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
gdm-log
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local s6 = require("s6lua")
|
||||||
|
s6.exec({ "/usr/bin/gdm" })
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user