1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
--[[
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 boot (kernel_path, initrd_path, cmdline)
print ("Loading " .. kernel_path .. " ...")
local kernel = sl.loadfile (kernel_path)
local initrd
if (initrd_path) then
print ("Loading " .. initrd_path .. " ...")
initrd = sl.initramfs():load (initrd_path)
end
sl.boot_it (kernel, initrd, cmdline)
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
if not lfs.attributes (initrd, "size") then
initrd = nil
end
table.insert (params.items, {
show = name .. (initrd and " +initrd" or ""),
version = version,
execute = function ()
boot (path, initrd,
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 get (x)
if type (x) == "function" then
return x ()
else
return x
end
end
local function draw (params)
print (get (params.title) or "\n=== Boot menu ===")
for i, item in ipairs (params.items) do
print ((i == params.default and " > " or " ") .. i .. " " .. get (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
}
|