Advertisement
EnderShadowN7

LuxOS installer

Feb 22nd, 2025 (edited)
327
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 93.05 KB | None | 0 0
  1. local DIRECTORY = 1
  2. local FILE = 2
  3. local ENTRY_POINT = "LuxOS/main.lua"
  4.  
  5. local package = {
  6.     name = "LuxOS",
  7.     type = DIRECTORY,
  8.     children = {
  9.         {
  10.             name = "autorun",
  11.             type = DIRECTORY,
  12.             children = {
  13.                 {
  14.                     name = "lib.lua",
  15.                     type = FILE,
  16.                     code = 1
  17.                 },
  18.                 {
  19.                     name = "registered",
  20.                     type = DIRECTORY,
  21.                     children = {
  22.  
  23.                     }
  24.                 },
  25.                 {
  26.                     name = "routine.lua",
  27.                     type = FILE,
  28.                     code = 2
  29.                 }
  30.             }
  31.         },
  32.         {
  33.             name = "boot",
  34.             type = DIRECTORY,
  35.             children = {
  36.                 {
  37.                     name = "routine.lua",
  38.                     type = FILE,
  39.                     code = 3
  40.                 }
  41.             }
  42.         },
  43.         {
  44.             name = "kernel.lua",
  45.             type = FILE,
  46.             code = 4
  47.         },
  48.         {
  49.             name = "lux.lua",
  50.             type = FILE,
  51.             code = 5
  52.         },
  53.         {
  54.             name = "luxnet",
  55.             type = DIRECTORY,
  56.             children = {
  57.                 {
  58.                     name = "lib.lua",
  59.                     type = FILE,
  60.                     code = 6
  61.                 },
  62.                 {
  63.                     name = "routine.lua",
  64.                     type = FILE,
  65.                     code = 7
  66.                 }
  67.             }
  68.         },
  69.         {
  70.             name = "luxUI",
  71.             type = DIRECTORY,
  72.             children = {
  73.                 {
  74.                     name = "routine.lua",
  75.                     type = FILE,
  76.                     code = 8
  77.                 }
  78.             }
  79.         },
  80.         {
  81.             name = "main.lua",
  82.             type = FILE,
  83.             code = 9
  84.         },
  85.         {
  86.             name = "processes",
  87.             type = DIRECTORY,
  88.             children = {
  89.                 {
  90.                     name = "lib.lua",
  91.                     type = FILE,
  92.                     code = 10
  93.                 }
  94.             }
  95.         },
  96.         {
  97.             name = "services",
  98.             type = DIRECTORY,
  99.             children = {
  100.                 {
  101.                     name = "lib.lua",
  102.                     type = FILE,
  103.                     code = 11
  104.                 },
  105.                 {
  106.                     name = "routine.lua",
  107.                     type = FILE,
  108.                     code = 12
  109.                 },
  110.                 {
  111.                     name = "scripts",
  112.                     type = DIRECTORY,
  113.                     children = {
  114.  
  115.                     }
  116.                 }
  117.             }
  118.         },
  119.         {
  120.             name = "syscall.lua",
  121.             type = FILE,
  122.             code = 13
  123.         }
  124.     }
  125. }
  126.  
  127. local raw_package = {
  128. [[--]].."[["..[[
  129. This the standart autorun Lux API. Use it to register/unregister scripts that should be run at startup.
  130. ]].."]]"..[[
  131.  
  132. _G.autorun = {}     -- The autorun Lux API. Allows you to add and manage Lua script files to be run at system startup.
  133.  
  134.  
  135.  
  136.  
  137.  
  138. autorun.register = syscall.new(
  139.     "autorun.register",
  140.     ---Registers a script file to be run at system startup.
  141.     ---@param script_path string The path to the script file to register.
  142.     ---@return string name The name under which the script will be referenced.
  143.     function (script_path)
  144.         if type(script_path) ~= "string" then
  145.             error("expected string, got '"..type(script_path).."'", 2)
  146.         end
  147.         local ok, name_or_err = syscall.trampoline(script_path)
  148.         if ok then
  149.             return name_or_err
  150.         else
  151.             error(name_or_err, 2)
  152.         end
  153.     end
  154. )
  155.  
  156.  
  157. autorun.unregister = syscall.new(
  158.     "autorun.unregister",
  159.     ---Unregisters an autorun script that has been run at system startup.
  160.     ---@param name string The name under which the script is registered.
  161.     function (name)
  162.         if type(name) ~= "string" then
  163.             error("expected string, got '"..type(name).."'", 2)
  164.         end
  165.         local ok, err = syscall.trampoline(name)
  166.         if not ok then
  167.             error(err, 2)
  168.         end
  169.     end
  170. )
  171.  
  172.  
  173. autorun.enumerate = syscall.new(
  174.     "autorun.enumerate",
  175.     ---Returns the array of the names of the currently registered autorun scripts.
  176.     ---@return string[] names The names of the autorun scripts.
  177.     function ()
  178.         local ok, names_or_err = syscall.trampoline()
  179.         if not ok then
  180.             error(names_or_err, 2)
  181.         else
  182.             return names_or_err
  183.         end
  184.     end
  185. )
  186.  
  187.  
  188. autorun.running_startup_scripts = syscall.new(
  189.     "autorun.running_startup_scripts",
  190.     ---Returns the list of currently running startup scripts.
  191.     ---@return string[] running The running script names.
  192.     function ()
  193.         local ok, running_or_err = syscall.trampoline()
  194.         if not ok then
  195.             error(running_or_err, 2)
  196.         else
  197.             return running_or_err
  198.         end
  199.     end
  200. )
  201.  
  202.  
  203. ---Returns true if all the startup scripts have returned.
  204. ---@return boolean finished Is startup finished?
  205. function autorun.is_startup_finished()
  206.     return #(autorun.running_startup_scripts()) == 0
  207. end
  208.  
  209.  
  210.  
  211.  
  212.  
  213. return {["autorun"] = autorun}]],
  214. [[--]].."[["..[[
  215. Routine for the autorun Lux package. Runs all the scripts registered for startup then halts.
  216. ]].."]]"..[[
  217.  
  218.  
  219.  
  220.  
  221.  
  222. local AUTORUN_FS_STRUCTURE = kernel.filesystem_structure{
  223.  
  224.     kernel.filesystem_node{
  225.         name = "LuxOS",
  226.         type = kernel.DIRECTORY,
  227.         children = {
  228.  
  229.             kernel.filesystem_node{
  230.                 name = "autorun",
  231.                 type = kernel.DIRECTORY,
  232.                 children = {
  233.  
  234.                     kernel.filesystem_node{
  235.                         name = "registered",
  236.                         type = kernel.DIRECTORY
  237.                     },
  238.  
  239.                     kernel.filesystem_node{
  240.                         name = "routine.lua",
  241.                         type = kernel.FILE
  242.                     },
  243.  
  244.                     kernel.filesystem_node{
  245.                         name = "lib.lua",
  246.                         type = kernel.FILE
  247.                     }
  248.  
  249.                 }
  250.             }
  251.  
  252.         }
  253.     }
  254.  
  255. }
  256.  
  257. local REGISTERED_SCRIPTS_DIR = "LuxOS/autorun/registered/"
  258. local script_coroutines = {}       ---@type {[string] : thread}
  259.  
  260. ---Answers the system calls to autorun.register.
  261. local function answer_calls_to_register(...)
  262.     local args = {...}
  263.     if #args ~= 1 then
  264.         return false, "syscall got "..tostring(#args).." parameters, expected 1"
  265.     else
  266.         local script_path = args[1]
  267.         if type(script_path) ~= "string" then
  268.             return false, "expected string, got '"..type(args[1]).."'"
  269.         elseif not kernel.panic_pcall("fs.exists", fs.exists, script_path) then
  270.             return false, "given script file does not exist: '"..script_path.."'"
  271.         elseif kernel.panic_pcall("fs.isDir", fs.isDir, script_path) then
  272.             return false, "given script file is not a file: '"..script_path.."'"
  273.         else
  274.             local name = kernel.panic_pcall("fs.getName", fs.getName, script_path)
  275.             if kernel.panic_pcall("fs.exists", fs.exists, REGISTERED_SCRIPTS_DIR..name) then
  276.                 return false, "a registered autorun script with name '"..name.."' already exists."
  277.             else
  278.                 kernel.panic_pcall("fs.copy", fs.copy, script_path, REGISTERED_SCRIPTS_DIR..name)
  279.                 return true, name
  280.             end
  281.         end
  282.     end
  283. end
  284.  
  285. ---Answers the system calls to autorun.unregister.
  286. local function answer_calls_to_unregister(...)
  287.     local args = {...}
  288.     if #args ~= 1 then
  289.         return false, "syscall got "..tostring(#args).." parameters, expected 1"
  290.     else
  291.         local name = args[1]
  292.         if type(name) ~= "string" then
  293.             return false, "expected string, got '"..type(args[1]).."'"
  294.         elseif not kernel.panic_pcall("fs.exists", fs.exists, REGISTERED_SCRIPTS_DIR..name) then
  295.             return false, "no such registered script: '"..name.."'"
  296.         else
  297.             kernel.panic_pcall("fs.delete", fs.delete, REGISTERED_SCRIPTS_DIR..name)
  298.             return true
  299.         end
  300.     end
  301. end
  302.  
  303. ---Answers the system calls to autorun.enumerate.
  304. local function answer_calls_to_enumerate(...)
  305.     local args = {...}
  306.     if #args > 0 then
  307.         return false, "syscall got "..tostring(#args).." parameters, expected 0"
  308.     else
  309.         return true, kernel.panic_pcall("fs.list", fs.list, REGISTERED_SCRIPTS_DIR)
  310.     end
  311. end
  312.  
  313. local function answer_calls_to_running_startup_scripts(...)
  314.     local args = {...}
  315.     if #args > 0 then
  316.         return false, "syscall got "..tostring(#args).." parameters, expected 0"
  317.     else
  318.         local running = {}      ---@type string[]
  319.         for name, coro in pairs(script_coroutines) do
  320.             if coroutine.status(coro) ~= "dead" then
  321.                 table.insert(running, name)
  322.             end
  323.         end
  324.         return true, running
  325.     end
  326. end
  327.  
  328. ---Internal function that runs the given program in user space.
  329. ---@param path string The path to the Lua script file to run
  330. local function execute_program(path)
  331.     coroutine.yield()
  332.     local func, err = loadfile(path, os.create_user_environment())
  333.     if not func then
  334.         term.setTextColor(colors.red)
  335.         print("Error while executing autorun script '"..path.."':\n"..err)
  336.     end
  337.     local ok, err = kernel.run_function_in_user_space(pcall, func)
  338.     if not ok then
  339.         term.setTextColor(colors.red)
  340.         print("Error while executing autorun script '"..path.."':\n"..err)
  341.     end
  342. end
  343.  
  344. ---Runs all the autorun-registered script files in parallel and returns.
  345. local function execute_autorun()
  346.  
  347.     for index, script_name in pairs(kernel.panic_pcall("fs.list", fs.list, "LuxOS/autorun/registered")) do
  348.         local script_path = "LuxOS/autorun/registered/"..script_name
  349.         if kernel.panic_pcall("fs.isDir", fs.isDir, script_path) then
  350.             kernel.panic("A folder '"..script_name.."' found its way in the 'LuxOS/autorun/registered/' script directory.")
  351.         end
  352.         local coro = coroutine.create(kernel.panic_pcall)
  353.         kernel.promote_coroutine(coro)
  354.         local ok, err = coroutine.resume(coro, "execute_program", execute_program, script_path)
  355.         if not ok then
  356.             kernel.panic("Error while creating autorun script coroutine for '"..script_name.."':\n"..err)
  357.         end
  358.         script_coroutines[script_name] = coro
  359.     end
  360.  
  361.     syscall.affect_routine(autorun.register, answer_calls_to_register)
  362.     syscall.affect_routine(autorun.unregister, answer_calls_to_unregister)
  363.     syscall.affect_routine(autorun.enumerate, answer_calls_to_enumerate)
  364.     syscall.affect_routine(autorun.running_startup_scripts, answer_calls_to_running_startup_scripts)
  365.  
  366.     kernel.mark_routine_ready()
  367.     local running = true
  368.     local event = {}
  369.     while true do
  370.         running = false
  371.         for name, coro in pairs(script_coroutines) do
  372.             if coroutine.status(coro) ~= "dead" then
  373.                 coroutine.resume(coro, table.unpack(event))
  374.             end
  375.             if coroutine.status(coro) ~= "dead" then
  376.                 running = true
  377.             end
  378.         end
  379.         if kernel.is_system_shutting_down() then
  380.             kernel.mark_routine_offline(false)
  381.         end
  382.         if running then
  383.             event = {coroutine.yield()}
  384.         else
  385.             return
  386.         end
  387.     end
  388. end
  389.  
  390.  
  391. local function main()
  392.  
  393.     kernel.validate_filesystem_structure(AUTORUN_FS_STRUCTURE)
  394.     execute_autorun()
  395.     kernel.mark_routine_offline()
  396.  
  397. end
  398.  
  399. return main]],
  400. [[--]].."[["..[[
  401. This routine ensures that LuxOS will boot successfully.
  402. ]].."]]"..[[
  403.  
  404. -- while true do print(textutils.serialize({os.pullEvent()})) end
  405.  
  406.  
  407.  
  408.  
  409.  
  410. local WATCH_KEYS = {keys.s, keys.r}
  411. local CTRL_LEFT = keys.leftCtrl
  412. local CTRL_RIGHT = keys.rightCtrl
  413. local BOOT_FS_STRUCTURE = kernel.filesystem_structure{
  414.  
  415.     kernel.filesystem_node{
  416.         name = "LuxOS",
  417.         type = kernel.DIRECTORY,
  418.         children = {
  419.  
  420.             kernel.filesystem_node{
  421.                 name = "boot",
  422.                 type = kernel.DIRECTORY,
  423.                 children = {
  424.  
  425.                     kernel.filesystem_node{
  426.                         name = "routine.lua",
  427.                         type = kernel.FILE
  428.                     }
  429.  
  430.                 }
  431.             }
  432.         }
  433.     }
  434.  
  435. }
  436.  
  437.  
  438.  
  439.  
  440.  
  441. local function main()
  442.  
  443.     kernel.validate_filesystem_structure(BOOT_FS_STRUCTURE)
  444.  
  445.     local settings_set = settings.set
  446.     local settings_save = settings.save
  447.     local open = fs.open
  448.  
  449.     local ctrl_left = false
  450.     local ctrl_right = false
  451.     local last_save = -math.huge
  452.  
  453.     local function ensure_boot(force)
  454.         if force or os.time() - last_save > 0.01 then
  455.             settings_set("shell.allow_startup", true)
  456.             settings_set("shell.allow_disk_startup", false)
  457.             settings_save(".settings")
  458.             local file = open("startup", "w")
  459.             file.write("dofile('LuxOS/main.lua')")
  460.             file.close()
  461.             last_save = os.time()
  462.         end
  463.     end
  464.  
  465.     kernel.panic_pcall("ensure_boot", ensure_boot)
  466.  
  467.     kernel.mark_routine_ready()
  468.  
  469.     while true do
  470.         local event = {coroutine.yield()}
  471.         if kernel.is_system_shutting_down() then
  472.             break
  473.         end
  474.         if event[1] == "key" then
  475.             local key = event[2]
  476.             if key == CTRL_LEFT then
  477.                 ctrl_left = true
  478.             elseif key == CTRL_RIGHT then
  479.                 ctrl_right = true
  480.             elseif ctrl_left or ctrl_right then
  481.                 for _, k in ipairs(WATCH_KEYS) do
  482.                     if k == key then
  483.                         kernel.panic_pcall("ensure_boot", ensure_boot)
  484.                     end
  485.                 end
  486.             end
  487.         elseif event[1] == "key_up" then
  488.             local key = event[2]
  489.             if key == CTRL_LEFT then
  490.                 ctrl_left = false
  491.             elseif key == CTRL_RIGHT then
  492.                 ctrl_right = false
  493.             end
  494.         end
  495.     end
  496.  
  497.     kernel.panic_pcall("ensure_boot", ensure_boot, true)
  498.  
  499.     kernel.mark_routine_offline()
  500.  
  501. end
  502.  
  503. return main]],
  504. [[--]].."[["..[[
  505. The Lux kernel API. Used only by Lux code!
  506. ]].."]]"..[[
  507.  
  508. _G.kernel = {} --- The kernel API. Used mostly by the Lux kernel. Most of its functions are not available in user space.
  509. libraries.kernel = kernel
  510.  
  511.  
  512.  
  513.  
  514.  
  515. local main_coro = coroutine.running()
  516. local kernel_coroutines = {[main_coro] = main_coro}    ---@type {[thread] : thread} Stores the kernel coroutines. The Key is the allowed coroutine and the value is the coroutine that added it.
  517.  
  518. ---Checks that the caller runs in kernel space. Throws an error above ther caller otherwise.
  519. function kernel.check_kernel_space_before_running()
  520.     if not kernel.kernel_space() then
  521.         error("Kernel-space-only function.", 3)
  522.     end
  523. end
  524.  
  525.  
  526. local current_coroutine = coroutine.running
  527.  
  528. ---Returns whether or not this code is running in kernel space.
  529. ---@return boolean in_kernel_space If true, you are currently in kernel space. false otherwise.
  530. function kernel.kernel_space()
  531.     local coro, is_main = current_coroutine()
  532.     return kernel_coroutines[coro] ~= nil
  533. end
  534.  
  535.  
  536. ---Makes a coroutine a kernel coroutine.
  537. ---@param coro thread
  538. function kernel.promote_coroutine(coro)
  539.     kernel.check_kernel_space_before_running()
  540.     local current_coroutine = current_coroutine()
  541.     kernel_coroutines[coro] = current_coroutine
  542. end
  543.  
  544.  
  545.  
  546.  
  547.  
  548. ---Lux Kernel panic. Stops everything and prints the error.
  549. ---@param message string The message to print on the purple screen.
  550. ---@param level integer? The stacktrace level to trace the error back to. Default (0) is the the line where panic was called, 1 is where the function that called panic was called, etc.
  551. function kernel.panic(message, level)
  552.     kernel.check_kernel_space_before_running()
  553.     if level == nil then
  554.         level = 0
  555.     end
  556.     if type(message) ~= "string" then
  557.         local ok, res = pcall(textutils.serialise, message)
  558.         if ok then
  559.             message = "Panic error: panic() received a non-string object:\n"..res
  560.         else      
  561.             message = "Panic error: panic() received a non-string object:\n"..tostring(message)
  562.         end
  563.     end
  564.     local ok, res = pcall(error, "", 3 + level)
  565.     os.queueEvent("Lux_panic", res..message)
  566.     while true do
  567.         coroutine.yield()
  568.     end
  569. end
  570.  
  571.  
  572.  
  573.  
  574.  
  575. ---Calls the function in kernel protected mode: if an error is raised by this function, causes kernel panic.
  576. ---Returns what the function had returned on success.
  577. ---@generic R
  578. ---@generic P
  579. ---@param func_name string The name of the function to call. Used when panicking for better traceback.
  580. ---@param func fun(... : P) : R The function to call.
  581. ---@param ... any The arguments to call the function with.
  582. ---@return R func_return The return value(s) of the function.
  583. function kernel.panic_pcall(func_name, func, ...)
  584.     kernel.check_kernel_space_before_running()
  585.     local res = {pcall(func, ...)}
  586.     if not res[1] then
  587.         kernel.panic("Error while calling the function '"..tostring(func_name).."':\n"..tostring(res[2]), 1)
  588.     end
  589.     table.remove(res, 1)
  590.     return table.unpack(res)
  591. end
  592.  
  593.  
  594. ---Runs the given function in user space (re-entering and leaving kernel space around each yield).
  595. ---Note that if this function raises an error, it will still cause a kernel panic.
  596. ---@generic P
  597. ---@generic R
  598. ---@param func fun(... : P) : R The function to run in user space.
  599. ---@param ... P The arguments to pass to the function.
  600. ---@return R func_return The return value(s) of the function
  601. function kernel.run_function_in_user_space(func, ...)
  602.     kernel.check_kernel_space_before_running()
  603.     local coro = coroutine.create(func)
  604.     local res = {coroutine.resume(coro, ...)}
  605.     local ok, err = res[1], res[2]
  606.     table.remove(res, 1)
  607.     while true do
  608.         if not ok then
  609.             kernel.panic("An error occured while running a function in user space:\n"..err)
  610.         end
  611.         if coroutine.status(coro) == "dead" then
  612.             return table.unpack(res)
  613.         end
  614.         local event = {coroutine.yield()}
  615.         res = {coroutine.resume(coro, table.unpack(event))}
  616.         ok, err = res[1], res[2]
  617.         table.remove(res, 1)
  618.     end
  619. end
  620.  
  621.  
  622.  
  623.  
  624.  
  625. local routine_coroutines = {}   ---@type {[string] : thread}
  626. local current_routine = nil     ---@type string?
  627. local routines_ready = {}       ---@type {[string] : boolean}
  628. local routines_offline = {}     ---@type {[string] : boolean}
  629. local private_event = {}        ---@type {[string] : string}
  630.  
  631. ---Register a new system routine. Only called by the main scheduler.
  632. ---@param name string The name of the coroutine.
  633. ---@param coro thread The coroutine object itself.
  634. function kernel.register_routine(name, coro)
  635.     kernel.check_kernel_space_before_running()
  636.     kernel.promote_coroutine(coro)
  637.     for iname, icoro in pairs(routine_coroutines) do
  638.         if name == iname then
  639.             kernel.panic("Routine '"..name.."' has already been registered.")
  640.         end
  641.     end
  642.     routine_coroutines[name] = coro
  643.     routines_ready[name] = false
  644.     routines_offline[name] = false
  645. end
  646.  
  647. ---Sets the name of the currently running routine.
  648. ---@param name string? The name of the current routine.
  649. function kernel.set_current_routine(name)
  650.     kernel.check_kernel_space_before_running()
  651.     current_routine = name
  652. end
  653.  
  654. ---Returns the name of the currently running system routine.
  655. ---@return string name The name of the current system routine.
  656. function kernel.current_routine()
  657.     if current_routine == nil then
  658.         kernel.panic("Calling function 'kernel.current_routine' outside of a routine.")
  659.     else
  660.         return current_routine
  661.     end
  662.     return "nil"
  663. end
  664.  
  665. ---Returns a dictionnary of all the existing routines.
  666. ---@return { [string]: thread } routines The existing routine, indexed by names.
  667. function kernel.routines()
  668.     kernel.check_kernel_space_before_running()
  669.     return routine_coroutines
  670. end
  671.  
  672. ---Registers an event as private to the currently running routine.
  673. ---@param event_name string The name of the event to make private.
  674. function kernel.make_event_private(event_name)
  675.     kernel.check_kernel_space_before_running()
  676.     if private_event[event_name] ~= nil then
  677.         kernel.panic("Event '"..event_name.."' is already private to routine '"..private_event[event_name].."'.")
  678.     end
  679.     private_event[event_name] = kernel.current_routine()
  680. end
  681.  
  682. ---Returns a table of the routines to run for a given event.
  683. ---@param event_name string The name of the event to get the routines for.
  684. ---@return {[string] : thread} routines The routines to run for the event.
  685. function kernel.get_routines_for_event(event_name)
  686.     kernel.check_kernel_space_before_running()
  687.     if private_event[event_name] ~= nil then
  688.         return {[private_event[event_name]].."]]"..[[ = routine_coroutines[private_event[event_name]].."]]"..[[}
  689.     else
  690.         return kernel.routines()
  691.     end
  692. end
  693.  
  694. ---Marks the currently running routine as ready to start LuxOS.
  695. ---@param halt boolean? If true (default), waits until all the other routines are ready to start.
  696. function kernel.mark_routine_ready(halt)
  697.     kernel.check_kernel_space_before_running()
  698.     if halt == nil then
  699.         halt = true
  700.     end
  701.     local name = kernel.current_routine()
  702.     if routines_ready[name] == nil then
  703.         kernel.panic("Unknown routine '"..name.."' tried to mark itself ready.", 1)
  704.     end
  705.     routines_ready[name] = true
  706.     while not kernel.is_system_ready() do
  707.         coroutine.yield()
  708.     end
  709. end
  710.  
  711. ---Returns true when the system is ready to run (i.e. when all routines haved marked themselves ready).
  712. ---@return boolean ready Is LuxOS ready to run?
  713. function kernel.is_system_ready()
  714.     for name, ready in pairs(routines_ready) do
  715.         if not ready then
  716.             return false
  717.         end
  718.     end
  719.     return true
  720. end
  721.  
  722. ---Returns an array of the routines that have yet to finish startup.
  723. ---@param event_name string The name of the event to get the routines for.
  724. ---@return {[string] : thread} not_ready The routines' coroutines to run to finish startup.
  725. function kernel.starting_routines(event_name)
  726.     kernel.check_kernel_space_before_running()
  727.     if private_event[event_name] ~= nil and not routines_ready[private_event[event_name]].."]]"..[[ then
  728.         return {[private_event[event_name]].."]]"..[[ = routine_coroutines[private_event[event_name]].."]]"..[[}
  729.     end
  730.     local not_ready = {}
  731.     for name, ready in pairs(routines_ready) do
  732.         if not ready then
  733.             not_ready[name] = routine_coroutines[name]
  734.         end
  735.     end
  736.     return not_ready
  737. end
  738.  
  739. ---Marks the currently running routine as offline and ready for shutdown.
  740. ---@param halt boolean? If true (default), this function will halt forever, awaiting shutdown.
  741. function kernel.mark_routine_offline(halt)
  742.     kernel.check_kernel_space_before_running()
  743.     if halt == nil then
  744.         halt = true
  745.     end
  746.     local name = kernel.current_routine()
  747.     if routines_offline[name] == nil then
  748.         kernel.panic("Unknown routine '"..name.."' tried to mark itself offline.", 1)
  749.     end
  750.     routines_offline[name] = true
  751.     if halt then
  752.         while true do
  753.             coroutine.yield()
  754.         end
  755.     end
  756. end
  757.  
  758. ---Returns true when the system is ready for shutdown (i.e. when all routines haved marked themselves offline).
  759. ---@return boolean offline Is LuxOS ready to shutdown?
  760. function kernel.is_system_offline()
  761.     for name, offline in pairs(routines_offline) do
  762.         if not offline then
  763.             return false
  764.         end
  765.     end
  766.     return true
  767. end
  768.  
  769. ---Returns an array of the routines that have yet to finish shutting down.
  770. ---@param event_name string The name of the event to get the routines for.
  771. ---@return {[string] : thread} not_ready The routines' coroutines to run to finish shutdown.
  772. function kernel.disconnecting_routines(event_name)
  773.     kernel.check_kernel_space_before_running()
  774.     if private_event[event_name] ~= nil and not routines_offline[private_event[event_name]].."]]"..[[ then
  775.         return {[private_event[event_name]].."]]"..[[ = routine_coroutines[private_event[event_name]].."]]"..[[}
  776.     end
  777.     local not_offline = {}
  778.     for name, offline in pairs(routines_offline) do
  779.         if not offline then
  780.             not_offline[name] = routine_coroutines[name]
  781.         end
  782.     end
  783.     return not_offline
  784. end
  785.  
  786.  
  787.  
  788.  
  789.  
  790. local SHUTTING_DOWN = false
  791.  
  792. ---Returns true if the kernel has initialized shutdown.
  793. ---@return boolean shutting_down
  794. function kernel.is_system_shutting_down()
  795.     return SHUTTING_DOWN
  796. end
  797.  
  798. ---Initializes kernel shutdown. Should only be called once.
  799. function kernel.initialize_shutdown()
  800.     kernel.check_kernel_space_before_running()
  801.     if SHUTTING_DOWN then
  802.         kernel.panic("Shutdown has already been initialized.", 1)
  803.     end
  804.     SHUTTING_DOWN = true
  805. end
  806.  
  807.  
  808.  
  809.  
  810.  
  811. --- A class to represent a filesystem node for kernel runtime checks. See function 'kernel.chec'
  812. ---@class FS_Node
  813. ---@field name string
  814. ---@field type FS_Node_type
  815. ---@field mode FileModes | DirectoryModes
  816. ---@field children FS_Node[]
  817. local FS_Node = {}
  818. FS_Node.__index = FS_Node
  819. FS_Node.__name = "FS_Node"
  820.  
  821. local i = 1
  822. ---Internal function used to create enums.
  823. ---@return integer
  824. local function enum()
  825.     local res = i
  826.     i = i * 2
  827.     return res
  828. end
  829.  
  830. ---@enum FileModes
  831. kernel.FILE = {
  832.     EXISTS = enum(),                    -- Checks that the file exists and is not a directory. Panics otherwise.
  833.     ENSURE_EXISTS = enum(),               -- Ensures that the file does exist, creating an empty file if not and deleting recursively directories with the same name.
  834.     DOES_NOT_EXIST = enum(),            -- Checks that the file does not exist nor a that a directory with the same name exists. Panics otherwise.
  835.     ENSURE_DOES_NOT_EXISTS = enum(),    -- Ensures that the file does not exist, deleting it or deleting recursively directories with the same name.
  836. }
  837. ---@enum DirectoryModes
  838. kernel.DIRECTORY = {
  839.     EXISTS = enum(),                    -- Checks that the directory exists and is not a file. Panics otherwise.
  840.     ENSURE_EXISTS = enum(),             -- Ensures that the directory does exist, creating an empty directory if not and deleting files with the same name.
  841.     DOES_NOT_EXIST = enum(),            -- Checks that the directory does not exist nor a that a file with the same name exists. Panics otherwise.
  842.     ENSURE_DOES_NOT_EXISTS = enum(),    -- Ensures that the directory does not exist, deleting it recursively or deleting files with the same name.
  843. }
  844.  
  845. ---@alias FS_Node_type `kernel.FILE` | `kernel.DIRECTORY`
  846.  
  847. ---Creates a new node for a file system structure tree.
  848. ---@param obj {name : string, type : FS_Node_type, mode : FileModes | DirectoryModes ?, children : FS_Node[]?}
  849. function FS_Node:new(obj)
  850.     kernel.check_kernel_space_before_running()
  851.     local fs_struct = obj or {}
  852.     setmetatable(fs_struct, self)
  853.     if type(fs_struct.name) ~= "string" then
  854.         kernel.panic("FS_Node's 'name' field should be a string, not '"..type(fs_struct.name).."'", 1)
  855.     end
  856.     if fs_struct.type ~= kernel.FILE and fs_struct.type ~= kernel.DIRECTORY then
  857.         kernel.panic("FS_Node's 'type' field should be either 'FS_Node.FILE' or 'FS_Node.DIRECTORY', not '"..tostring(fs_struct.type).."'", 1)
  858.     end
  859.     if not fs_struct.mode then
  860.         if fs_struct.type == kernel.FILE then
  861.             fs_struct.mode = kernel.FILE.EXISTS
  862.         else
  863.             fs_struct.mode = kernel.DIRECTORY.EXISTS
  864.         end
  865.     else
  866.         local ok = false
  867.         for mode_name, mode_value in pairs(fs_struct.type) do
  868.             if fs_struct.mode == mode_value then
  869.                 ok = true
  870.                 break
  871.             end
  872.         end
  873.         if not ok then
  874.             kernel.panic("Unrecognized "..((fs_struct.type == kernel.FILE) and "file" or "directory").." mode: "..tostring(fs_struct.mode), 1)
  875.         end
  876.     end
  877.     if type(fs_struct.children) ~= "table" and fs_struct.children ~= nil then
  878.         kernel.panic("FS_Node's 'children' field should be a table, not '"..type(fs_struct.children).."'", 1)
  879.     end
  880.     local children = fs_struct.children or {}
  881.     fs_struct.children = {}
  882.     for index, child in ipairs(children) do
  883.         if type(child) ~= type(fs_struct) then
  884.             kernel.panic("FS_Node children should also be '"..type(child).."', not '"..type(fs_struct).."'", 1)
  885.         end
  886.         if not rawequal(getmetatable(child), self) then
  887.             kernel.panic("FS_Node children should also be '"..type(child).."', not '"..type(fs_struct).."' (different metatables)", 1)
  888.         end
  889.         table.insert(fs_struct.children, child)
  890.     end
  891.     if #fs_struct.children > 0 and fs_struct.type == kernel.FILE then
  892.         kernel.panic("FS_Node of type FILE has "..tostring(#fs_struct.children).." children.")
  893.     end
  894.     if #fs_struct.children > 0 and (fs_struct.mode == kernel.DIRECTORY.DOES_NOT_EXIST or fs_struct.mode == kernel.DIRECTORY.ENSURE_DOES_NOT_EXISTS) then
  895.         kernel.panic("FS_Node of type DIRECTORY has "..tostring(#fs_struct.children).." children with mode '"..((fs_struct.mode == kernel.DIRECTORY.DOES_NOT_EXIST) and "DOES_NOT_EXIST" or "ENSURE_DOES_NOT_EXISTS").."'.")
  896.     end
  897.     return fs_struct
  898. end
  899.  
  900. ---Creates a new filesystem structure tree, starting from the root.
  901. ---@param root_items {children : FS_Node[]}
  902. function kernel.filesystem_structure(root_items)
  903.     kernel.check_kernel_space_before_running()
  904.     return FS_Node:new{
  905.         name = "",
  906.         type = kernel.DIRECTORY,
  907.         children = root_items
  908.     }
  909. end
  910.  
  911. ---Creates a new node for a file system structure tree.
  912. ---@param node {name : string, type : FS_Node_type, mode : FileModes | DirectoryModes ?, children : FS_Node[]?} A table hodling the required information about the node.
  913. ---@return FS_Node node The new filesystem node.
  914. function kernel.filesystem_node(node)
  915.     kernel.check_kernel_space_before_running()
  916.     return FS_Node:new(node)
  917. end
  918.  
  919.  
  920.  
  921.  
  922.  
  923. --]].."[["..[[Ensures that the given filesystem structure exists. Panics if not. Here is an example of such a structure:
  924.  
  925.     local filesystem = kernel.filesystem_structure{       -- Implicitely creates the root directory.
  926.  
  927.         kernel.filesystem_node{
  928.             name = "startup.lua",       -- A startup file at the root.
  929.             type = kernel.FILE
  930.         },
  931.  
  932.         kernel.filesystem_node{
  933.             name = "tmp",               -- A possible tmp directory...
  934.             type = kernel.DIRECTORY,
  935.             mode = kernel.DIRECTORY.IS_NOT_FILE  -- ...as long as it does not exist as a file.
  936.         },
  937.  
  938.         kernel.filesystem_node{
  939.             name = "LuxOS",             -- The LuxOS directory at the root.
  940.             type = kernel.DIRECTORY,
  941.             children = {
  942.  
  943.                 kernel.filesystem_node{
  944.                     name = "kernel.lua",
  945.                     type = kernel.FILE  -- A kernel file inside LuxOS.
  946.                 }
  947.  
  948.             }
  949.         }
  950.  
  951.     }
  952.    
  953.     kernel.validate_filesystem_structure(filesystem)      -- Panics if the described filesystem structure does not exist.
  954.  
  955. ]].."]]"..[[
  956. ---@param structure FS_Node The structure. A FS_Node object.
  957. function kernel.validate_filesystem_structure(structure)
  958.     kernel.check_kernel_space_before_running()
  959.  
  960.     ---Inner recursive structure checker
  961.     ---@param sub_structure FS_Node Directory to check.
  962.     ---@param root string The path of the parent directory.
  963.     ---@param level integer The depth in the filesystem + 1.
  964.     local function recursive_structure_validation(sub_structure, root, level)
  965.         for index, child_node in ipairs(sub_structure.children) do
  966.             local path = root..child_node.name
  967.             if child_node.type == kernel.FILE then
  968.                 if child_node.mode == kernel.FILE.EXISTS then
  969.                     if not kernel.panic_pcall("fs.exists", fs.exists, path) then
  970.                         kernel.panic("Required file node '"..path.."' does not exist.", level)
  971.                     end
  972.                     if kernel.panic_pcall("fs.isDir", fs.isDir, path) then
  973.                         kernel.panic("Required file node '"..path.."' exists but is a directory.", level)
  974.                     end
  975.                 elseif child_node.mode == kernel.FILE.ENSURE_EXISTS then
  976.                     if kernel.panic_pcall("fs.exists", fs.exists, path) and kernel.panic_pcall("fs.isDir", fs.isDir, path) then
  977.                         kernel.panic_pcall("fs.delete", fs.delete, path)
  978.                     end
  979.                     if not kernel.panic_pcall("fs.exists", fs.exists, path) then
  980.                         local h = kernel.panic_pcall("fs.open", fs.open, path, "w")
  981.                         kernel.panic_pcall("h.close", h.close)
  982.                     end
  983.                 elseif child_node.mode == kernel.FILE.DOES_NOT_EXIST then
  984.                     if kernel.panic_pcall("fs.exists", fs.exists, path) then
  985.                         if kernel.panic_pcall("fs.isDir", fs.isDir, path) then
  986.                             kernel.panic("Forbidden file node '"..path.."' exists as a directory.", level)
  987.                         end
  988.                         kernel.panic("Forbidden file node '"..path.."' exists.", level)
  989.                     end
  990.                 elseif child_node.mode == kernel.FILE.ENSURE_DOES_NOT_EXISTS then
  991.                     if kernel.panic_pcall("fs.exists", fs.exists, path) then
  992.                         kernel.panic_pcall("fs.delete", fs.delete, path)
  993.                     end
  994.                 end
  995.             else
  996.                 if child_node.mode == kernel.DIRECTORY.EXISTS then
  997.                     if not kernel.panic_pcall("fs.exists", fs.exists, path) then
  998.                         kernel.panic("Required directory node '"..path.."' does not exist.", level)
  999.                     end
  1000.                     if not kernel.panic_pcall("fs.isDir", fs.isDir, path) then
  1001.                         kernel.panic("Required directory node '"..path.."' exists but is a file.", level)
  1002.                     end
  1003.                     recursive_structure_validation(child_node, path.."/", level + 1)
  1004.                 elseif child_node.mode == kernel.DIRECTORY.ENSURE_EXISTS then
  1005.                     if kernel.panic_pcall("fs.exists", fs.exists, path) and not kernel.panic_pcall("fs.isDir", fs.isDir, path) then
  1006.                         kernel.panic_pcall("fs.delete", fs.delete, path)
  1007.                     end
  1008.                     if not kernel.panic_pcall("fs.exists", fs.exists, path) then
  1009.                         kernel.panic_pcall("fs.makeDir", fs.makeDir, path)
  1010.                     end
  1011.                     recursive_structure_validation(child_node, path.."/", level + 1)
  1012.                 elseif child_node.mode == kernel.DIRECTORY.DOES_NOT_EXIST then
  1013.                     if kernel.panic_pcall("fs.exists", fs.exists, path) then
  1014.                         if not kernel.panic_pcall("fs.isDir", fs.isDir, path) then
  1015.                             kernel.panic("Forbidden directory node '"..path.."' exists as a file.", level)
  1016.                         end
  1017.                         kernel.panic("Forbidden directory node '"..path.."' exists.", level)
  1018.                     end
  1019.                 elseif child_node.mode == kernel.DIRECTORY.ENSURE_DOES_NOT_EXISTS then
  1020.                     if kernel.panic_pcall("fs.exists", fs.exists, path) then
  1021.                         kernel.panic_pcall("fs.delete", fs.delete, path)
  1022.                     end
  1023.                 end
  1024.             end
  1025.         end
  1026.     end
  1027.  
  1028.     if getmetatable(structure) ~= FS_Node then
  1029.         kernel.panic("'kernel.validate_fs_structure' expected a FS_Node object, got a '"..type(structure).."' (wrong metatable).")
  1030.     end
  1031.     if structure.name ~= "" or structure.type ~= kernel.DIRECTORY then
  1032.         kernel.panic("Invalid root node in structure given to 'kernel.validate_fs_structure', got a "..((structure.type == kernel.FILE) and "file" or "directory").." node named '"..tostring(structure.name).."'")
  1033.     end
  1034.     recursive_structure_validation(structure, structure.name, 1)
  1035. end]],
  1036. [[--]].."[["..[[
  1037. This is the standart library of LuxOS
  1038. ]].."]]"..[[
  1039.  
  1040. _G.lux = {} --- The lux API. It contains simple functions to interact with LuxOS.
  1041. libraries.lux = lux
  1042.  
  1043.  
  1044.  
  1045.  
  1046.  
  1047. local CURRENT_TICK = 0
  1048. local NEXT_TICK = 0
  1049.  
  1050. ---This function creates an immediate "tick" event and waits (instantly) for it.
  1051. ---This is used for heavy computations.
  1052. ---@param terminable? boolean If set to false, a terminate event will not be caught by this function.
  1053. function lux.tick(terminable)
  1054.     if CURRENT_TICK == NEXT_TICK then
  1055.         NEXT_TICK = NEXT_TICK + 1
  1056.         os.queueEvent("tick")
  1057.     end
  1058.     local expected_tick = NEXT_TICK
  1059.     if terminable == false then
  1060.         local event = {coroutine.yield()}
  1061.         while #event ~= 1 or event[1] ~= "tick" do
  1062.             event = {coroutine.yield()}
  1063.         end
  1064.     else
  1065.         local event = {os.pullEvent()}
  1066.         while #event ~= 1 or event[1] ~= "tick" do
  1067.             event = {os.pullEvent()}
  1068.         end
  1069.     end
  1070.     if CURRENT_TICK < expected_tick then
  1071.         CURRENT_TICK = expected_tick
  1072.     end
  1073. end
  1074.  
  1075. ---This function creates a "tick" event without waiting for it to happen.
  1076. function lux.make_tick()
  1077.     if CURRENT_TICK == NEXT_TICK then
  1078.         NEXT_TICK = NEXT_TICK + 1
  1079.         os.queueEvent("tick")
  1080.     end
  1081. end
  1082.  
  1083.  
  1084.  
  1085.  
  1086.  
  1087. ---Turns off the computer.
  1088. ---Actually throws a {"shutdown", "s"} event for the kernel to handle then blocks undefinitely.
  1089. ---Note that in kernel space, this function also marks the current routine offline.
  1090. function os.shutdown()
  1091.     os.queueEvent("shutdown", "s")
  1092.     if kernel.kernel_space() then
  1093.         local event = {}
  1094.         while event[1] ~= "shutdown" do
  1095.             event = {coroutine.yield()}
  1096.         end
  1097.         kernel.mark_routine_offline()
  1098.     else
  1099.         while true do
  1100.             coroutine.yield()
  1101.         end
  1102.     end
  1103. end
  1104.  
  1105. ---Reboots the computer.
  1106. ---Actually throws a {"shutdown", "r"} event for the kernel to handle then blocks undefinitely.
  1107. ---Note that in kernel space, this function also marks the current routine offline.
  1108. function os.reboot()
  1109.     os.queueEvent("shutdown", "r")
  1110.     if kernel.kernel_space() then
  1111.         local event = {}
  1112.         while event[1] ~= "shutdown" do
  1113.             event = {coroutine.yield()}
  1114.         end
  1115.         kernel.mark_routine_offline()
  1116.     else
  1117.         while true do
  1118.             coroutine.yield()
  1119.         end
  1120.     end
  1121. end
  1122.  
  1123. ---Creates a new user namespace.
  1124. function os.create_user_environment()
  1125.     local env = {
  1126.         ["print"] = print,
  1127.         ["error"] = error,
  1128.         ["assert"] = assert,
  1129.         ["pcall"] = pcall,
  1130.         ["tostring"] = tostring,
  1131.         ["tonumber"] = tonumber,
  1132.         ["type"] = type,
  1133.         ["pairs"] = pairs,
  1134.         ["ipairs"] = ipairs,
  1135.         ["next"] = next,
  1136.         ["unpack"] = table.unpack,
  1137.         ["loadfile"] = loadfile,
  1138.         ["dofile"] = dofile,
  1139.         ["setmetatable"] = setmetatable,
  1140.         ["getmetatable"] = getmetatable,
  1141.         ["write"] = write,
  1142.         ["read"] = read,
  1143.         ["sleep"] = sleep
  1144.     }
  1145.     for name, lib in pairs(libraries) do
  1146.         env[name] = lib
  1147.     end
  1148.     return env
  1149. end]],
  1150. [[--]].."[["..[[
  1151. This is the standart LuxNet service API. It contains all the functions to communicate with other machines running on LuxOS.
  1152. ]].."]]"..[[
  1153.  
  1154. _G.luxnet = {}      -- The LuxNet API. Allows you to communicate with other machines running LuxOS.
  1155.  
  1156.  
  1157.  
  1158.  
  1159.  
  1160. luxnet.LUXNET_PORT = 42      -- The port that LuxNet uses to communicate with other machines.
  1161. luxnet.BROADCAST_ID = -1     -- The ID that represents a broadcast message. This is used to send messages to all machines.
  1162.  
  1163.  
  1164.  
  1165.  
  1166.  
  1167. ---@class Message The class for message objects.
  1168. ---@field sender integer The ID of the sender.
  1169. ---@field receiver integer The ID of the receiver.
  1170. ---@field message table | string | number | boolean | nil The message itself.
  1171. ---@field protocol string | nil The protocol used to send the message.
  1172. ---@field identifier integer A unique identifier for the message.
  1173. ---@field jumps integer The amount of jumps the message has done to reach the receiver.
  1174. ---@field distance number The distance that the message has traveled.
  1175. ---@field time_sent number The time when the message was sent.
  1176. ---@field time_received number The time when the message was received.
  1177. local Message = {}
  1178. luxnet.Message = Message
  1179.  
  1180. Message.__index = Message
  1181. Message.__name = "Message"
  1182.  
  1183.  
  1184. ---Creates a new message object. Can only be called from kernel space.
  1185. ---@param message {["sender"]: integer, ["receiver"]: integer, ["message"]: table | string | number | boolean | nil, ["protocol"]: string | nil, ["identifier"]: integer, ["jumps"]: integer, ["distance"]: number, ["time_sent"]: number, ["time_received"]: number} The required parameters for creating a message.
  1186. ---@return Message message The new message object.
  1187. function Message:new(message)
  1188.     kernel.check_kernel_space_before_running()
  1189.     setmetatable(message, self)
  1190.     return message
  1191. end
  1192.  
  1193.  
  1194.  
  1195. ---@class Response The class for response objects.
  1196. ---@field sender integer The ID of the sender of the corresponding message.
  1197. ---@field receiver integer The ID of the receiver of the corresponding message.
  1198. ---@field identifier integer The identifier of the corresponding message.
  1199. ---@field jumps integer The amount of jumps that the corresponding message has done to reach the receiver.
  1200. ---@field distance number The distance that the corresponding message has traveled to reach the receiver.
  1201. ---@field time_sent number The time when the corresponding message was sent.
  1202. ---@field time_received number The time when the corresponding message was received.
  1203. local Response = {}
  1204. luxnet.Response = Response
  1205.  
  1206. Response.__index = Response
  1207. Response.__name = "Response"
  1208.  
  1209.  
  1210. ---Creates a new response object. Can only be called from kernel space.
  1211. ---@param response {["sender"]: integer, ["receiver"]: integer, ["identifier"]: integer, ["jumps"]: integer, ["distance"]: number, ["time_sent"]: number, ["time_received"]: number} The required parameters for creating a response.
  1212. ---@return Response response The new response object.
  1213. function Response:new(response)
  1214.     kernel.check_kernel_space_before_running()
  1215.     setmetatable(response, self)
  1216.     return response
  1217. end
  1218.  
  1219.  
  1220.  
  1221.  
  1222.  
  1223. luxnet.send = syscall.new(
  1224.     "luxnet.send",
  1225.     ---Sends a message to another machine.
  1226.     ---@param receiver integer The ID of the receiver.
  1227.     ---@param message table | string | number | boolean | nil The message to send.
  1228.     ---@param protocol string | nil The protocol to use to send the message.
  1229.     ---@return Response | false response The response from the receiver, if any.
  1230.     function (receiver, message, protocol)
  1231.         local ok, err_or_response = syscall.trampoline(receiver, message, protocol)
  1232.         if ok then
  1233.             return err_or_response
  1234.         else
  1235.             error(err_or_response, 2)
  1236.         end
  1237.     end
  1238. )
  1239.  
  1240.  
  1241.  
  1242.  
  1243.  
  1244. luxnet.broadcast = syscall.new(
  1245.     "luxnet.broadcast",
  1246.     ---Sends a message to all machines.
  1247.     ---@param message table | string | number | boolean | nil The message to send.
  1248.     ---@param protocol string | nil The protocol to use to send the message.
  1249.     function (message, protocol)
  1250.         local ok, err = syscall.trampoline(message, protocol)
  1251.         if ok then
  1252.             return
  1253.         else
  1254.             error(err, 2)
  1255.         end
  1256.     end
  1257. )
  1258.  
  1259.  
  1260.  
  1261.  
  1262.  
  1263. luxnet.set_response_timeout = syscall.new(
  1264.     "luxnet.set_response_timeout",
  1265.     ---Sets the time to wait for a response before giving up.
  1266.     ---@param timeout number The time to wait for a response before giving up.
  1267.     function (timeout)
  1268.         local ok, err = syscall.trampoline(timeout)
  1269.         if ok then
  1270.             return
  1271.         else
  1272.             error(err, 2)
  1273.         end
  1274.     end
  1275. )
  1276.  
  1277.  
  1278.  
  1279.  
  1280.  
  1281. --- Receives a message from another machine.
  1282. ---@param protocol string | nil An optional protocal to filter messages by.
  1283. ---@param timeout number | nil The time to wait for a message before giving up. Defaults to no timeout.
  1284. ---@return Message | nil message The message received, or nil if the timeout was reached.
  1285. function luxnet.receive(protocol, timeout)
  1286.     if protocol ~= nil and type(protocol) ~= "string" then
  1287.         error("bad argument #1 (expected string, got " .. type(protocol) .. ")", 2)
  1288.     end
  1289.     if timeout ~= nil and type(timeout) ~= "number" then
  1290.         error("bad argument #2 (expected number, got " .. type(timeout) .. ")", 2)
  1291.     end
  1292.     if timeout < 0 then
  1293.         error("bad argument #2 (expected number >= 0, got " .. timeout .. ")", 2)
  1294.     end
  1295.     local timer
  1296.     if timeout ~= nil then
  1297.         timer = os.startTimer(timeout)
  1298.     end
  1299.     while true do
  1300.         local event = {coroutine.yield()}
  1301.         if event[1] == "luxnet_message" or event[1] == "luxnet_broadcast" then
  1302.             local message = event[2]
  1303.             if protocol == nil or message.protocol == protocol then
  1304.                 return message
  1305.             end
  1306.         elseif event[1] == "terminate" then
  1307.             error("terminated", 2)
  1308.         elseif event[1] == "timer" and event[2] == timer then
  1309.             return nil
  1310.         end
  1311.     end
  1312. end
  1313.  
  1314.  
  1315.  
  1316.  
  1317.  
  1318. --- Receives a message from a specific machine.
  1319. ---@param sender integer The ID of the sender to receive a message from.
  1320. ---@param protocol string | nil An optional protocal to filter messages by.
  1321. ---@param timeout number | nil The time to wait for a message before giving up. Defaults to no timeout.
  1322. ---@return Message | nil message The message received, or nil if the timeout was reached.
  1323. function luxnet.receive_from(sender, protocol, timeout)
  1324.     if type(sender) ~= "number" then
  1325.         error("bad argument #1 (expected number, got " .. type(sender) .. ")", 2)
  1326.     end
  1327.     if protocol ~= nil and type(protocol) ~= "string" then
  1328.         error("bad argument #2 (expected string, got " .. type(protocol) .. ")", 2)
  1329.     end
  1330.     if timeout ~= nil and type(timeout) ~= "number" then
  1331.         error("bad argument #3 (expected number, got " .. type(timeout) .. ")", 2)
  1332.     end
  1333.     if sender < 0 or sender > 65535 then
  1334.         error("bad argument #1 (expected number in range 0-65535, got " .. sender .. ")", 2)
  1335.     end
  1336.     if timeout < 0 then
  1337.         error("bad argument #3 (expected number >= 0, got " .. timeout .. ")", 2)
  1338.     end
  1339.     local timer
  1340.     if timeout ~= nil then
  1341.         timer = os.startTimer(timeout)
  1342.     end
  1343.     while true do
  1344.         local event = {coroutine.yield()}
  1345.         if event[1] == "luxnet_message" or event[1] == "luxnet_broadcast" then
  1346.             local message = event[2]
  1347.             if message.sender == sender and (protocol == nil or message.protocol == protocol) then
  1348.                 return message
  1349.             end
  1350.         elseif event[1] == "terminate" then
  1351.             error("terminated", 2)
  1352.         elseif event[1] == "timer" and event[2] == timer then
  1353.             return nil
  1354.         end
  1355.     end
  1356. end]],
  1357. [[--]].."[["..[[
  1358. This is the luxnet kernel routine. It handles all modems and everything that goes through them.
  1359. ]].."]]"..[[
  1360.  
  1361.  
  1362.  
  1363.  
  1364.  
  1365. local LUXNET_FS_STRUCTURE = kernel.filesystem_structure{
  1366.  
  1367.     kernel.filesystem_node{
  1368.         name = "LuxOS",
  1369.         type = kernel.DIRECTORY,
  1370.         children = {
  1371.  
  1372.             kernel.filesystem_node{
  1373.                 name = "luxnet",
  1374.                 type = kernel.DIRECTORY,
  1375.                 children = {
  1376.  
  1377.                     kernel.filesystem_node{
  1378.                         name = "routine.lua",
  1379.                         type = kernel.FILE
  1380.                     },
  1381.  
  1382.                     kernel.filesystem_node{
  1383.                         name = "lib.lua",
  1384.                         type = kernel.FILE
  1385.                     }
  1386.  
  1387.                 }
  1388.             }
  1389.         }
  1390.     }
  1391.  
  1392. }
  1393. local LUXNET_PORT = luxnet.LUXNET_PORT
  1394. local HOST_ID_BUFFER_SIZE = 16
  1395. local RESPONSE_TIMEOUT = 5
  1396.  
  1397. local modems = {}      ---@type {[string] : table} The table of all modems connected to the computer. The key is the side of the modem and the value is the modem object.
  1398. local COMPUTER_ID = os.getComputerID()
  1399. local seen_messages = {[COMPUTER_ID] = {}}        ---@type {[number] : number[]} The table of used identifiers per host for messages.
  1400. local seen_responses = {[COMPUTER_ID] = {}}        ---@type {[number] : number[]} The table of used identifiers per host for responses.
  1401. local n_messages = {[COMPUTER_ID] = 0}           ---@type {[number] : number} The table of the amount of messages per host.
  1402. local awaiting_responses = {}                   ---@type {[number] : Response | true} The table for receiving responses.
  1403.  
  1404. local simple_message_fields = {
  1405.     ["sender"] = "number",
  1406.     ["receiver"] = "number",
  1407.     ["identifier"] = "number",
  1408.     ["jumps"] = "number",
  1409.     ["distance"] = "number",
  1410.     ["time_sent"] = "number",
  1411.     ["time_received"] = "number"
  1412. }
  1413.  
  1414. ---Checks if a message is a luxnet response.
  1415. ---@param message table The message to check.
  1416. ---@return boolean ok Whether the message is a response or not.
  1417. local function is_valid_response(message)
  1418.     if type(message) ~= "table" then
  1419.         return false
  1420.     end
  1421.     for field, field_type in pairs(simple_message_fields) do
  1422.         if type(message[field]) ~= field_type then
  1423.             return false
  1424.         end
  1425.     end
  1426.     return true
  1427. end
  1428.  
  1429. ---Checks if a message can make a valid Message object.
  1430. ---@param message any The message to check.
  1431. ---@return boolean ok Whether the message is valid or not.
  1432. local function is_valid_message(message)
  1433.     if type(message) ~= "table" then
  1434.         return false
  1435.     end
  1436.     if type(message["message"]) ~= "table" and type(message["message"]) ~= "string" and message["message"] ~= "number" and message["message"] ~= "boolean" and message["message"] ~= "nil" then
  1437.         return false
  1438.     end
  1439.     if type(message["protocol"]) ~= "string" and type(message["protocol"]) ~= "nil" then
  1440.         return false
  1441.     end
  1442.     for field, field_type in pairs(simple_message_fields) do
  1443.         if type(message[field]) ~= field_type then
  1444.             return false
  1445.         end
  1446.     end
  1447.     return true
  1448. end
  1449.  
  1450. ---Generates an identifier for a message.
  1451. ---@return integer identifier The generated identifier.
  1452. local function identifier_generator()
  1453.     return math.random(0, 2147483646)       -- 2^31 - 2 for some reason
  1454. end
  1455.  
  1456.  
  1457. ---Inserts a seen message into the seen messages table.
  1458. ---@param sender integer The ID of the sender.
  1459. ---@param identifier integer The identifier of the message.
  1460. local function insert_seen_message(sender, identifier)
  1461.     local index = (#seen_messages[sender] + 1) % HOST_ID_BUFFER_SIZE
  1462.     if index == 0 then
  1463.         index = HOST_ID_BUFFER_SIZE
  1464.     end
  1465.     seen_messages[sender][index] = identifier
  1466.     n_messages[sender] = n_messages[sender] + 1
  1467. end
  1468.  
  1469. ---Inserts a seen response into the seen responses table.
  1470. ---@param sender integer The ID of the sender.
  1471. ---@param identifier integer The identifier of the response.
  1472. local function insert_seen_response(sender, identifier)
  1473.     local index = (#seen_responses[sender] + 1) % HOST_ID_BUFFER_SIZE
  1474.     if index == 0 then
  1475.         index = HOST_ID_BUFFER_SIZE
  1476.     end
  1477.     seen_responses[sender][index] = identifier
  1478. end
  1479.  
  1480. local function answer_calls_to_send(...)
  1481.     local args = {...}
  1482.     if #args < 2 or #args > 3 then
  1483.         return false, "syscall got "..tostring(#args).." parameters, expected 2 to 3"
  1484.     else
  1485.         local receiver, message, protocol = args[1], args[2], args[3]
  1486.         if type(receiver) ~= "number" or (type(message) ~= "table" and type(message) ~= "string" and type(message) ~= "number" and type(message) ~= "boolean" and type(message) ~= "nil") or (type(protocol) ~= "string" and type(protocol) ~= "nil") then
  1487.             return false, "expected number, table | string | number | boolean | nil, string | nil, got '"..type(receiver).."', '"..type(message).."', '"..type(protocol).."'"
  1488.         end
  1489.         if receiver < 0 or receiver > 65535 then
  1490.             return false, "receiver ID must be between 0 and 65535"
  1491.         end
  1492.         local identifier = identifier_generator()
  1493.         local message = luxnet.Message:new{
  1494.             sender = COMPUTER_ID,
  1495.             receiver = receiver,
  1496.             message = message,
  1497.             protocol = protocol,
  1498.             identifier = identifier,
  1499.             jumps = 0,
  1500.             distance = 0,
  1501.             time_sent = os.time(),
  1502.             time_received = 0
  1503.         }
  1504.         for side, modem in pairs(modems) do
  1505.             modem.transmit(LUXNET_PORT, LUXNET_PORT, message)
  1506.         end
  1507.         insert_seen_message(COMPUTER_ID, identifier)
  1508.         awaiting_responses[identifier] = true
  1509.         local timer = os.startTimer(RESPONSE_TIMEOUT)
  1510.         while true do
  1511.             local event = {coroutine.yield()}
  1512.             if awaiting_responses[identifier] ~= true then
  1513.                 local response = awaiting_responses[identifier]
  1514.                 awaiting_responses[identifier] = nil
  1515.                 return true, response
  1516.             end
  1517.             if event[1] == "terminate" then
  1518.                 awaiting_responses[identifier] = nil
  1519.                 return false, "terminated"
  1520.             elseif event[1] == "timer" and event[2] == timer then
  1521.                 awaiting_responses[identifier] = nil
  1522.                 return true, false
  1523.             end
  1524.         end
  1525.     end
  1526. end
  1527.  
  1528. local function answer_calls_to_broadcast(...)
  1529.     local args = {...}
  1530.     if #args <1 or #args > 2 then
  1531.         return false, "syscall got "..tostring(#args).." parameters, expected 1 to 2"
  1532.     else
  1533.         local message, protocol = args[1], args[2]
  1534.         if (type(message) ~= "table" and type(message) ~= "string" and type(message) ~= "number" and type(message) ~= "boolean" and type(message) ~= "nil") or (type(protocol) ~= "string" and type(protocol) ~= "nil") then
  1535.             return false, "expected table | string | number | boolean | nil, string | nil, got '"..type(message).."', '"..type(protocol).."'"
  1536.         end
  1537.         local identifier = identifier_generator()
  1538.         local message = luxnet.Message:new{
  1539.             sender = COMPUTER_ID,
  1540.             receiver = luxnet.BROADCAST_ID,
  1541.             message = message,
  1542.             protocol = protocol,
  1543.             identifier = identifier,
  1544.             jumps = 0,
  1545.             distance = 0,
  1546.             time_sent = os.time(),
  1547.             time_received = 0
  1548.         }
  1549.         for side, modem in pairs(modems) do
  1550.             modem.transmit(LUXNET_PORT, LUXNET_PORT, message)
  1551.         end
  1552.         insert_seen_message(COMPUTER_ID, identifier)
  1553.         return true
  1554.     end
  1555. end
  1556.  
  1557. local function answer_calls_to_set_response_timeout(...)
  1558.     local args = {...}
  1559.     if #args ~= 1 then
  1560.         return false, "syscall got "..tostring(#args).." parameters, expected 1"
  1561.     else
  1562.         local timeout = args[1]
  1563.         if type(timeout) ~= "number" then
  1564.             return false, "expected number, got '"..type(timeout).."'"
  1565.         end
  1566.         if timeout < 0 then
  1567.             return false, "timeout must be a positive number"
  1568.         end
  1569.         RESPONSE_TIMEOUT = timeout
  1570.         return true
  1571.     end
  1572. end
  1573.  
  1574.  
  1575.  
  1576.  
  1577.  
  1578. local function main()
  1579.     kernel.validate_filesystem_structure(LUXNET_FS_STRUCTURE)
  1580.  
  1581.     kernel.make_event_private("modem_message")
  1582.     kernel.make_event_private("rednet_message")
  1583.  
  1584.     syscall.affect_routine(luxnet.send, answer_calls_to_send)
  1585.     syscall.affect_routine(luxnet.broadcast, answer_calls_to_broadcast)
  1586.     syscall.affect_routine(luxnet.set_response_timeout, answer_calls_to_set_response_timeout)
  1587.  
  1588.     -- Initialize modems
  1589.  
  1590.     local sides = peripheral.getNames()
  1591.     for _, side in ipairs(sides) do
  1592.         if peripheral.getType(side) == "modem" then
  1593.             modems[side] = peripheral.wrap(side)
  1594.             modems[side].open(LUXNET_PORT)
  1595.         end
  1596.     end
  1597.    
  1598.     kernel.mark_routine_ready()
  1599.  
  1600.     -- Handle all modem related events
  1601.  
  1602.     while true do
  1603.         local event = {coroutine.yield()}
  1604.         if event[1] == "modem_message" then
  1605.             local side, sender, receiver, message, distance = event[2], event[3], event[4], event[5], event[6]
  1606.             if type(message) == "table" then
  1607.                 if is_valid_message(message) then
  1608.                     -- It is a luxnet message
  1609.                     message = luxnet.Message:new(message)
  1610.                     if distance == nil then
  1611.                         distance = math.huge
  1612.                     end
  1613.                     message.distance = message.distance + distance
  1614.                     message.jumps = message.jumps + 1
  1615.                     message.time_received = os.time()
  1616.  
  1617.                     -- handle its identifier
  1618.                     if seen_messages[message.sender] == nil then
  1619.                         seen_messages[message.sender] = {}
  1620.                         n_messages[message.sender] = 0
  1621.                     end
  1622.                     local seen = false
  1623.                     for index, identifier in ipairs(seen_messages[message.sender]) do
  1624.                         if identifier == message.identifier then
  1625.                             seen = true
  1626.                             break
  1627.                         end
  1628.                     end
  1629.                     if not seen then
  1630.                         insert_seen_message(message.sender, message.identifier)
  1631.  
  1632.                         -- What should we do with it?
  1633.                         if message.receiver == COMPUTER_ID then
  1634.                             os.queueEvent("luxnet_message", message)
  1635.                             local response = {
  1636.                                 sender = message.sender,
  1637.                                 receiver = message.receiver,
  1638.                                 identifier = message.identifier,
  1639.                                 jumps = message.jumps,
  1640.                                 distance = message.distance,
  1641.                                 time_sent = message.time_sent,
  1642.                                 time_received = message.time_received
  1643.                             }
  1644.                             for side, modem in pairs(modems) do
  1645.                                 modem.transmit(LUXNET_PORT, LUXNET_PORT, response)
  1646.                             end
  1647.                         else
  1648.                             if message.receiver == luxnet.BROADCAST_ID then
  1649.                                 os.queueEvent("luxnet_broadcast", message)
  1650.                             end
  1651.                             for side, modem in pairs(modems) do
  1652.                                 modem.transmit(LUXNET_PORT, LUXNET_PORT, message)
  1653.                             end
  1654.                         end
  1655.                     end
  1656.                 elseif is_valid_response(message) then
  1657.                     -- It is a luxnet response
  1658.                     if seen_responses[message.sender] == nil then
  1659.                         seen_responses[message.sender] = {}
  1660.                     end
  1661.                     local seen = false
  1662.                     for index, identifier in ipairs(seen_responses[message.sender]) do
  1663.                         if identifier == message.identifier then
  1664.                             seen = true
  1665.                             break
  1666.                         end
  1667.                     end
  1668.                     if not seen then
  1669.                         insert_seen_response(message.sender, message.identifier)
  1670.                        
  1671.                         -- What should we do with it?
  1672.                         if message.sender == COMPUTER_ID and awaiting_responses[message.identifier] ~= nil then
  1673.                             awaiting_responses[message.identifier] = message
  1674.                             lux.make_tick()
  1675.                         else
  1676.                             for side, modem in pairs(modems) do
  1677.                                 modem.transmit(LUXNET_PORT, LUXNET_PORT, message)
  1678.                             end
  1679.                         end
  1680.                     end
  1681.                 end
  1682.             end
  1683.         elseif event[1] == "peripheral" then
  1684.             local side = event[2]
  1685.             if peripheral.getType(side) == "modem" then
  1686.                 modems[side] = peripheral.wrap(side)
  1687.                 modems[side].open(LUXNET_PORT)
  1688.             end
  1689.         elseif event[1] == "peripheral_detach" then
  1690.             local side = event[2]
  1691.             if modems[side] ~= nil then
  1692.                 table.remove(modems, side)
  1693.             end
  1694.         elseif kernel.is_system_shutting_down() then
  1695.             break
  1696.         end
  1697.     end
  1698.  
  1699.     -- Disable modems
  1700.  
  1701.     for side, modem in pairs(modems) do
  1702.         modem.close(LUXNET_PORT)
  1703.     end
  1704.  
  1705.     kernel.mark_routine_offline()
  1706.    
  1707. end
  1708.  
  1709. return main]],
  1710. [[--]].."[["..[[
  1711. Routine for the lightUI Lux package. Handles the User Interface.
  1712. ]].."]]"..[[
  1713.  
  1714.  
  1715.  
  1716.  
  1717.  
  1718. local LUXUI_FS_STRUCTURE = kernel.filesystem_structure{
  1719.  
  1720.     kernel.filesystem_node{
  1721.         name = "LuxOS",
  1722.         type = kernel.DIRECTORY,
  1723.         children = {
  1724.  
  1725.             kernel.filesystem_node{
  1726.                 name = "luxUI",
  1727.                 type = kernel.DIRECTORY,
  1728.                 children = {
  1729.  
  1730.                     kernel.filesystem_node{
  1731.                         name = "routine.lua",
  1732.                         type = kernel.FILE
  1733.                     },
  1734.  
  1735.                     -- kernel.filesystem_node{
  1736.                     --     name = "lib.lua",
  1737.                     --     type = kernel.FILE
  1738.                     -- }
  1739.  
  1740.                 }
  1741.             }
  1742.  
  1743.         }
  1744.     }
  1745.  
  1746. }
  1747.  
  1748. ---The main function that handles the user interface
  1749. local function run_shell()
  1750.  
  1751.     kernel.validate_filesystem_structure(LUXUI_FS_STRUCTURE)
  1752.  
  1753.     local func, err = loadfile("rom/programs/shell.lua", os.create_user_environment())
  1754.     if not func then
  1755.         kernel.panic("Could not load CraftOS shell:\n"..err)
  1756.     end
  1757.     local shell_coro = coroutine.create(func)
  1758.     local event = {}
  1759.  
  1760.     term.clear()
  1761.     term.setCursorPos(1, 1)
  1762.  
  1763.     kernel.mark_routine_ready()
  1764.  
  1765.     while not autorun.is_startup_finished() do
  1766.         event = {coroutine.yield()}
  1767.     end
  1768.     event = {}
  1769.  
  1770.     while true do
  1771.         local ok, err = coroutine.resume(shell_coro, table.unpack(event))
  1772.         if not ok then
  1773.             kernel.panic("Shell coroutine got an error:\n"..err)
  1774.         end
  1775.         if coroutine.status(shell_coro) == "dead" then
  1776.             break
  1777.         end
  1778.         if kernel.is_system_shutting_down() then
  1779.             kernel.mark_routine_offline(false)
  1780.         end
  1781.         event = {coroutine.yield()}
  1782.     end
  1783.  
  1784.     local c = term.getTextColor()
  1785.     term.setTextColor(colors.purple)
  1786.     term.write("Good")
  1787.     term.setTextColor(colors.blue)
  1788.     print("bye")
  1789.     term.setTextColor(c)
  1790.     sleep(1.5)
  1791.     if not kernel.is_system_shutting_down() then
  1792.         os.shutdown()
  1793.     end
  1794.     kernel.mark_routine_offline()
  1795.  
  1796. end
  1797.  
  1798. return run_shell]],
  1799. [[--]].."[["..[[
  1800. This is the main script of LuxOS. It has multiple steps:
  1801. - It makes the "lux" and "kernel" APIs available.
  1802. - It enumerates all the Lux packages APIs and all the routines of the Lux packages.
  1803. - It makes all the Lux packages APIs available.
  1804. - It runs all of the Lux packages routines in parallel and watches for "Lux_panic" events.
  1805. - It shuts down the computer.
  1806.  
  1807. Note that no routine should ever stop! This would cause a kernel panic.
  1808. ]].."]]"..[[
  1809.  
  1810. if _G.lux then       -- LuxOS is already running. Do not start it again!
  1811.     -- local function override_shell()
  1812.     --     local w, h = term.getSize()
  1813.     --     local rshift = 1
  1814.     --     term.clear()
  1815.     --     term.setCursorPos(1, 1)
  1816.     --     term.setBackgroundColor(colors.black)
  1817.     --     term.setTextColor(colors.purple)
  1818.     --     term.write("Lux")
  1819.     --     term.setTextColor(colors.blue)
  1820.     --     term.write("OS")
  1821.     --     term.setTextColor(colors.gray)
  1822.     --     term.write(" 1.0")
  1823.     --     term.setTextColor(colors.white)
  1824.     --     term.setCursorPos(1, 2)
  1825.     --     term.setBackgroundColor(colors.magenta)
  1826.     --     term.setCursorPos(w - rshift - 2, 1)
  1827.     --     term.write(" ")
  1828.     --     term.setBackgroundColor(colors.purple)
  1829.     --     term.setCursorPos(w - rshift - 1, 1)
  1830.     --     term.write(" ")
  1831.     --     term.setBackgroundColor(colors.blue)
  1832.     --     term.setCursorPos(w - rshift, 1)
  1833.     --     term.write(" ")
  1834.     --     term.setBackgroundColor(colors.black)
  1835.     --     term.setCursorPos(1, 2)
  1836.     -- end
  1837.     -- local ok, err = pcall(override_shell)
  1838.     -- if not ok then
  1839.     --     error("Error setting up shell:\n"..err, 1)
  1840.     -- end
  1841.     return
  1842. end
  1843.  
  1844. local old_shutdown = os.shutdown
  1845. local old_reboot = os.reboot
  1846.  
  1847. function os.version()
  1848.     return "LuxOS 1.0"
  1849. end
  1850.  
  1851. local panic_recovery_coroutine = coroutine.create(function()
  1852.  
  1853.     -- Copy the references to all the original functions: in case of panic, we need to have working code, no matter what the user did to the APIs.
  1854.  
  1855.     local getSize = term.getSize
  1856.     local setBackgroundColor = term.setBackgroundColor
  1857.     local clear = term.clear
  1858.     local setCursorPos = term.setCursorPos
  1859.     local setTextColor = term.setTextColor
  1860.     local native_term = term.native()
  1861.     local redirect = term.redirect
  1862.  
  1863.     local purple = colors.purple
  1864.     local blue = colors.blue
  1865.     local black = colors.black
  1866.  
  1867.     local create_window = window.create
  1868.  
  1869.     -- This yield only returns if a panic occurs. It will return the panic message.
  1870.  
  1871.     local panic_message = coroutine.yield()
  1872.  
  1873.     redirect(native_term)
  1874.     local w, h = getSize()
  1875.     setBackgroundColor(purple)
  1876.     clear()
  1877.     setCursorPos(1, 1)
  1878.     setTextColor(black)
  1879.     print()
  1880.     local panic_header = "LUX KERNEL PANIC"
  1881.     panic_header = string.rep(" ", math.floor((w - #panic_header) / 2))..panic_header
  1882.     print(panic_header)
  1883.     setCursorPos(1, h - 1)
  1884.     print(panic_header)
  1885.     local error_message_zone = create_window(native_term, 1, 4, w, h - 6, true)
  1886.     local virtual_error_screen = create_window(error_message_zone, 1, 1, w, 1024, true)
  1887.     virtual_error_screen.setBackgroundColor(black)
  1888.     virtual_error_screen.setTextColor(blue)
  1889.     redirect(virtual_error_screen)
  1890.  
  1891.     local message = "\nYour computer ran into a big problem. Here is the issue:\n\n" .. panic_message .. "\n\nPress any key to reboot"
  1892.     local pos = 1
  1893.     print(message)
  1894.     local h_visible = h - 6
  1895.     local _, h_text = virtual_error_screen.getCursorPos()
  1896.     virtual_error_screen.reposition(1, pos, w, 1024)
  1897.  
  1898.     while true do
  1899.         local event = {coroutine.yield()}
  1900.         if event[1] == "key" then
  1901.             break
  1902.         elseif event[1] == "mouse_scroll" then
  1903.             pos = math.min(1, math.max(h_visible - h_text + 1, pos - event[2]))
  1904.             virtual_error_screen.reposition(1, pos, w, 1024)
  1905.         end
  1906.     end
  1907.     old_reboot()
  1908.  
  1909. end)
  1910. coroutine.resume(panic_recovery_coroutine)
  1911. -- print("Loading kernel...")
  1912.  
  1913. _G.libraries = {       -- Contains all the APIs that the user will have access to (making it its environment).
  1914.     term = term,
  1915.     colors = colors,
  1916.     colours = colours,
  1917.     fs = fs,
  1918.     os = os,
  1919.     table = table,
  1920.     string = string,
  1921.     coroutine = coroutine,
  1922.     math = math,
  1923.     peripheral = peripheral,
  1924.     http = http,
  1925.     textutils = textutils,
  1926.     turtle = turtle,
  1927.     commands = commands,
  1928.     settings = settings,
  1929.     paintutils = paintutils,
  1930.     parallel = parallel,
  1931.     window = window,
  1932.     event = event,
  1933.     disk = disk,
  1934.     help = help,
  1935. }
  1936.  
  1937. libraries.libraries = libraries
  1938.  
  1939. dofile("LuxOS/kernel.lua")
  1940. dofile("LuxOS/syscall.lua")
  1941. dofile("LuxOS/lux.lua")
  1942.  
  1943. -- print("Kernel loaded!")
  1944. kernel.promote_coroutine(panic_recovery_coroutine)
  1945.  
  1946. local PANIC = false
  1947. local PANIC_MESSAGE = ""
  1948.  
  1949. ---System hook for Lux kernel panic
  1950. local function catch_panic()
  1951.     while true do
  1952.         local event = {coroutine.yield()}
  1953.         if #event == 2 and event[1] == "Lux_panic" then
  1954.             PANIC_MESSAGE = event[2]
  1955.             PANIC = true
  1956.             return
  1957.         end
  1958.     end
  1959. end
  1960.  
  1961. ---As the name says, it runs all of LuxOS
  1962. local function run_everything()
  1963.     local packages = {}
  1964.     local routines = {}
  1965.  
  1966.     if not kernel.panic_pcall("fs.exists", fs.exists, "LuxOS") then
  1967.         lux.panic("'LuxOS' folder does not exists: your Lux installation might have been partially erased.")
  1968.         return
  1969.     end
  1970.  
  1971.     if not kernel.panic_pcall("fs.isDir", fs.isDir, "LuxOS") then
  1972.         lux.panic("'LuxOS' folder is actually a file: your Lux installation has been broken.")
  1973.         return
  1974.     end
  1975.  
  1976.     for index, package_name in pairs(kernel.panic_pcall("fs.list", fs.list, "LuxOS")) do
  1977.         local package_path = "LuxOS/"..package_name
  1978.         if kernel.panic_pcall("fs.isDir", fs.isDir, package_path) then
  1979.             if kernel.panic_pcall("fs.exists", fs.exists, package_path.."/lib.lua") then
  1980.                 packages[package_name] = package_path.."/lib.lua"
  1981.             end
  1982.             if kernel.panic_pcall("fs.exists", fs.exists, package_path.."/routine.lua") then
  1983.                 routines[package_name] = package_path.."/routine.lua"
  1984.             end
  1985.         end
  1986.     end
  1987.  
  1988.     for package_name, package_path in pairs(packages) do
  1989.         local libs = kernel.panic_pcall("dofile", dofile, package_path)
  1990.         for name, lib in pairs(libraries) do
  1991.             libraries[name] = lib
  1992.         end
  1993.     end
  1994.  
  1995.     for package_name, routine_path in pairs(routines) do
  1996.         local routine_main = kernel.panic_pcall("dofile", dofile, routine_path)
  1997.         if type(routine_main) ~= "function" then
  1998.             kernel.panic("Routine loader of package '"..package_name.."' did not return a function.'")
  1999.         end
  2000.         local coro = coroutine.create(routine_main)
  2001.         kernel.register_routine(package_name, coro)
  2002.     end
  2003.  
  2004.     -- Start runtime : wait for all routines to be ready.
  2005.  
  2006.     local event = {}
  2007.     while true do
  2008.         for name, coro in pairs(kernel.starting_routines("")) do
  2009.             -- print("Resuming starting routine '"..name.."' with '"..tostring(event[1]).."' event.")
  2010.             kernel.set_current_routine(name)
  2011.             local ok, err = coroutine.resume(coro, table.unpack(event))
  2012.             kernel.set_current_routine()
  2013.             if not ok then
  2014.                 kernel.panic("Kernel routine '"..name.."' had an exception during startup:\n"..err)
  2015.             elseif coroutine.status(coro) == "dead" then
  2016.                 kernel.panic("Kernel routine '"..name.."' stopped unexpectedly during startup.")
  2017.             end
  2018.         end
  2019.         if kernel.is_system_ready() then
  2020.             event = {}
  2021.             break
  2022.         end
  2023.         event = {coroutine.yield()}
  2024.     end
  2025.     syscall.validate_syscall_table()
  2026.  
  2027.     -- Normal runtime : everyone runs as if the world had no end
  2028.  
  2029.     while event[1] ~= "shutdown" do
  2030.         for name, coro in pairs(kernel.get_routines_for_event(event[1])) do
  2031.             -- print("Resuming routine '"..name.."' with '"..tostring(event[1]).."' event.")
  2032.             kernel.set_current_routine(name)
  2033.             local ok, err = coroutine.resume(coro, table.unpack(event))
  2034.             kernel.set_current_routine()
  2035.             if not ok then
  2036.                 kernel.panic("Kernel routine '"..name.."' had an exception:\n"..err)
  2037.             elseif coroutine.status(coro) == "dead" then
  2038.                 kernel.panic("Kernel routine '"..name.."' stopped unexpectedly.")
  2039.             end
  2040.         end
  2041.         event = {coroutine.yield()}
  2042.     end
  2043.  
  2044.     -- Shutdown runtime : wait for all routines to return true before shutting down.
  2045.  
  2046.     kernel.initialize_shutdown()
  2047.     local shutdown_info = {table.unpack(event, 2)}
  2048.  
  2049.     while true do
  2050.         for name, coro in pairs(kernel.disconnecting_routines(event[1])) do
  2051.             -- print("Resuming disconnecting routine '"..name.."' with '"..tostring(event[1]).."' event.")
  2052.             kernel.set_current_routine(name)
  2053.             local ok, err = coroutine.resume(coro, table.unpack(event))
  2054.             kernel.set_current_routine()
  2055.             if not ok then
  2056.                 kernel.panic("Kernel routine '"..name.."' had an exception during shutdown:\n"..err)
  2057.             elseif coroutine.status(coro) == "dead" then
  2058.                 kernel.panic("Kernel routine '"..name.."' stopped unexpectedly during shutdown.")
  2059.             end
  2060.         end
  2061.         if kernel.is_system_offline() then
  2062.             break
  2063.         end
  2064.         event = {coroutine.yield()}
  2065.     end
  2066.  
  2067.     -- Time to shutdown.
  2068.  
  2069.     if shutdown_info[1] == "s" then
  2070.         old_shutdown()
  2071.     elseif shutdown_info[1] == "r" then
  2072.         old_reboot()
  2073.     else
  2074.         kernel.panic("Shutdown event has incoherent arguments: "..kernel.tostring(shutdown_info))
  2075.     end
  2076.  
  2077. end
  2078.  
  2079. local catch_panic_coro, run_everything_coro = coroutine.create(catch_panic), coroutine.create(run_everything)
  2080. kernel.promote_coroutine(catch_panic_coro)
  2081. kernel.promote_coroutine(run_everything_coro)
  2082. local event = {}
  2083. while coroutine.status(catch_panic_coro) ~= "dead" do
  2084.     coroutine.resume(run_everything_coro, table.unpack(event))
  2085.     coroutine.resume(catch_panic_coro, table.unpack(event))
  2086.     event = {coroutine.yield()}
  2087. end
  2088.  
  2089. if PANIC then
  2090.  
  2091.     coroutine.resume(panic_recovery_coroutine, PANIC_MESSAGE)
  2092.     while true do
  2093.         local event = {coroutine.yield()}
  2094.         local ok, err = coroutine.resume(panic_recovery_coroutine, table.unpack(event))
  2095.         if not ok then
  2096.             print(err)
  2097.             return
  2098.         end
  2099.     end
  2100.  
  2101. end]],
  2102. [[--]].."[["..[[
  2103. This is the Lux process library. It defines the process class, and all its interface.
  2104. ]].."]]"..[[
  2105.  
  2106. _G.processes = {}     -- The process Lux API. Allows you to manage and interact with processes.
  2107.  
  2108.  
  2109.  
  2110.  
  2111.  
  2112. return {["processes"] = processes}]],
  2113. [[--]].."[["..[[
  2114. This is the standart Lux service API. It contains all the function to perform system calls to the service system.
  2115. ]].."]]"..[[
  2116.  
  2117. _G.services = {}        -- The service Lux API. Allows you to register services that run in background.
  2118.  
  2119.  
  2120.  
  2121.  
  2122.  
  2123. services.log = syscall.new(
  2124.     "services.log",
  2125.     ---Logs a message to the service journal.
  2126.     ---@param ... string The message to log in the journal. Converts all parameters to string (pretty printed if possible) and concatenates them.
  2127.     function (...)
  2128.         local args = {...}
  2129.         local message = ""
  2130.        
  2131.         ---Convert an argument to a string.
  2132.         ---@param arg any
  2133.         ---@return string
  2134.         local function convert(arg)
  2135.             if type(arg) == "table" then
  2136.                 local ok, err = pcall(textutils.serialise, arg)
  2137.                 if ok then
  2138.                     return err
  2139.                 end
  2140.             end
  2141.             return tostring(arg)
  2142.         end
  2143.  
  2144.         for index, arg in ipairs(args) do
  2145.             message = message..convert(arg)
  2146.         end
  2147.         if type(message) ~= "string" then
  2148.             error("expected string, got '"..type(message).."'", 2)
  2149.         end
  2150.         local ok, err = syscall.trampoline(message)
  2151.         if not ok then
  2152.             error(err, 2)
  2153.         end
  2154.     end
  2155. )
  2156.  
  2157.  
  2158.  
  2159.  
  2160.  
  2161. services.enumerate = syscall.new(
  2162.     "services.enumerate",
  2163.     ---Returns a table of the registered services, indexed by names associated to a boolean indicating if the service is running.
  2164.     ---@return {[string] : boolean} services The table of services.
  2165.     function ()
  2166.         local ok, err = syscall.trampoline()
  2167.         if not ok then
  2168.             error(err, 2)
  2169.         end
  2170.         return err
  2171.     end
  2172. )
  2173.  
  2174.  
  2175.  
  2176.  
  2177.  
  2178. services.install = syscall.new(
  2179.     "services.install",
  2180.     function ()
  2181.        
  2182.     end
  2183. )
  2184.  
  2185.  
  2186.  
  2187.  
  2188.  
  2189. services.uninstall = syscall.new(
  2190.     "services.uninstall",
  2191.     function ()
  2192.        
  2193.     end
  2194. )
  2195.  
  2196.  
  2197.  
  2198.  
  2199.  
  2200. services.start = syscall.new(
  2201.     "services.start",
  2202.     function ()
  2203.        
  2204.     end
  2205. )
  2206.  
  2207.  
  2208.  
  2209.  
  2210.  
  2211. services.stop = syscall.new(
  2212.     "services.stop",
  2213.     function ()
  2214.        
  2215.     end
  2216. )
  2217.  
  2218.  
  2219.  
  2220.  
  2221.  
  2222. services.read_logs = syscall.new(
  2223.     "services.read_logs",
  2224.     function ()
  2225.        
  2226.     end
  2227. )
  2228.  
  2229.  
  2230.  
  2231.  
  2232.  
  2233. return {["services"] = services}]],
  2234. [[--]].."[["..[[
  2235. This is the scheduler of the service system. It loads, runs and terminates all the services.
  2236.  
  2237. Note that a service script will be transformed into a coroutine that will be run by the scheduler.
  2238. This coroutine will get all the system events and should answer to the "service" events.
  2239. These events have two versions:
  2240. ("servcice", "start", "<service name>") received as script arguments.
  2241. ("service", "stop", "<service name>") received when the service should stop.
  2242. Note that when the stop event is received, the service will not be run again, even if the coroutine is still alive.
  2243. In the case of a "shutdown" event, the service is guaranteed to receive the "shutdown" event followed by a "service stop" event.
  2244. ]].."]]"..[[
  2245.  
  2246.  
  2247.  
  2248.  
  2249.  
  2250. local service_coroutines = {}     ---@type {[string] : thread} The table of services coroutines indexed by names
  2251. local CURRENT_SERVICE = nil         ---@type string? The currently running service, if any.
  2252. local SERVICES_ENABLED_DIR = "LuxOS/services/scripts/enabled/"           --- This is where all the currently enabled services script files will be located
  2253. local SERVICES_DISABLED_DIR = "LuxOS/services/scripts/disabled/"           --- This is where all the currently disabled services script files will be located
  2254. local SERVICES_LOGS = "LuxOS/services/logs"                     --- This is the log file, which holds all the services logs.
  2255. local SERVICE_FS_STRUCTURE = kernel.filesystem_structure{
  2256.  
  2257.     kernel.filesystem_node{
  2258.         name = "LuxOS",
  2259.         type = kernel.DIRECTORY,
  2260.         children = {
  2261.  
  2262.             kernel.filesystem_node{
  2263.                 name = "services",
  2264.                 type = kernel.DIRECTORY,
  2265.                 children = {
  2266.  
  2267.                     kernel.filesystem_node{
  2268.                         name = "scripts",
  2269.                         type = kernel.DIRECTORY,
  2270.                         mode = kernel.DIRECTORY.ENSURE_EXISTS,
  2271.                         children = {
  2272.  
  2273.                             kernel.filesystem_node{
  2274.                                 name = "enabled",
  2275.                                 type = kernel.DIRECTORY,
  2276.                                 mode = kernel.DIRECTORY.ENSURE_EXISTS
  2277.                             },
  2278.  
  2279.                             kernel.filesystem_node{
  2280.                                 name = "disabled",
  2281.                                 type = kernel.DIRECTORY,
  2282.                                 mode = kernel.DIRECTORY.ENSURE_EXISTS
  2283.                             }
  2284.  
  2285.                         }
  2286.                     },
  2287.  
  2288.                     kernel.filesystem_node{
  2289.                         name = "logs",
  2290.                         type = kernel.FILE,
  2291.                         mode = kernel.FILE.ENSURE_EXISTS
  2292.                     },
  2293.  
  2294.                     kernel.filesystem_node{
  2295.                         name = "routine.lua",
  2296.                         type = kernel.FILE
  2297.                     },
  2298.  
  2299.                     kernel.filesystem_node{
  2300.                         name = "lib.lua",
  2301.                         type = kernel.FILE
  2302.                     }
  2303.  
  2304.                 }
  2305.             }
  2306.  
  2307.         }
  2308.     }
  2309.  
  2310. }
  2311.  
  2312.  
  2313. ---Internal function that creates a table to serve as the runtime environment of service coroutines.
  2314. local function create_service_environment()
  2315.     local env = os.create_user_environment()
  2316.     env["print"] = services.log
  2317.     env["write"] = services.log
  2318.     return env
  2319. end
  2320.  
  2321.  
  2322. ---Internal wrapper function to run a service script file.
  2323. ---@param path string The path to the service script file.
  2324. ---@param name string The name of the service.
  2325. local function service_main(path, name)
  2326.     local service, err = loadfile(path, create_service_environment())
  2327.     if not service then
  2328.         error(err, 2)
  2329.     else
  2330.         service("service", "start", name)
  2331.     end
  2332. end
  2333.  
  2334.  
  2335. ---Starts a service's coroutine from its script file and adds it in the service registry.
  2336. ---@param name string The name of the service
  2337. ---@return boolean success If the service was successfully loaded
  2338. ---@return string success_message The error message if not, (or "Success")
  2339. local function launch_service(name)
  2340.     local coro = coroutine.create(service_main)
  2341.     CURRENT_SERVICE = name
  2342.     local ok, err = coroutine.resume(coro, SERVICES_ENABLED_DIR..name, name)
  2343.     CURRENT_SERVICE = nil
  2344.     if not ok then
  2345.         return false, "Service '"..name.."' had an error at startup:\n"..tostring(err)
  2346.     end
  2347.     if coroutine.status(coro) ~= "suspended" then
  2348.         return false, "Service '"..name.."' returned at startup"
  2349.     end
  2350.     service_coroutines[name] = coro
  2351.     return true, "Success"
  2352. end
  2353.  
  2354.  
  2355. ---Stops a service's coroutine and removes it from the service registry.
  2356. ---Note that even on failure, the service is removed from the registry.
  2357. ---@param name string The name of the service
  2358. ---@return boolean success If the service was successfully stopped
  2359. ---@return string success_message The error message if not, (or "Success")
  2360. local function shutdown_service(name)
  2361.     local coro = service_coroutines[name]
  2362.     coroutine.resume(coro, "service", "stop")
  2363.     service_coroutines[name] = nil
  2364.     if coroutine.status(coro) ~= "dead" then
  2365.         return false, "Coroutine of service '"..name.."' did not die when sent the 'service stop' event"
  2366.     end
  2367.     return true, "Success"
  2368. end
  2369.  
  2370.  
  2371. ---Returns a table of the services indexed by name. The values are booleans indicating if the service is running.
  2372. ---@return {[string] : boolean} services The table of all services by names.
  2373. local function enumerate_services()
  2374.     local tab = {}
  2375.     for i, name in ipairs(kernel.panic_pcall("fs.list", fs.list, SERVICES_ENABLED_DIR)) do
  2376.         tab[name] = true
  2377.     end
  2378.     for i, name in ipairs(kernel.panic_pcall("fs.list", fs.list, SERVICES_DISABLED_DIR)) do
  2379.         tab[name] = false
  2380.     end
  2381.     return tab
  2382. end
  2383.  
  2384.  
  2385. ---Moves a service from the disabled folder into the enabled folder.
  2386. ---@param name string The name of the service to enable
  2387. local function enable_service(name)
  2388.     kernel.panic_pcall("fs.move", fs.move, SERVICES_DISABLED_DIR..name, SERVICES_ENABLED_DIR..name)
  2389. end
  2390.  
  2391.  
  2392. ---Moves a service from the enabled folder into the disabled folder.
  2393. ---@param name string The name of the service to disable
  2394. local function disable_service(name)
  2395.     kernel.panic_pcall("fs.move", fs.move, SERVICES_ENABLED_DIR..name, SERVICES_DISABLED_DIR..name)
  2396. end
  2397.  
  2398.  
  2399. ---Installs a service from the given lua script file with the given name. The service will be disabled.
  2400. ---@param source string The path to the script file of the service
  2401. ---@param name string The name of the new service
  2402. local function install_service(source, name)
  2403.     kernel.panic_pcall("fs.copy", fs.copy, source, SERVICES_DISABLED_DIR..name)
  2404. end
  2405.  
  2406.  
  2407. ---Uninstalls the service with the given name. The service must be disabled.
  2408. ---@param name string The name of the service to uninstall
  2409. local function uninstall_service(name)
  2410.     kernel.panic_pcall("fs.delete", fs.delete, SERVICES_DISABLED_DIR..name)
  2411. end
  2412.  
  2413.  
  2414.  
  2415.  
  2416.  
  2417. ---Answers the system calls to services.log.
  2418. local function answer_calls_to_log(...)
  2419.     local args = {...}
  2420. end
  2421.  
  2422. ---Answers the system calls to services.enumerate.
  2423. local function answer_calls_to_enumerate(...)
  2424.     local args = {...}
  2425.     if #args > 0 then
  2426.         return false, "syscall got "..tostring(#args).." parameters, expected 0"
  2427.     else
  2428.         return true, kernel.panic_pcall("enumerate_services", enumerate_services)
  2429.     end
  2430. end
  2431.  
  2432. ---Answers the system calls to services.install.
  2433. local function answer_calls_to_install(...)
  2434.     local args = {...}
  2435. end
  2436.  
  2437. ---Answers the system calls to services.uninstall.
  2438. local function answer_calls_to_uninstall(...)
  2439.     local args = {...}
  2440. end
  2441.  
  2442. ---Answers the system calls to services.start.
  2443. local function answer_calls_to_start(...)
  2444.     local args = {...}
  2445. end
  2446.  
  2447. ---Answers the system calls to services.stop.
  2448. local function answer_calls_to_stop(...)
  2449.     local args = {...}
  2450. end
  2451.  
  2452. ---Answers the system calls to services.read_logs.
  2453. local function answer_calls_to_read_logs(...)
  2454.     local args = {...}
  2455. end
  2456.  
  2457.  
  2458.  
  2459.  
  2460.  
  2461. local function main()
  2462.  
  2463.     kernel.validate_filesystem_structure(SERVICE_FS_STRUCTURE)
  2464.  
  2465.     syscall.affect_routine(services.log, answer_calls_to_log)
  2466.     syscall.affect_routine(services.enumerate, answer_calls_to_enumerate)
  2467.     syscall.affect_routine(services.install, answer_calls_to_install)
  2468.     syscall.affect_routine(services.uninstall, answer_calls_to_uninstall)
  2469.     syscall.affect_routine(services.start, answer_calls_to_start)
  2470.     syscall.affect_routine(services.stop, answer_calls_to_stop)
  2471.     syscall.affect_routine(services.read_logs, answer_calls_to_read_logs)
  2472.  
  2473.     kernel.mark_routine_ready()
  2474.  
  2475.     -- Startup : load enabled services
  2476.  
  2477.     for name, enabled in pairs(enumerate_services()) do
  2478.         if enabled then
  2479.             local ok, err = launch_service(name)
  2480.             if not ok then
  2481.                 services.log("Service '"..name.."' crashed at startup:\n"..err)
  2482.             end
  2483.         end
  2484.     end
  2485.  
  2486.     -- Normal runtime : forward events to services
  2487.  
  2488.     while not kernel.is_system_shutting_down() do
  2489.         local event = {coroutine.yield()}
  2490.         for name, coro in pairs(service_coroutines) do
  2491.             local ok, err = coroutine.resume(coro, table.unpack(event))
  2492.             if not ok then
  2493.                 services.log("Service '"..name.."' stopped due to an exception:\n"..err)
  2494.                 service_coroutines[name] = nil
  2495.                 ok, err = launch_service(name)
  2496.                 if not ok then
  2497.                     services.log("Service '"..name.."' could not be restarted after an exception as another one occured at startup:\n"..err)
  2498.                     disable_service(name)
  2499.                 end
  2500.             end
  2501.         end
  2502.     end
  2503.  
  2504.     -- Shutdown time : shutdown all services
  2505.  
  2506.     for name, enabled in pairs(enumerate_services()) do
  2507.         if enabled then
  2508.             local ok, err = shutdown_service(name)
  2509.             if not ok then
  2510.                 services.log("Service '"..name.."' crashed at system shutdown:\n"..err)
  2511.             end
  2512.         end
  2513.     end
  2514.  
  2515.     kernel.mark_routine_offline()
  2516.  
  2517. end
  2518.  
  2519. return main]],
  2520. [[--]].."[["..[[
  2521. This library is used by the kernel to define and hook system calls.
  2522. ]].."]]"..[[
  2523.  
  2524.  
  2525.  
  2526.  
  2527.  
  2528. _G.syscall = {}     -- The Lux syscall API. Mostly restricted to kernel.
  2529. libraries.syscall = syscall
  2530. local syscall_table = {}    ---@type {[string] : SysCall} The system call table.
  2531. local syscall_routines = {} ---@type {[string] : thread} The table of kernel routines that handle the system calls.
  2532.  
  2533.  
  2534. ---@class SysCall The class for system call objects. These are special function accessible for the user that jump back into kernel space.
  2535. ---@field name string The name of the system call.
  2536. ---@field __user_func function The function that is called when calling the SysCall object.
  2537. local SysCall = {}
  2538.  
  2539. SysCall.__index = SysCall
  2540. SysCall.__name = "syscall"
  2541.  
  2542. ---Creates a new syscall object.
  2543. ---@generic P
  2544. ---@generic R
  2545. ---@param syscallinfo [string, fun(... : P) : R] The required parameters for creating a syscall: a name and the user function.
  2546. ---@return SysCall syscall The new system call object.
  2547. function SysCall:new(syscallinfo)
  2548.     kernel.check_kernel_space_before_running()
  2549.     local syscall = {name = syscallinfo[1], __user_func = syscallinfo[2]}
  2550.     if type(syscall.name) ~= "string" then
  2551.         kernel.panic("SysCall's 'name' field should be a string, not '"..type(syscall.name).."'", 1)
  2552.     end
  2553.     if syscall_table[syscall.name] ~= nil then
  2554.         kernel.panic("Syscall '"..syscall.name.."' already exists.", 1)
  2555.     end
  2556.     if type(syscall.__user_func) ~= "function" then
  2557.         kernel.panic("SysCall's 'user_func' attribute should be a function, not '"..type(syscall.__user_func).."'", 1)
  2558.     end
  2559.     setmetatable(syscall, self)
  2560.     syscall_table[syscall.name] = syscall
  2561.     return syscall
  2562. end
  2563.  
  2564. local ongoing_calls = {}    ---@type string[] The ongoing system call pile.
  2565.  
  2566. ---Calls the user function
  2567. ---@param ... any
  2568. ---@return any
  2569. function SysCall:__call(...)
  2570.     table.insert(ongoing_calls, self.name)
  2571.     local res = {pcall(self.__user_func, ...)}
  2572.     local ok = table.remove(res, 1)
  2573.     local exiting_call = table.remove(ongoing_calls)
  2574.     if exiting_call ~= self.name then
  2575.         kernel.panic("Corrupted system call pile: exited a call of '"..exiting_call.."' where '"..self.name.."' was expected.", 1)
  2576.     end
  2577.     if not ok then
  2578.         error(res[1], 0)
  2579.     else
  2580.         return table.unpack(res)
  2581.     end
  2582. end
  2583.  
  2584.  
  2585.  
  2586.  
  2587.  
  2588. local report_syscall_crash_coro = coroutine.create(
  2589. ---Single-use kernel coroutine that throws panic when a syscall routine crashes.
  2590. ---@param syscall SysCall
  2591. ---@param err string?
  2592. ---@param ... any
  2593. function (syscall, err, ...)
  2594.     local ok, res = pcall(textutils.serialise, {...})
  2595.     if not ok then
  2596.         res = tostring({...})
  2597.     end
  2598.     if err == nil then
  2599.         kernel.panic("Syscall '"..syscall.name.."' routine stopped unexpectedly when it received arguments : '"..res.."'", 2)
  2600.     else
  2601.         kernel.panic("Syscall '"..syscall.name.."' routine got an error when it received arguments : '"..res.."' :\n"..err, 2)
  2602.     end
  2603. end)
  2604. kernel.promote_coroutine(report_syscall_crash_coro)
  2605.  
  2606. ---Performs the actual system call: jumps into kernel space passing on the arguments.
  2607. ---@param ... any The arguments that the system will work with.
  2608. ---@return boolean success Did the system call succeed
  2609. ---@return any ... The return values of the system call or an error message.
  2610. function syscall.trampoline(...)
  2611.     if #ongoing_calls == 0 then
  2612.         if kernel.kernel_space() then
  2613.             kernel.panic("Corrupted system call pile: syscall.trampoline() called with no running system calls.", 1)
  2614.         else
  2615.             error("This function can only be called from inside a system call.", 2)
  2616.         end
  2617.     end
  2618.     local call_name = ongoing_calls[#ongoing_calls]
  2619.     -- if kernel.kernel_space() then
  2620.     --     res = {pcall(syscall_handlers[call_name], ...)}
  2621.     --     local ok = table.remove(res, 1)
  2622.     --     if not ok then
  2623.     --         coroutine.resume(report_syscall_crash_coro, syscall_table[call_name], res[1], ...)
  2624.     --     end
  2625.     --     if coroutine.status(syscall_routines[call_name]) == "dead" then
  2626.     --         coroutine.resume(report_syscall_crash_coro, syscall_table[call_name], nil, ...)
  2627.     --     end
  2628.     -- else
  2629.     --     res = {coroutine.resume(syscall_routines[call_name], ...)}
  2630.     --     local ok = table.remove(res, 1)
  2631.     --     if not ok then
  2632.     --         coroutine.resume(report_syscall_crash_coro, syscall_table[call_name], res[1], ...)
  2633.     --     end
  2634.     --     if coroutine.status(syscall_routines[call_name]) == "dead" then
  2635.     --         coroutine.resume(report_syscall_crash_coro, syscall_table[call_name], nil, ...)
  2636.     --     end
  2637.     -- end
  2638.  
  2639.     local ok, syscall_coro = coroutine.resume(syscall_routines[call_name])
  2640.     if not ok then
  2641.         kernel.panic("Syscall '"..call_name.."' generating routine crashed: "..syscall_coro, 2)
  2642.     end
  2643.     local res = {coroutine.resume(syscall_coro, ...)}
  2644.     while true do
  2645.         ok = table.remove(res, 1)
  2646.         if not ok then
  2647.             coroutine.resume(report_syscall_crash_coro, syscall_table[call_name], res[1], ...)
  2648.         end
  2649.         if coroutine.status(syscall_coro) == "dead" then
  2650.             break
  2651.         end
  2652.         res = {coroutine.resume(syscall_coro, coroutine.yield())}
  2653.     end
  2654.     return table.unpack(res)
  2655. end
  2656.  
  2657.  
  2658.  
  2659.  
  2660.  
  2661. ---Creates a new syscall object.
  2662. ---@generic F : function
  2663. ---@param name string The name of the system call.
  2664. ---@param func F The user function.
  2665. ---@return F syscall system call wrapped function.
  2666. function syscall.new(name, func)
  2667.     kernel.check_kernel_space_before_running()
  2668.     return SysCall:new{name, func}
  2669. end
  2670.  
  2671.  
  2672.  
  2673.  
  2674.  
  2675. ---Affects the given function to the handling of the specified system call.
  2676. ---It will receive the parameters of the system call to answer and should return back a boolean indicating if the call succeeded and any return values.
  2677. ---@param syscall SysCall The system call to affect this routine to.
  2678. ---@param handler_func function The function that will handle all incomming system calls.
  2679. function syscall.affect_routine(syscall, handler_func)
  2680.     kernel.check_kernel_space_before_running()
  2681.     if syscall_routines[syscall.name] ~= nil then
  2682.         kernel.panic("Syscall '"..syscall.name.."' routine has already been affected.", 1)
  2683.     end
  2684.  
  2685.     local function do_system_call(func, func_name)
  2686.         local res = {pcall(func, table.unpack({coroutine.yield()}))}
  2687.         local ok = table.remove(res, 1)
  2688.         if not ok then
  2689.             kernel.panic("Syscall '"..func_name.."' crashed: "..res[1], 2)
  2690.         end
  2691.         return table.unpack(res)
  2692.     end
  2693.  
  2694.     local generator_coro = coroutine.create(
  2695.         function (func)
  2696.             local func_name = "syscall."..syscall.name..".handler"
  2697.             coroutine.yield()
  2698.             while true do
  2699.                 local coro = coroutine.create(do_system_call)
  2700.                 coroutine.resume(coro, func, func_name)
  2701.                 kernel.promote_coroutine(coro)
  2702.                 coroutine.yield(coro)
  2703.             end
  2704.         end
  2705.     )
  2706.  
  2707.     kernel.promote_coroutine(generator_coro)
  2708.     syscall_routines[syscall.name] = generator_coro
  2709.     coroutine.resume(generator_coro, handler_func)
  2710. end
  2711.  
  2712.  
  2713.  
  2714.  
  2715.  
  2716. ---Validates the system call table when the system is about to start. It ensures that each system call has a handler and routine.
  2717. function syscall.validate_syscall_table()
  2718.     kernel.check_kernel_space_before_running()
  2719.     for name, syscall in pairs(syscall_table) do
  2720.         if syscall_routines[name] == nil then
  2721.             kernel.panic("SysCall '"..syscall.name.."' has not routine affected to it!")
  2722.         end
  2723.     end
  2724. end
  2725.  
  2726.  
  2727.  
  2728.  
  2729.  
  2730. ---Returns the table of all system calls indexed by names.
  2731. ---@return {[string] : SysCall} table The system call table.
  2732. function syscall.table()
  2733.     local table = {}
  2734.     for name, syscall in pairs(syscall_table) do
  2735.         table[name] = syscall
  2736.     end
  2737.     return table
  2738. end]]
  2739. }
  2740.  
  2741. local function install(node, parent_path)
  2742.     if node.type == DIRECTORY then
  2743.         if not fs.exists(parent_path..node.name) then
  2744.             fs.makeDir(parent_path..node.name)
  2745.         end
  2746.         for _, child in ipairs(node.children) do
  2747.             install(child, parent_path..node.name.."/")
  2748.         end
  2749.     elseif node.type == FILE then
  2750.         local path = parent_path..node.name
  2751.         if not fs.exists(path) then
  2752.             local content = raw_package[node.code]
  2753.             print("Installing file '"..path.."'...")
  2754.             local file = fs.open(path, "w")
  2755.             file.write(content)
  2756.             file.close()
  2757.         end
  2758.     end
  2759. end
  2760.  
  2761. install(package, "")
  2762.  
  2763. print("Installation is finished. Press any key to boot LuxOS.")
  2764. while true do
  2765.     local event = coroutine.yield()
  2766.     if event == "key" then
  2767.         break
  2768.     end
  2769. end
  2770.  
  2771. dofile(ENTRY_POINT)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement