local rc = require("rc") local fs = require("filesystem") local function loadConfig() local env = {} local result, reason = loadfile('/etc/rc.cfg', 't', env) if result then result, reason = xpcall(result, debug.traceback) if result then return env end end return nil, reason end local function saveConfig(conf) local file, reason = io.open('/etc/rc.cfg', 'w') if not file then return nil, reason end for key, value in pairs(conf) do file:write(tostring(key) .. " = " .. require("serialization").serialize(value) .. "\n") end file:close() return true end local function load(name, args) if rc.loaded[name] then return rc.loaded[name] end local fileName = fs.concat('/etc/rc.d/', name .. '.lua') local env = setmetatable({args = args}, {__index = _G}) local result, reason = loadfile(fileName, 't', env) if result then result, reason = xpcall(result, debug.traceback) if result then rc.loaded[name] = env return env else return nil, string.format("%s failed to start: %s", fileName, reason) end end return nil, string.format("%s failed to load: %s", fileName, reason) end function rc.unload(name) rc.loaded[name] = nil end local function rawRunCommand(conf, name, cmd, args, ...) local result, what = load(name, args) if result then if not cmd then io.output():write("Commands for service " .. name .. "\n") for command, val in pairs(result) do if type(val) == "function" then io.output():write(tostring(command) .. " ") end end return true elseif type(result[cmd]) == "function" then result, what = xpcall(result[cmd], debug.traceback, ...) if result then return true end elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then local daemon = result result, what = xpcall(daemon["stop"], debug.traceback, ...) if result then result, what = xpcall(daemon["start"], debug.traceback, ...) if result then return true end end elseif cmd == "enable" then conf.enabled = conf.enabled or {} for _, _name in ipairs(conf.enabled) do if name == _name then return nil, "Service already enabled" end end conf.enabled[#conf.enabled + 1] = name return saveConfig(conf) elseif cmd == "disable" then conf.enabled = conf.enabled or {} for n, _name in ipairs(conf.enabled) do if name == _name then table.remove(conf.enabled, n) end end return saveConfig(conf) else what = "Command '" .. cmd .. "' not found in daemon '" .. name .. "'" end end return nil, what end local function runCommand(name, cmd, ...) local conf, reason = loadConfig() if not conf then return nil, reason end return rawRunCommand(conf, name, cmd, conf[name], ...) end local function allRunCommand(cmd, ...) local conf, reason = loadConfig() if not conf then return nil, reason end local results = {} for _, name in ipairs(conf.enabled or {}) do results[name] = table.pack(rawRunCommand(conf, name, cmd, conf[name], ...)) end return results end local stream = io.stderr local write = stream.write if select("#", ...) == 0 then -- if called during boot, pipe errors to onError handler if _G.runlevel == "S" then write = function(_, msg) require("event").onError(msg) end end local results, reason = allRunCommand("start") if not results then local msg = "rc failed to start:"..tostring(reason) write(stream, msg, "\n") return end for _, result in pairs(results) do local ok, reason = table.unpack(result) if not ok then write(stream, reason, "\n") end end else local result, reason = runCommand(...) if not result then write(stream, reason, "\n") return 1 end end