Proof of Concept for a "Multi" Installer for FreeBSD

Posted on March 18, 2025

This is another post in the series: “A Personal Multi Installer in Lua”.

This post describes the current Work in Progress with screenshots and previews. Please refer to the previous post for more background information.


The idea is that a client creates an installation file, and a server performs the installation. A proof of feasibility is available:

  • installer.lua reads the configuration file and builds the start menu.
  • installer.lua calls client.lua.
  • client.lua uses the configuration file and the selected frontend (currently TUI and CLI) to create the installation file.
  • installer.lua calls server.lua
  • server.lua reads the installation file and actually performs the installation on the machine.

Diagram Image: A flowchart depicting the structure of a Lua-based application. At the top, there is a configuration file labeled “Configuration File” connected to an “installer.lua” block. Two branches extend from the installer.lua block: one leading to “client.lua” and the other to “server.lua.” Below, an “Installation File” block connects to “frontend.lua,” which in turn connects to a block labeled “UI implementation” with subcategories “tui - cli - gui - web”

The installer is independent of any graphical interface. Both the client and the server use a generic “Frontend” object, and they are unaware of its specific implementation. The installer can also run without a user interface:

  • The client can create the installation file by reading the default values from the configuration file or through auto installation.
  • The server can execute the installation commands without a user interface.

That is a non-interactive installation. The installer provides several options for interactive, semi-interactive, and non-interactive installations, which will be described later in this post.

The installation file acts as the communication file between the client and server. Additionally, it offers two useful features:

  1. It can be reused.
  2. It can function as a standalone installer.

More details will follow.


The installer skeleton is complete and serves as a proof of concept. It meets most of the requirements and features outlined in the first post: multi-language, multi-frontend, multi-way, and multi-interactivity.

Configuration File

The installer starts by reading the configuration file, which allows you to fully customize the installer settings. It also includes options from bsdinstall. Here is a snippet:

------------------------------------------------------------
--- Starting configuration, you can set up the installer ---

-- Clone bsdinstall OSNAME
Config.OSNAME = "FreeBSD"

-- Clone bsdinstall EFI_LABEL_NAME
Config.EFI_LABEL_NAME = "FreeBSD"

-- Logging and debug
Config.LOGGING_FILE = "/tmp/install.log"
Config.LOGGING_LEVEL = 3

-- Start menu beep: at start up and after restart (accessibility)
-- Config.BEEP = true

-- Client creates Config.INSTALLATION from this configuration file and the
-- user interface. The server reads it to perform the installation on the
-- machine.
-- If Config.CLIENT=false this should be an existing file for the server.
Config.INSTALLATION = "/tmp/installation.lua"

-- Enable client. If disabled the installer will run only the server, so be sure
-- Config.INSTALLATION already exists.
Config.CLIENT = true

-- Enable server
Config.SERVER = true

-- Directory to store "etc" files during server installation,
-- clone bsdinstall BSDINSTALL_TMPETC
Config.TMP_ETC = "/tmp"

.....

--------------------------------------------------------------------------------
-- Screens to show to the user. It is possible to set default values ​​for some
-- screens. If the screen is disabled but the default value is set, it will be
-- written in the INSTALLATION file.

-- Start Menu
Config.SCREEN_STARTMENU = true

-- Hostname
Config.SCREEN_HOSTNAME = true
Config.SCREEN_HOSTNAME_VALUE = HOSTNAME

.....

Then, the installer builds the Start Menu. Three beeps sound to inform blind and low-vision users that they can start the installation (more on this later).

The window displays a menu of installation options, including ‘Language,’ ‘Beep,’ ‘BSDInstall,’ and ‘Advanced.’ The ‘Language’ option is highlighted, showing ‘English’ selected. Buttons at the bottom read ‘Select,’ ‘Shutdown,’ and ‘Reboot’


Expert Menu

The Expert Menu (Start Menu > Advanced --->) offers various features. For example”

The window displays a menu of advanced installation options, including ‘File Install,’ ‘Live System,’ ‘Logging,’ ‘Verbose,’ and ‘Exit.’ The ‘File Install’ option is highlighted, with ‘Installation reading a file’ displayed to the right. A ‘Select’ button is at the bottom of the window.

“File Installation” allows you to select an installation file. The file can be manually created, generated from a previous installation, or generated from the configuration file. If the file exists, the installer skips the client step and directly starts the server. Of course the client can be disabled via the configuration file, to run a semi- or non-interactive installation by reading an installation file.

File Installation Image: A terminal window running the FreeBSD 15.0-CURRENT installation. A pop-up dialog labeled “File Installation” prompts the user to specify an installation file. The input field contains the path “/etc/installer.lua.” Below, there are “OK” and “Cancel” buttons, with “OK” highlighted.

“Logging” lets you choose a debugging/logging level.

The window displays logging options with radio buttons: ‘Disable No logging,’ ‘Level 1 Logging for sysadmins,’ ‘Level 2 Debug for developers,’ and ‘Level 3 Extra debug for the installer.’ The ‘Level 3’ option is selected with an asterisk. Buttons at the bottom read ‘Select’ and ‘Cancel’

