In the common case, this will effectively do nothing as the menu will get
redrawn as we leave submenus regardless of whether the screen has been
marked invalid or not
However, upon escape to the loader prompt, one could do either of the
following to re-enter the menu system:
-- Method 1
require('menu').run()
-- Method 2
require('menu').process(menu.default)
With method 1, the menu will get redrawn anyways as we do this before
autoboot checking upon entry. With method 2, however, the menu will not be
redrawn without this invalidation.
Both methods are acceptable for re-entering the menu system, although the
latter method in the local module for processing new and interesting menus
is more expected.
cli_execute is likely the only exception that we should make, due to it
being a global. We don't really need other globals, so this won't really end
up an epidemic.
There's no reason for autoboot handling to be mixed in with menu processing.
It is a distinct process that should only be done once when entering the
menu system.
menu.process has been modified to take an initial keypress to process and to
only draw the screen initially if it's been invalidated. The keypress is
kind of a kludge, although it could be argued to be a potentially useful
kludge if there are other processes that may need to feed a keypress into
the menu system.
In general, every menu redraw is going to require a screen clear and cursor
reset. Each redraw also has the potential to invalidate the alias table, so
we move the alias table being used out into a module variable. This allows
third party consumers to also inspect or update the alias table if they need
to.
While here, stop searching the alias table once we've found a match.
This is driven by an urge to separate out the bits that really only need to
happen when the menu system starts up. Key points:
- menu.process now does the bulk of menu handling. It retains autoboot
handling for dubious reasons, and it no longer accepts a 'nil' menu to
process as 'the default'. Its return value is insignificant.
- The MENU_SUBMENU handler now returns nothing. If menu.process has exited,
then we continue processing menu items on the parent menu as expected.
- menu.run is now the entry point of the menu system. It checks whether the
menu should be skipped, processes the default menu, then returns.
These indices were assigned the same values as they would've been implicitly
assigned anyways.
While here, throw terminating commas after the last value of tables.
It should use the common parser, but it should not be processed like a
standard file. Rewite check_nextboot to read the file in, check whether it
should continue, then parse as needed.
This allows us to throw the recently introduced check_and_halt callback
swiftly out the window.
config.parse is now purely a parser, rather than a whole proccessor. The
standard process for loading a config file has been split out into
config.processFile.
This clears the way for having nextboot read its own config file and decide
there whether it should parse the rest of the file.
This is step 1 towards revoking config.parse of it I/O privileges. Ideally,
all reading would be done before config.parse and config.parse would just
take text and parse it rather than being charged with the entire process.
Functionally, the latter error wouldn't necessarily hurt anything. io.write
will just error out as it's not passed a valid file handle. Still, we can do
better than that.
The functionality was correct, but our style guidelines tend to request that
we shy away from using boolean operations in place of explicit comparisons
to nil.
config.parse now takes an extra callback that is invoked on the full text of
the config file. This callback dictates where we should actually try to
parse this file or not.
For nextboot, we use this to halt parsing if we see 'nextboot_enable="NO"'.
If we don't, parse it and write 'nextboot_enable="NO" ' to it. The same
caveat as with forth still applies- writing is only supported by UFS.
The latter is good, but the former is more elegant and clear about what 'x'
is. Adopt it, preferably only using the latter kind of notation where needed
as values for tables.
I've also made some not-insignificant changes/additions to this file, to
include the added constants, ACPI changes, boot environment listing, and
some utility functions.
Graphics have a tendency to cause 80-col issues, so make an exception to our
standard indentation guidelines for these graphics. This does not hamper
readability too badly.
Two 40-column strings of spaces is trivially replaced with
string.rep(" ", 80)
luacheck pointed out an assortment of issues, ranging from non-standard
globals being created as well as unused parameters, variables, and redundant
assignments.
Using '_' as a placeholder for values unused (whether it be parameters
unused or return values unused, assuming multiple return values) feels clean
and gets the point across, so I've adopted it. It also helps flag candidates
for cleanup later in some of the lambdas I've created, giving me an easy way
to re-evaluate later if we're still not using some of these features.
Instead of the global namespace, let's attach these to the cli module. Other
users, including the "local" module, can attach functions to the cli module
at will to add other cli commands and things will still Just Work.
This distills down the candidates for functions that may be invoked via the
cli to a minimal set (boot, autoboot, arguments), rather than any function
that happens to live in the global lua namespace.
This will be the translation layer for varargs -> cmd_name, argv for cli
commands. We reserve the right to break exactly what the varargs inclulde,
but this gives us a stable way to pull the arguments out of varargs.
This module will, in the not-so-distant future, grow functionality for
reducing boilerplate in functions that implement cli commands. It will
likely also house most in-tree cli commands.
This seems to have been arbitrary; bootlock_password and password don't seem
to have any documented length restrictions, and loader(8) probably shouldn't
care about whatever GELI passphrase length restrictions might exist.
Reported by: Kalle Carlbark <kalle.carlbark+freebsd@kcbark.net>
Attempt to autoboot when we open the default menu, and only when we open the
default menu. This alleviates the need for checking menu.already_autoboot,
because we're not trying to autoboot every time we open a submenu.
I note that escaping to loader prompt and going back to the menu (by running
require('menu').run() at the loader prompt) will happily work and not
re-initiate the autoboot sequence since "Escape to loader prompt" disables
the autoboot_delay.
Instead of based it off of whether 'kernels' was specified, base it off of a
new variable: kernels_autodetect. If set to yes, we'll run the autodetection
bits and add any detected kernels to the already existing list *after* both
'kernel' and 'kernels'.
This looks a little bit differently than the forth version for the time
being, just to get off the ground- rather than a paging system, it's
implemented as a simple carousel like the kernel selector.
Reviewed by: cem
Differential Revision: https://reviews.freebsd.org/D14436
This matches forth behavior. Hitting "6" when autobooting at the welcome
menu will now take you directly to the "Boot Options" menu.
We likely have some slight optimizations we should make, like not checking
autoboot every time we open a new menu and things of this nature. Further
work will go towards this end.
Allow "name" entries to be simple strings, instead of just functions. We
know whether we support colors or not by the time any of this is setup, so
all menu names that are basically static with colors sprinkled in are good
candidates for simplification.
Also simplify "func" in many cases where it's just invoking another function
with no arguments. The downside to this simplification is that the functions
called can no longer be trivially replaced by a local module. The upside is
that it removes another layer of indirection that we likely don't need.
These can be re-evaluated later if a compelling argument is raised, on a
case-by-case basis, for replacement.
The intent here is to abstract away the name of the default menu. The
default menu is still the welcome menu, but this detail doesn't need to
matter to things outside of the menu module. You may change the default
menu, but one would need to modify a specific menu.
Provide a way for out-of-tree users of lualoader to patch into the loader
system without having to modify our distributed scripts.
Do note that we can't really offer any API compatibility guarantees at this
time due to the evolving nature of lualoader right now.
This still has some utility as local modules may add commands at the loader
prompt without relying heavily on lualoader features- this specific
functionality is less likely to change without more careful consideration.
Reviewed by: cem (earlier version)
Differential Revision: https://reviews.freebsd.org/D14439
I can't find any good reason these aren't enabled, so enable them.
The silent runs will only return false on actual parse errors, so it's ok to
be loud about those failures.
This was also a convenience convention (for me) that is not very lua-tic.
Drop it.
I've maintained some parentheses where I'd prefer them, for example,
'if x or y or (z and w) then', but these situations are far and few between.
This was previously chosen out of convenience, as we had a mixed style and
needed to be consistent. I started learning Lua on Friday, so I switched
everything over. It is not a very lua-nic convention, though, so drop it.
Excessive parenthesizing around conditionals is next on the chopping block.
Track the latest value we've set an environment variable to, and only
restore those that are unchanged from that.
This gives us some leeway to make sure we're not clobbering variables
overwritten by menu changes.
This should be functional and roughly equivalent to the Forth version.
Stop doing a loadelf() on menu exit now that we can DTRT with boot
invocations. autoboot interception will follow not long after.
core.boot and core.autoboot may both take arguments; add a helper to cleanly
append an argstring to the given loader command.
Also provide a popFrontTable() that we'll use pop the command name off of an
argv table. We don't have the table library included, and including it is
non-trivial, so we'll implement this one function that we need in lua for
the time being.
If the user's selected a kernel, we really should be trying to load that one
instead of falling back to some default kernel.
This should generally be a no-op and most desirable, unless you really
enjoyed surprises.
Carousel storage doesn't need to happen in the menu module, and indeed
storing it there introduces a circular reference between drawer and menu
that only works because of global pollution in loader.lua.
Carousel choices generally map to config entries anyways, making it as good
of place as any to store these. Move {get,set}CarouselIndex functionality
out into config so that drawer and menu may both use it. If we had more
carousel functionality, it might make sense to create a carousel module, but
this is not the case.
If we failed to execute the input line as pure lua, run the command through
parse for consistent argument parsing. Pass the parsed arguments through to
a global "cli_execute" written in Lua, which is expected to either handle it
or pass it back through to interp_builtin_cmd (via loader.command).
lua-handled cli commands will then exist as globals in whatever module they
most belong in, and invocations at the loader prompt will magically dispatch
to them if they exist.
Reviewed by: imp
Differential Revision: https://reviews.freebsd.org/D14450
We follow pretty closely the following structure of a module:
1. Copyright notice
2. Module requires
3. Module local declarations
4. Module local definitions
5. Module exports
6. return
Re-organize the one-offs (config/drawer) and denote the start of module
exports with a comment.
Declare these adjacent to the local definitions at the top of the module,
and make sure they're actually declared local to pollute global namespace a
little bit less.
This refactor makes it straightforward to add new logos for drawing and
organizes logo definitions in a logical manner.
The graphic to be drawn for each logo may again be modified outside of
drawer, but it must be done on a case-by-case basis as a modification to the
loader_logo.
As part of an effort to slowly reduce our exports overall to a set of stable
properties/functions, go ahead and reduce what drawer exposes to others.
The graphics should generally not be modified on their own, but their
position could be modified if additional grahics also need to be drawn.
Export position/shift information, but leave the actual graphic local to
the module.
The next step will be to create a 'menudef' that gets exported instead, with
each entry in the menudef table describing the graphic to be drawn along
with specific positioning information.
Pull out specialized naming behavior into drawer.menu_name_handlers. This is
currently only needed for the carousel entry, where naming is based on the
current choice and the menu item purposefully does not store this state.
The setup was designed so that every type can have a special name handler,
and the default action is to simply use the result of entry.name().
This is a bit cleaner than our former method of an if ... else chain of
handlers. Store handlers in the menu.handlers table so that they may be
added to or removed dynamically.
All handlers take the current menu and selected entry as parameters, and
their return value indicates whether the menu processor should continue or
not. An omitted return value or 'true' will indicate that we should
continue, while returning 'false' will indicate that we should exit the
current menu.
The omitted return value behavior is due to continuing the loop being the
more common situation.
Building the swapped welcome menu (first two items swapped) is kind of a
sluggish, because it requires a full (recrusive) shallow copy of the welcome
menu. Cache the result of that and re-use it later, instead of building it
everytime.
While here, don't create temporary locals just for swapping. The following
is just as good:
x, y = y, x;
Reported by: Alexander Nasonov <alnsn@yandex.ru> (swapping)
[Enter] should be moved to the single user menu item when we swap them.
Define a non-standard menu entry function "alternate_name" to use for this
purpose for ultimate flexibility if we change our minds later. When we're
booting single user, make a shallow copy of the menu that we'd normally
display and swap the items and their name functions to use alternate_name
instead. Toggling single user in the options menu and going back to the main
menu will now correctly reflect the current boot setting with the first two
menu options and "[Enter]" will always be on the right one.
This shallow copy technique has the chance of being quite slow since it's
done on every redraw, but in my testing it does not seem to make any obvious
difference.
shallowCopyTable could likely belong better in a general-purpose utility
module, but this (and the key constnats) are the only candidates we have at
the moment so we'll drop it into our core stuff for the moment and consider
re-organization at a later date.
lualoader does a pretty good job of reverting any environment changes now.
It will even wipe out boot_verbose if it's set explicitly in loader.conf(5)
and overwritten in the boot options menu.
Future work will likely change this, as explicit choices made in the menu
should probably override the new loader.conf(5). I don't suspect this will
cause much grief, though, so it is not a high priority until boot
environment support actually lands.
This will be used when boot environment support lands to make a good-faith
effort to apply any new loader.conf(5) environment settings atop the default
configuration that we started with.
If we've fetched menu.entries and it turns out it's a function, call it to
get the actual menu entries.
This will be used to swap multi-/single- user boot options if we're booting
single user by default (boot_single="YES" in loader.conf(5)). It can also be
used fairly easily for other non-standard situations.
Instead of directly listing them in menu.welcome and menu.boot_options,
store them at menu.welcome.entries and welcome.boot_options.entries.
This will come into play later when we need to re-order the welcome menu if
boot_single is specified.
Menus are actually defined as entries in the 'menu' table. These local
declarations have not been used in the history of our in-tree lua scripts,
so give them the boot.
Loading the kernel and modules can be really slow. Loading before the menu
draws and every time one changes kernel/boot environment is even more
painful.
Defer loading until we either boot, auto-boot, or escape to loader prompt.
We still need to deal with configuration changes as the boot environment
changes, but this is generally much quicker.
This commit strips all ELF loading out of config.load/config.reload so that
these are purely for configuration. config.loadelf has been created to deal
with kernel/module loads. Unloading logic has been ripped out, as we won't
need to deal with it in the menu anymore.
Discussed in part with: allanjude
r329550 introduced config.kernel_loaded. config.load() doesn't provide a
means of overriding the kernel to load, but that likely isn't necessary as
it will not be a common case. When loading the kernel, just attempt to load
the kernel previously loaded and specified in config.kernel_loaded.
If we haven't loaded a kernel yet, config.kernel_loaded will be unset/nil
and the "default"/first kernel found will be loaded. If we've loaded a
kernel, we'll try to load that same kernel again and fallback to the default
kernel if we need to.
This in also in support of upcoming boot environment support.
'nil' means the 'first kernel found in module_path', which is the same
interpretation as passing 'nil' to loadkernel.
Otherwise, it denotes the name of a kernel that we've successfully loaded.
When reloaded later, we will still need to do the full search again to
locate the actual kernel in case things have changed, so just the name is
good enough.
This is in support of upcoming boot environment support. vfs.root.mountfrom
and currdev will be changed, then we will reload configuration and attempt
to reload the currently chosen kernel unless we shouldn't.
In the worst case scenario, we have no passwords to prompt for and we end up
just clearing the screen twice before we draw the menu or proceed with boot.
In the best case scenario, we don't try drawing password prompts amidst a
bunch of kernel/module loading.
Some other points I think we need to be consistent on:
- Spacing around string concatenation (always)
- Test against 'nil' explicitly rather than relying on 'not' for things that
reasonably won't be returning a boolean. e.g. loader.getenv
Eventually this will all get formalized somewhere.
Once we've successfully loaded a kernel, we add its directory to
module_path. If we switch kernels with the kernel selector, we again prepend
the kernel directory to the current module_path and end up with multiple
kernel paths, potentially with mismatched kernel/modules, added to
module_path.
Fix it by caching module_path at load() time and using the cached version
whenever we load a new kernel.
These are the style points that I'd like to try and maintain in our lua
scripts:
- Parentheses around conditionals
- Trailing semicolons, except on block terminators
- s:method(...) instead of string.method(s, ...) where applicable
There's likely more, but that'll get hammered out as we continue.
Prompt for GELI passphrase when geom_eli_passphrase_prompt has been set to
"YES" in loader.conf(5).
This entailed breaking out the password prompt into its own function that
can be reused between the password compare bits and this prompt that simply
takes the entered password and passes it along in the environment as
kern.geom.eli.passphrase.
I've also added a TODO to re-evaluate later if we want the "password
masking" -- it is currently not functional, so one still can't observe the
length of the password typed at the prompt.
This is the procedure that config.loadkernel tries to go through, but
reloading kernel config didn't use this function. Amend config.loadkernel to
take an optional other_kernel.
While here, be a little more verbose ("Trying to load kernel") so that it's
easy to follow where we've gone wrong.
1.) Instead of string.function(s, ...), use s:function(...)
2.) Don't try to concatenate `res`, it was just tested to be nil
3.) Note that "Loading configuration" is configured modules, and be a little
more precise in mentioning what failed ("loading of one or more modules")
An empty module_path to start with isn't ideal, but if all modules are
contained within a kernel directory (which is what we just tested) then it
isn't strictly an error. Don't assume that module_path has a value already.
When we fail to load the kernel, printing the result (which is guaranteed to
be nil) is not intended; print the name of the kernel.
autoboot_delay=NO is documented to wait for input and *not* autoboot, which
is the exact opposite of the current behavior.
Additionally, autoboot_delay=-1 is documented to disallow the user from
interrupting the boot (i.e. autoboot immediately), which was not previously
honored.
This also fixes the case insensitive comparison to be truly case
insensitive. This is kind of nit-picky, but the previous version would only
accept "no" and "NO".
Don't move this into config.reload because we may want to force reloads if
/boot changes out from under us later.
As a caution: changing kernels in lualoader at the moment might not be
loading all of your modules (in my testing, at least) from loader.conf(5).
This is a known problem.
Decimals screw up the escape sequence and the cursor will not get set. Right
now this only affects setting the cursor for drawing "Welcome to FreeBSD" --
the resulting number after our (x+(w/2)-9) calculation gets output as
"14.0."
This should be fixed at the interpreter level, rather than here, but this is
not a widespread problem at the moment so we'll fix it up in further work.
Reported by: David Wolfskill <david@catwhisker.org>
Reviewed by: imp
Differential Revision: https://reviews.freebsd.org/D14375
This is a pre-cursor to boot environment support in lualoader. Create a new
menu item type, "carousel_entry", that generally provides a callback to get
the list of items, a carousel_id for storing the current value, and the
standard name/func functions that an entry has.
The difference between this and a normal menu item, functionally, is that
selecting a carousel item will automatically rotate through available items
and wrap back at the beginning when the list is exhausted.
The 'name' function takes the choice index, current choice, and the list of
choices as parameters so that the menu item can decorate the name freely as
desired.
The 'func' function takes the current choice as a parameter, so it can act
accordingly.
The kernel menu item has been rewritten to use the carousel_entry type as
both an example and initial test of its functionality before it is used for
boot environment options.
Noting that we're in lualoader is nice, but it's not a difference we raelly
need to expose to Fred. Re-word it to match the 4th wording and reduce
differences.
This removes a redundant alias that has since been converted into a global
alias. It was converted to a global alias before to ensure that we always
have a way to go up one level in the menu.
Set it based on hint.acpi.0.rsdp. Initially, hint.acpi.0.disabled will be
respected. "Using System Defaults" will override whether it's explicitly
disabled by hint and re-load it based on whether it's present on the system.
Unlike the 4th version, this is not restricted to x86. I have no strong
reasoning for this, so this is definitely open to change.
This submenu is likely going to go away in favor of kernel selection as it
is done in forth at the moment, but for the time being don't descend into it
if we have no kernels available for listing.
These are the .lua files from from Pedro Souza's 2014 Summer of Code
project. Rui Paulo, Pedro Arthur and Wojciech A. Koszek also
contributed.
Obtained from: https://wiki.freebsd.org/SummerOfCode2014/LuaLoader
Sponsored by: Google Summer of Code
Improve the SoC lua menu code to bring it in line with forth
menu functionality
Submitted by: Zakary Nafziger
Sponsored by: FreeBSD Foundation
Use loader.setenv and loader.unsetenv instead of loader.perform
Convert from include("/boot/foo.lua") to foo = require("foo");
to bring in line with latest lua module conventions.
Enforce a uniform style for the new .lua files:
o hard tab indenation for 8 spaces
o don't have if foo then bar; else bas; end on one line
MFC After: 1 month
Relnotes: yes
Differential Review: https://reviews.freebsd.org/D14295