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['be-list'] = function(...) 164 local _, argv = cli.arguments(...) 165 if #argv ~= 0 then 166 print("usage error: be-list") 167 return 168 end 169 170 for _, bootenv in core.bootenvIter() do 171 print(bootenv) 172 end 173end 174 175cli['be-switch'] = function(...) 176 local _, argv = cli.arguments(...) 177 if #argv == 0 then 178 print("usage error: be-switch beName") 179 return 180 end 181 182 local env = argv[1] 183 core.switchBE(env) 184end 185 186cli["toggle-module"] = function(...) 187 local _, argv = cli.arguments(...) 188 if #argv == 0 then 189 print("usage error: toggle-module module") 190 return 191 end 192 193 local module = argv[1] 194 setModule(module, not config.isModuleEnabled(module)) 195end 196 197cli["show-module-options"] = function() 198 local module_info = config.getModuleInfo() 199 local modules = module_info['modules'] 200 local blacklist = module_info['blacklist'] 201 local lines = {} 202 203 for module, info in pairs(modules) do 204 if #lines > 0 then 205 lines[#lines + 1] = "" 206 end 207 208 lines[#lines + 1] = "Name: " .. module 209 if info.name then 210 lines[#lines + 1] = "Path: " .. info.name 211 end 212 213 if info.type then 214 lines[#lines + 1] = "Type: " .. info.type 215 end 216 217 if info.flags then 218 lines[#lines + 1] = "Flags: " .. info.flags 219 end 220 221 if info.before then 222 lines[#lines + 1] = "Before load: " .. info.before 223 end 224 225 if info.after then 226 lines[#lines + 1] = "After load: " .. info.after 227 end 228 229 if info.error then 230 lines[#lines + 1] = "Error: " .. info.error 231 end 232 233 local status 234 if blacklist[module] and not info.force then 235 status = "Blacklisted" 236 elseif info.load == "YES" then 237 status = "Load" 238 else 239 status = "Don't load" 240 end 241 242 lines[#lines + 1] = "Status: " .. status 243 end 244 245 pager.open() 246 for _, v in ipairs(lines) do 247 pager.output(v .. "\n") 248 end 249 pager.close() 250end 251 252cli["disable-device"] = function(...) 253 local _, argv = cli.arguments(...) 254 local d, u 255 256 if #argv == 0 then 257 print("usage error: disable-device device") 258 return 259 end 260 261 if #argv > 1 then 262 print("Too many arguments") 263 print("usage error: disable-device device") 264 return 265 end 266 267 d, u = string.match(argv[1], "(%w*%a)(%d+)") 268 if d ~= nil and u ~= nil then 269 loader.setenv("hint." .. d .. "." .. u .. ".disabled", "1") 270 else 271 print("Cannot parse " .. argv[1] .." into driver and unit number.") 272 end 273end 274 275-- Used for splitting cli varargs into cmd_name and the rest of argv 276function cli.arguments(...) 277 local argv = {...} 278 local cmd_name 279 cmd_name, argv = core.popFrontTable(argv) 280 return cmd_name, argv 281end 282 283return cli 284