Just a log snippet:

====================================================
= Starting Installer Sat Mar 15 14:17:49 2025
====================================================
DEBUG: Loaded configuration file: "config.lua"
DEBUG: load "./i18n.lua"
DEBUG: load "./configure.lua"
DEBUG: load "./startmenu.lua"
DEBUG: load "./client/client.lua"
DEBUG: load "./userinterface/frontend.lua"
DEBUG: load "./client/hostname.lua"
DEBUG: load "./server.lua"
DEBUG: load "./userinterface/cli.lua"
DEBUG: call ./userinterface/frontend.lua:16 new()
	(arg) self = object table: 0xad707088880
	(arg) o = nil
DEBUG: load "./userinterface/gui.lua"
DEBUG: load "./userinterface/tui.lua"
DEBUG: call ./userinterface/frontend.lua:16 new()
	(arg) self = object table: 0xad707088880
	(arg) o = {
		__tostring = "TUI",
	}
DEBUG: load "./userinterface/web.lua"
DEBUG: call ./configure.lua:17 newInstallationTable()
DEBUG: call ./userinterface/tui.lua:146 new()
	(arg) self = object Frontend
DEBUG: call ./startmenu.lua:171 run()

.....

====================================================
= Start Client
====================================================
DEBUG: call ./client/client.lua:49 loop()
	(arg) ui = {
		__tostring = "TUI",
		new = function,
		menu = function,
		radiolist = function,
		inputString = function,
		hostname = function,
		highContrast = function,
		message = function,
		question = function,
	}
.....

====================================================
= Start Server
====================================================

...

Reading Installation file "installation.lua"
DEBUG: load "installation.lua"
Read Installation file "installation.lua"
DEBUG: call ./server.lua:8 shell()
	(arg) cmd = "BSDINSTALL_TMPETC=/tmp HOSTNAME=laptop.bsd
		./bsdinstall/hostname"
----- Command ----
BSDINSTALL_TMPETC=/tmp HOSTNAME=laptop.bsd ./bsdinstall/hostname
	1>/tmp/lua_u0OmXk 2>/tmp/lua_QeM98G
rv: 0
out: err: 
-----------------

.....

---------------- Dialog -------------------------
bsddialog --backtitle 'Installing FreeBSD 15.0-CURRENT'
--title ' Complete ' --no-label 'Shell'
--ok-label 'Reboot' --extra-button --extra-label 'Shutdown'
--yesno 'Installation Complete. Thank you to use FreeBSD!'
0 0  2>/tmp/lua_VZ6R
Button: Shutdown (dialog rv:3)
stderr: 
------------------------------------------------
====================================================
= End Installer Sat Mar 15 14:18:19 2025
====================================================

Returning to the Start Menu.

The window displays a menu of installation options, including ‘Language,’ ‘Beep,’ ‘BSDInstall,’ and ‘Advanced.’ The ‘Language’ option is highlighted, showing ‘English’ selected. Buttons at the bottom read ‘Select,’ ‘Shutdown,’ and ‘Reboot.’

You can switch to “Dark Theme” for improved visibility.

Previous start menu image in black and white

The installer supports multiple frontends and can seamlessly switch to CLI mode using a simple key combination (“S + Enter”) to ensure screen reader compatibility. Special thanks to the FreeBSD Foundation for making this possible. It’s important to note that this is currently just a draft and will be improved further.

The i18n (internationalization) module is implemented, allowing language selection through the “Language” option.

Screenshot of the FreeBSD 15.0 installer ‘Languages’ menu, with ‘Italiano (Italian)’ highlighted, reflecting the user’s location in Italy. A light-colored window labeled ‘Languages’ is centered on a blue background. The window displays a list of languages: ‘Française (French),’ ‘Italiano (Italian),’ and ‘English.’ The ‘Italiano’ option is highlighted. Buttons at the bottom read ‘Select’ and ‘Cancel’

If a translation is unavailable, the default English text is used. To note “Beep” “Disabled”.

Main Menu in Italian


Client Loop

The client provides its features in a loop, so it is easy to implement:

  • Multi-way installation
  • Navigation buttons

After selecting “Install”, choose between Auto, Simple, or Expert installation modes.

The window displays three installation mode options: ‘Auto,’ ‘Simple,’ and ‘Expert,’ each with a radio button. The ‘Simple’ mode option is selected with an asterisk. Buttons at the bottom read ‘Prev,’ ‘Restart,’ and ‘Next’

Each screen includes navigation buttons: [Prev], [Restart], and [Next].

The window displays a text field with ‘laptop.bsd’ entered as the hostname. Buttons at the bottom read ‘Prev,’ ‘Restart,’ and ‘Next’

After selecting [Restart], you can choose between Soft, Hard, or Reboot.

