1-- 2-- SPDX-License-Identifier: BSD-2-Clause 3-- 4-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 5-- 6-- Redistribution and use in source and binary forms, with or without 7-- modification, are permitted provided that the following conditions 8-- are met: 9-- 1. Redistributions of source code must retain the above copyright 10-- notice, this list of conditions and the following disclaimer. 11-- 2. Redistributions in binary form must reproduce the above copyright 12-- notice, this list of conditions and the following disclaimer in the 13-- documentation and/or other materials provided with the distribution. 14-- 15-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25-- SUCH DAMAGE. 26-- 27 28local config = require("config") 29local core = require("core") 30 31local cli = {} 32 33-- Internal function 34-- Parses arguments to boot and returns two values: kernel_name, argstr 35-- Defaults to nil and "" respectively. 36-- This will also parse arguments to autoboot, but the with_kernel argument 37-- will need to be explicitly overwritten to false 38local function parseBootArgs(argv, with_kernel) 39 if with_kernel == nil then 40 with_kernel = true 41 end 42 if #argv == 0 then 43 if with_kernel then 44 return nil, "" 45 else 46 return "" 47 end 48 end 49 local kernel_name 50 local argstr = "" 51 52 for _, v in ipairs(argv) do 53 if with_kernel and v:sub(1,1) ~= "-" then 54 kernel_name = v 55 else 56 argstr = argstr .. " " .. v 57 end 58 end 59 if with_kernel then 60 return kernel_name, argstr 61 else 62 return argstr 63 end 64end 65 66local function setModule(module, loading) 67 if loading and config.enableModule(module) then 68 print(module .. " will be loaded") 69 elseif not loading and config.disableModule(module) then 70 print(module .. " will not be loaded") 71 end 72end 73 74-- Declares a global function cli_execute that attempts to dispatch the 75-- arguments passed as a lua function. This gives lua a chance to intercept 76-- builtin CLI commands like "boot" 77-- This function intentionally does not follow our general naming guideline for 78-- functions. This is global pollution, but the clearly separated 'cli' looks 79-- more like a module indicator to serve as a hint of where to look for the 80-- corresponding definition. 81function cli_execute(...) 82 local argv = {...} 83 -- Just in case... 84 if #argv == 0 then 85 return loader.command(...) 86 end 87 88 local cmd_name = argv[1] 89 local cmd = cli[cmd_name] 90 if cmd ~= nil and type(cmd) == "function" then 91 -- Pass argv wholesale into cmd. We could omit argv[0] since the 92 -- traditional reasons for including it don't necessarily apply, 93 -- it may not be totally redundant if we want to have one global 94 -- handling multiple commands 95 return cmd(...) 96 else 97 return loader.command(...) 98 end 99 100end 101 102function cli_execute_unparsed(str) 103 return cli_execute(loader.parse(str)) 104end 105 106-- Module exports 107 108function cli.boot(...) 109 local _, argv = cli.arguments(...) 110 local kernel, argstr = parseBootArgs(argv) 111 if kernel ~= nil then 112 loader.perform("unload") 113 config.selectKernel(kernel) 114 end 115 core.boot(argstr) 116end 117 118function cli.autoboot(...) 119 local _, argv = cli.arguments(...) 120 local argstr = parseBootArgs(argv, false) 121 core.autoboot(argstr) 122end 123 124cli['boot-conf'] = function(...) 125 local _, argv = cli.arguments(...) 126 local kernel, argstr = parseBootArgs(argv) 127 if kernel ~= nil then 128 loader.perform("unload") 129 config.selectKernel(kernel) 130 end 131 core.autoboot(argstr) 132end 133 134cli['read-conf'] = function(...) 135 local _, argv = cli.arguments(...) 136 config.readConf(assert(core.popFrontTable(argv))) 137end 138 139cli['reload-conf'] = function() 140 config.reload() 141end 142 143cli["enable-module"] = function(...) 144 local _, argv = cli.arguments(...) 145 if #argv == 0 then 146 print("usage error: enable-module module") 147 return 148 end 149 150 setModule(argv[1], true) 151end 152 153cli["disable-module"] = function(...) 154 local _, argv = cli.arguments(...) 155 if #argv == 0 then 156 print("usage error: disable-module module") 157 return 158 end 159 160 setModule(argv[1], false) 161end 162 163cli["toggle-module"] = function(...) 164 local _, argv = cli.arguments(...) 165 if #argv == 0 then 166 print("usage error: toggle-module module") 167 return 168 end 169 170 local module = argv[1] 171 setModule(module, not config.isModuleEnabled(module)) 172end 173 174cli["show-module-options"] = function() 175 local module_info = config.getModuleInfo() 176 local modules = module_info['modules'] 177 local blacklist = module_info['blacklist'] 178 local lines = {} 179 180 for module, info in pairs(modules) do 181 if #lines > 0 then 182 lines[#lines + 1] = "" 183 end 184 185 lines[#lines + 1] = "Name: " .. module 186 if info.name then 187 lines[#lines + 1] = "Path: " .. info.name 188 end 189 190 if info.type then 191 lines[#lines + 1] = "Type: " .. info.type 192 end 193 194 if info.flags then 195 lines[#lines + 1] = "Flags: " .. info.flags 196 end 197 198 if info.before then 199 lines[#lines + 1] = "Before load: " .. info.before 200 end 201 202 if info.after then 203 lines[#lines + 1] = "After load: " .. info.after 204 end 205 206 if info.error then 207 lines[#lines + 1] = "Error: " .. info.error 208 end 209 210 local status 211 if blacklist[module] and not info.force then 212 status = "Blacklisted" 213 elseif info.load == "YES" then 214 status = "Load" 215 else 216 status = "Don't load" 217 end 218 219 lines[#lines + 1] = "Status: " .. status 220 end 221 222 pager.open() 223 for _, v in ipairs(lines) do 224 pager.output(v .. "\n") 225 end 226 pager.close() 227end 228 229cli["disable-device"] = function(...) 230 local _, argv = cli.arguments(...) 231 local d, u 232 233 if #argv == 0 then 234 print("usage error: disable-device device") 235 return 236 end 237 238 d, u = string.match(argv[1], "(%w*%a)(%d+)") 239 if d ~= nil then 240 loader.setenv("hint." .. d .. "." .. u .. ".disabled", "1") 241 end 242end 243 244-- Used for splitting cli varargs into cmd_name and the rest of argv 245function cli.arguments(...) 246 local argv = {...} 247 local cmd_name 248 cmd_name, argv = core.popFrontTable(argv) 249 return cmd_name, argv 250end 251 252return cli 253