| --[[ |
| Automatically generated boot menu of the installed Linux kernels |
| |
| Example: |
| |
| m = require "automenu" |
| m.run { dir = "/", |
| default = 1, |
| timeout = 5, |
| append = "root=/dev/hda2 ro", |
| } |
| |
| TODO: |
| - add hooks |
| - demo adding break options from user config |
| - kernel flavor preference (pae/rt) |
| ]] |
| |
| local lfs = require "lfs" |
| local sl = require "syslinux" |
| |
| local single = false |
| local verbosity = 2 |
| |
| local function modifiers () |
| return (single and " single" or "") .. ({" quiet",""," debug"})[verbosity] |
| end |
| |
| local function scan (params) |
| local sep = string.sub (params.dir, -1) == "/" and "" or "/" |
| if not params.items then params.items = {} end |
| for name in lfs.dir (params.dir) do |
| local path = params.dir .. sep .. name |
| if lfs.attributes (path, "mode") == "file" then |
| local from,to,version = string.find (name, "^vmlinuz%-(.*)") |
| if from then |
| local initrd = params.dir .. sep .. "initrd.img-" .. version |
| local initrd_param = "" |
| if lfs.attributes (initrd, "size") then |
| initrd_param = "initrd=" .. initrd .. " " |
| end |
| table.insert (params.items, { |
| show = function () return name end, |
| version = version, |
| execute = function () sl.boot_linux (path, initrd_param .. params.append .. modifiers ()) end |
| }) |
| end |
| end |
| end |
| end |
| |
| local function version_gt (v1, v2) |
| local negatives = {"rc", "pre"} |
| local m1, r1 = string.match (v1, "^(%D*)(.*)") |
| local m2, r2 = string.match (v2, "^(%D*)(.*)") |
| if m1 ~= m2 then |
| for _, suffix in ipairs (negatives) do |
| suffix = "-" .. suffix |
| if m1 == suffix and m2 ~= suffix then |
| return false |
| elseif m1 ~= suffix and m2 == suffix then |
| return true |
| end |
| end |
| return m1 > m2 |
| end |
| m1, r1 = string.match (r1, "^(%d*)(.*)") |
| m2, r2 = string.match (r2, "^(%d*)(.*)") |
| m1 = tonumber (m1) or 0 |
| m2 = tonumber (m2) or 0 |
| if m1 ~= m2 then |
| return m1 > m2 |
| end |
| if r1 == "" and r2 == "" then |
| return false |
| end |
| return version_gt (r1, r2) |
| end |
| |
| local function kernel_gt (k1, k2) |
| return version_gt (k1.version, k2.version) |
| end |
| |
| local function print_or_call (x, def) |
| local t = type (x) |
| if t == "nil" then |
| if def then print (def) end |
| elseif t == "function" then |
| x () |
| else |
| print (x) |
| end |
| end |
| |
| local function draw (params) |
| print_or_call (params.title, "\n=== Boot menu ===") |
| for i, item in ipairs (params.items) do |
| print ((i == params.default and " > " or " ") .. i .. " " .. item.show ()) |
| end |
| print ("\nKernel arguments:\n " .. params.append .. modifiers ()) |
| print ("\nHit a number to select from the menu,\n ENTER to accept default,\n ESC to exit\n or any other key to print menu again") |
| end |
| |
| local function choose (params) |
| draw (params) |
| print ("\nBooting in " .. params.timeout .. " s...") |
| while true do |
| local i = sl.get_key (params.timeout * 1000) |
| if i == sl.KEY.ESC then |
| break |
| else |
| if i == sl.KEY.NONE or i == sl.KEY.ENTER then |
| i = params.default |
| elseif i == sl.KEY.DOWN then |
| params.default = params.default < #params.items and params.default + 1 or #params.items |
| elseif i == sl.KEY.UP then |
| params.default = params.default > 1 and params.default - 1 or 1 |
| else |
| i = i - string.byte "0" |
| end |
| if params.items[i] then |
| params.items[i].execute () |
| end |
| params.timeout = 0 |
| draw (params) |
| end |
| end |
| end |
| |
| local function run (params) |
| scan (params) |
| if not next (params.items) then |
| print ("No kernels found in directory " .. params.dir) |
| os.exit (false) |
| end |
| table.sort (params.items, kernel_gt) |
| table.insert (params.items, { |
| show = function () return "Single user: " .. (single and "true" or "false") end, |
| execute = function () single = not single end |
| }) |
| table.insert (params.items, { |
| show = function () return "Verbosity: " .. ({"quiet","normal","debug"})[verbosity] end, |
| execute = function () verbosity = verbosity < 3 and verbosity + 1 or 1 end |
| }) |
| choose (params) |
| end |
| |
| return { |
| scan = scan, |
| choose = choose, |
| run = run |
| } |