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.
110 lines
3.1 KiB
Lua
110 lines
3.1 KiB
Lua
-- Reads tools/catalog.lua and stamps out the -srv/-log longrun pairs and
|
|
-- agetty-tty1..6 under services/. Authored/run on the Windows box with
|
|
-- lua55; portable enough to rerun on a real Linux target later. Files are
|
|
-- written with explicit "\n" line endings in binary mode so Windows doesn't
|
|
-- translate them to CRLF.
|
|
--
|
|
-- Usage: lua55 tools/gen-services.lua
|
|
|
|
local script_dir = arg[0]:match("^(.*)[/\\][^/\\]*$") or "."
|
|
local repo_root = script_dir .. "/.."
|
|
local services_dir = repo_root .. "/services"
|
|
|
|
local catalog = dofile(script_dir .. "/catalog.lua")
|
|
|
|
local function is_windows()
|
|
return package.config:sub(1, 1) == "\\"
|
|
end
|
|
|
|
local function mkdir_p(path)
|
|
if is_windows() then
|
|
local winpath = path:gsub("/", "\\")
|
|
os.execute('if not exist "' .. winpath .. '" mkdir "' .. winpath .. '" >nul 2>nul')
|
|
else
|
|
os.execute('mkdir -p "' .. path .. '"')
|
|
end
|
|
end
|
|
|
|
local function dirname(path)
|
|
return path:match("^(.*)[/\\][^/\\]*$") or "."
|
|
end
|
|
|
|
local function write_file(path, content)
|
|
mkdir_p(dirname(path))
|
|
local f = assert(io.open(path, "wb"))
|
|
f:write(content)
|
|
f:close()
|
|
end
|
|
|
|
local function touch(path)
|
|
write_file(path, "")
|
|
end
|
|
|
|
local function lua_array_literal(items)
|
|
local parts = {}
|
|
for i, v in ipairs(items) do
|
|
parts[i] = string.format("%q", v)
|
|
end
|
|
return "{ " .. table.concat(parts, ", ") .. " }"
|
|
end
|
|
|
|
local function render_srv_run(argv)
|
|
return string.format(
|
|
"#!/usr/bin/lua\nlocal s6 = require(\"s6lua\")\ns6.exec(%s)\n",
|
|
lua_array_literal(argv)
|
|
)
|
|
end
|
|
|
|
local function render_finish(tag)
|
|
return string.format(
|
|
"#!/usr/bin/lua\nlocal s6 = require(\"s6lua\")\ns6.log(%q, \"exited (code=%%s signal=%%s)\", arg[1], arg[2])\n",
|
|
tag
|
|
)
|
|
end
|
|
|
|
local function render_log_run(log_dir)
|
|
return string.format(
|
|
"#!/usr/bin/lua\nlocal s6 = require(\"s6lua\")\ns6.mkdirp(%q)\ns6.exec({ \"/usr/bin/s6-log\", \"-b\", \"n20\", \"s10000000\", %q })\n",
|
|
log_dir, log_dir
|
|
)
|
|
end
|
|
|
|
local function render_agetty_run(n)
|
|
return render_srv_run({ "/sbin/agetty", "tty" .. tostring(n), "38400", "linux" })
|
|
end
|
|
|
|
for _, d in ipairs(catalog.daemons) do
|
|
local srv_dir = services_dir .. "/" .. d.srv
|
|
local log_svc_dir = services_dir .. "/" .. d.log
|
|
|
|
write_file(srv_dir .. "/type", "longrun\n")
|
|
write_file(srv_dir .. "/run", render_srv_run(d.argv))
|
|
write_file(srv_dir .. "/finish", render_finish(d.key))
|
|
write_file(srv_dir .. "/producer-for", d.log .. "\n")
|
|
for _, dep in ipairs(d.deps) do
|
|
touch(srv_dir .. "/dependencies.d/" .. dep)
|
|
end
|
|
|
|
write_file(log_svc_dir .. "/type", "longrun\n")
|
|
write_file(log_svc_dir .. "/run", render_log_run(d.log_dir))
|
|
write_file(log_svc_dir .. "/consumer-for", d.srv .. "\n")
|
|
write_file(log_svc_dir .. "/pipeline-name", d.key .. "\n")
|
|
|
|
print("generated " .. d.srv .. " / " .. d.log)
|
|
end
|
|
|
|
if catalog.ttys and #catalog.ttys > 0 then
|
|
local ttys_dir = services_dir .. "/ttys"
|
|
write_file(ttys_dir .. "/type", "bundle\n")
|
|
for _, n in ipairs(catalog.ttys) do
|
|
local name = "agetty-tty" .. tostring(n)
|
|
local dir = services_dir .. "/" .. name
|
|
write_file(dir .. "/type", "longrun\n")
|
|
write_file(dir .. "/run", render_agetty_run(n))
|
|
touch(ttys_dir .. "/contents.d/" .. name)
|
|
print("generated " .. name)
|
|
end
|
|
end
|
|
|
|
print("done")
|