1 --[[ 2 Automatically generated boot menu of the installed Linux kernels 3 4 Example: 5 6 m = require "automenu" 7 m.run { dir = "/", 8 default = 1, 9 timeout = 5, 10 append = "root=/dev/hda2 ro", 11 } 12 13 TODO: 14 - add hooks 15 - demo adding break options from user config 16 - kernel flavor preference (pae/rt) 17 ]] 18 19 local lfs = require "lfs" 20 local sl = require "syslinux" 21 22 local single = false 23 local verbosity = 2 24 25 local function modifiers () 26 return (single and " single" or "") .. ({" quiet",""," debug"})[verbosity] 27 end 28 29 local function scan (params) 30 local sep = string.sub (params.dir, -1) == "/" and "" or "/" 31 if not params.items then params.items = {} end 32 for name in lfs.dir (params.dir) do 33 local path = params.dir .. sep .. name 34 if lfs.attributes (path, "mode") == "file" then 35 local from,to,version = string.find (name, "^vmlinuz%-(.*)") 36 if from then 37 local initrd = params.dir .. sep .. "initrd.img-" .. version 38 local initrd_param = "" 39 if lfs.attributes (initrd, "size") then 40 initrd_param = "initrd=" .. initrd .. " " 41 end 42 table.insert (params.items, { 43 show = function () return name end, 44 version = version, 45 execute = function () sl.boot_linux (path, initrd_param .. params.append .. modifiers ()) end 46 }) 47 end 48 end 49 end 50 end 51 52 local function version_gt (v1, v2) 53 local negatives = {"rc", "pre"} 54 local m1, r1 = string.match (v1, "^(%D*)(.*)") 55 local m2, r2 = string.match (v2, "^(%D*)(.*)") 56 if m1 ~= m2 then 57 for _, suffix in ipairs (negatives) do 58 suffix = "-" .. suffix 59 if m1 == suffix and m2 ~= suffix then 60 return false 61 elseif m1 ~= suffix and m2 == suffix then 62 return true 63 end 64 end 65 return m1 > m2 66 end 67 m1, r1 = string.match (r1, "^(%d*)(.*)") 68 m2, r2 = string.match (r2, "^(%d*)(.*)") 69 m1 = tonumber (m1) or 0 70 m2 = tonumber (m2) or 0 71 if m1 ~= m2 then 72 return m1 > m2 73 end 74 if r1 == "" and r2 == "" then 75 return false 76 end 77 return version_gt (r1, r2) 78 end 79 80 local function kernel_gt (k1, k2) 81 return version_gt (k1.version, k2.version) 82 end 83 84 local function print_or_call (x, def) 85 local t = type (x) 86 if t == "nil" then 87 if def then print (def) end 88 elseif t == "function" then 89 x () 90 else 91 print (x) 92 end 93 end 94 95 local function draw (params) 96 print_or_call (params.title, "\n=== Boot menu ===") 97 for i, item in ipairs (params.items) do 98 print ((i == params.default and " > " or " ") .. i .. " " .. item.show ()) 99 end 100 print ("\nKernel arguments:\n " .. params.append .. modifiers ()) 101 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") 102 end 103 104 local function choose (params) 105 draw (params) 106 print ("\nBooting in " .. params.timeout .. " s...") 107 while true do 108 local i = sl.get_key (params.timeout * 1000) 109 if i == sl.KEY.ESC then 110 break 111 else 112 if i == sl.KEY.NONE or i == sl.KEY.ENTER then 113 i = params.default 114 elseif i == sl.KEY.DOWN then 115 params.default = params.default < #params.items and params.default + 1 or #params.items 116 elseif i == sl.KEY.UP then 117 params.default = params.default > 1 and params.default - 1 or 1 118 else 119 i = i - string.byte "0" 120 end 121 if params.items[i] then 122 params.items[i].execute () 123 end 124 params.timeout = 0 125 draw (params) 126 end 127 end 128 end 129 130 local function run (params) 131 scan (params) 132 if not next (params.items) then 133 print ("No kernels found in directory " .. params.dir) 134 os.exit (false) 135 end 136 table.sort (params.items, kernel_gt) 137 table.insert (params.items, { 138 show = function () return "Single user: " .. (single and "true" or "false") end, 139 execute = function () single = not single end 140 }) 141 table.insert (params.items, { 142 show = function () return "Verbosity: " .. ({"quiet","normal","debug"})[verbosity] end, 143 execute = function () verbosity = verbosity < 3 and verbosity + 1 or 1 end 144 }) 145 choose (params) 146 end 147 148 return { 149 scan = scan, 150 choose = choose, 151 run = run 152 } 153