The window displays options for restarting the installation: ‘Soft’ (Preserve your configurations), ‘Hard’ (Delete your configurations), and ‘Reboot’ (Reboot the computer). The ‘Soft’ option, ‘Preserve your configurations,’ is highlighted. Buttons at the bottom read ‘Select’ and ‘Cancel’

New guideline: each screen has to provide an help (F1 key).

The window contains text explaining that a hostname is the computer’s name for networking and suggests using a made-up name like ‘yourname.fbsd’ for home networks. An ‘EXIT’ button is at the bottom of the window


Server

The server prompts for confirmation before reading the configuration file and proceeding with the OS installation.

The window displays the question, ‘Do you want really install FreeBSD on this machine?’ Buttons at the bottom read ‘Yes’ and ‘No’

If an error occurs, it displays the failed command along with the return value, stdout, and stderr.

Error Message Image: A terminal window running the FreeBSD 15.0-CURRENT installation. A pop-up error message shows a command labeled “aaa” with the result value “127” and an output message that says “Error: sh: aaa: not found.” The pop-up has a “Close” button highlighted.

In verbose mode, the executed command is also displayed; please see Expert Menu above.

The window displays the command and its output: ‘BSDINSTALL_TMPETC=/tmp HOSTNAME=fbsd.mtc ./bsdinstall/hostname’, ‘Result value: 0’, ‘Output:’, and ‘Error:’. A ‘Close’ button is at the bottom of the window


Multi interactive

The configuration file offers extensive flexibility to customize user interactivity levels: interactive, semi-interactive, and non-interactive.

The setup is the combination of Start Menu (Enable/Disabled) + Client UI (Enabled/Disabled (also for each screen)) + Client (Enabled/Disabled) + Server UI and Verbose (Enabled/Disabled).

Fully Interactive (user driven) Installation example:

  • Start Menu enabled + client enabled + client UI enabled + server UI and Verbose enabled

Fully Non-Interactive (no user interaction) Installation examples:

  • Client UI disabled (client generates the installation file from default values or auto mode) + server UI and Verbose disabled
  • Alternatively, if the installation file already exists: the client disabled, the installer directly passes the installation file to the server with UI and Verbose disabled.

Standalone Installer

The installation file is both a client/server communication file and a standalone installer. Here is a snippet:

--- Installation file created on Sat Mar 15 00:25:54 2025 ---
	
-- Uncomment the following line to use this file like a standalone installer.
-- Example, run: '# lua /root/installation.lua'.
-- local standaloneInstaller = true

local installation = {
	version = "15.0-CURRENT",
	[1] = {
		label = "hostname",
		command = "BSDINSTALL_TMPETC=/tmp HOSTNAME=fbsd.mtc ./bsdinstall/hostname"
		}, -- End hostname
	[2] = {
		label = "date",
		command = {
.....

} -- End Installation

local function install()
	for _, step in ipairs(installation) do
		if type(step.command) == "table" then
			for _, c in ipairs(step.command) do
				os.execute(c)
			end
		else
			os.execute(step.command)
		end
	end
end

if standaloneInstaller then install() end
.....

To use the file as a standalone installer, uncomment the line:

-- Uncomment the following line to use this file like a standalone installer.
-- Example, run: '# lua /root/installation.lua'.
local standaloneInstaller = true

And execute:

# lua /root/installation.lua

WIP and Future

Currently finishing reading Programming in Lua, 4th Edition.

Integrating bsdinstall components and adding progress bars to the server:

The window displays progress bars for various installation steps: ‘Date e time’ 100%, ‘Network’ 100%, ‘Partitioner’ 100%, ‘Fetching System’ 30%, ‘Installing System’ 0%, and ‘Config Files’ 0%. The ‘Fetching System’ progress bar is partially filled. The text ‘pkgbase fetching base system…’ is displayed, and an ‘Overall Progress’ bar shows 30%

Currently, server.lua simply loads the installation file and executes its commands, adding only logging and a user interface. In pseudocode:

--Server.lua

installation = load("installation file")

for step in (installation)
	execute(step.command)
	log(step.namen step.command)
	UI.window(step.name, step.comman)
end

I am considering merging server.lua with the installation file so it becames a simple sequence of commands.

The server uses a lightweight version of the bsdinstall components, without a user interface and logging. For example, the hostname script becomes:

# Default file to store hostname entry in
: ${HOSTNAMEFILE:=$BSDINSTALL_TMPETC/rc.conf.hostname}

# Store the user's choice
echo hostname=\"$HOSTNAME\" > $HOSTNAMEFILE
retval=$?

# Activate entry if configured
if [ "$BSDINSTALL_CONFIGCURRENT" ]; then
	hostname -s $HOSTNAME
	retval=$?
fi

exit $retval

I am also considering rewriting the shell components in Lua.

Adding a final step to install a desktop environment, ports, and packages for accessibility. For example:

Menu in a terminal to select a GPU driver

Menu in a terminal to select software to install

Window with a bar to display the installation progress

I am considering to integrate this project to provide the web frontend.

Finally, I am working on improvements and extensions for the partitioner. Several feature requests exist, primarily on Bugzilla.