git: 3c3dd6296612 - main - bsdinstall: add pkgbase component selection dialog

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Thu, 22 May 2025 19:02:48 UTC
The branch main has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=3c3dd62966123b424657983d9a4d173f675ad8c7

commit 3c3dd62966123b424657983d9a4d173f675ad8c7
Author:     Isaac Freund <ifreund@freebsdfoundation.org>
AuthorDate: 2025-05-05 17:44:20 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2025-05-22 18:59:47 +0000

    bsdinstall: add pkgbase component selection dialog
    
    This matches the style of the component selection dialog for traditional
    tarball-based installations. The only difference is that there is
    currently no ports component offered when using pkgbase.
    
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D50178
---
 usr.sbin/bsdinstall/scripts/pkgbase.in | 132 +++++++++++++++++++++++++++++----
 1 file changed, 118 insertions(+), 14 deletions(-)

diff --git a/usr.sbin/bsdinstall/scripts/pkgbase.in b/usr.sbin/bsdinstall/scripts/pkgbase.in
index 7faeee40647b..eaed23a19548 100755
--- a/usr.sbin/bsdinstall/scripts/pkgbase.in
+++ b/usr.sbin/bsdinstall/scripts/pkgbase.in
@@ -7,6 +7,9 @@
 -- This software was developed by Isaac Freund <ifreund@freebsdfoundation.org>
 -- under sponsorship from the FreeBSD Foundation.
 
+local sys_wait = require("posix.sys.wait")
+local unistd = require("posix.unistd")
+
 local all_libcompats <const> = "%%_ALL_libcompats%%"
 
 -- Run a command using the OS shell and capture the stdout
@@ -38,20 +41,122 @@ local function append_list(list, other)
 	end
 end
 
--- Returns a list of pkgbase packages equivalent to the default base.txz and kernel.txz
+-- Read from the given fd until EOF
+-- Returns all the data read as a single string
+local function read_all(fd)
+	local ret = ""
+	repeat
+		local buffer = assert(unistd.read(fd, 1024))
+		ret = ret .. buffer
+	until buffer == ""
+	return ret
+end
+
+-- Run bsddialog with the given argument list
+-- Returns the exit code and stderr output of bsddialog
+local function bsddialog(args)
+	local r, w = assert(unistd.pipe())
+
+	local pid = assert(unistd.fork())
+	if pid == 0 then
+		assert(unistd.close(r))
+		assert(unistd.dup2(w, 2))
+		assert(unistd.execp("bsddialog", args))
+		unistd._exit()
+	end
+	assert(unistd.close(w))
+
+	local output = read_all(r)
+	assert(unistd.close(r))
+
+	local _, _, exit_code = assert(sys_wait.wait(pid))
+	return exit_code, output
+end
+
+-- Creates a dialog for component selection mirroring the
+-- traditional tarball component selection dialog.
+local function select_components(components, options)
+	local descriptions = {
+		kernel_dbg = "Kernel debug info",
+		base_dbg = "Base system debug info",
+		src = "System source tree",
+		tests = "Test suite",
+		lib32 = "32-bit compatibility libraries",
+		lib32_dbg = "32-bit compatibility libraries debug info",
+	}
+	local defaults = {
+		kernel_dbg = "on",
+		base_dbg = "off",
+		src = "off",
+		tests = "off",
+		lib32 = "on",
+		lib32_dbg = "off",
+	}
+
+	-- Sorting the components is necessary to ensure that the ordering is
+	-- consistent in the UI.
+	local sorted_components = {}
+	for component, _ in pairs(components) do
+		table.insert(sorted_components, component)
+	end
+	table.sort(sorted_components)
+
+	local checklist_items = {}
+	for _, component in ipairs(sorted_components) do
+		if component ~= "base" and component ~= "kernel" and
+		    not (component == "kernel_dbg" and options.no_kernel) and
+		    #components[component] > 0 then
+			local description = descriptions[component] or "''"
+			local default = defaults[component]  or "off"
+			table.insert(checklist_items, component)
+			table.insert(checklist_items, description)
+			table.insert(checklist_items, default)
+		end
+	end
+
+	local bsddialog_args = {
+		"--backtitle", "FreeBSD Installer",
+		"--title", "Select System Components",
+		"--nocancel",
+		"--disable-esc",
+		"--separate-output",
+		"--checklist", "Choose optional system components to install:",
+		"0", "0", "0", -- autosize
+	}
+	append_list(bsddialog_args, checklist_items)
+
+	local exit_code, output = bsddialog(bsddialog_args)
+	-- This should only be possible if bsddialog is killed by a signal
+	-- or buggy, we disable the cancel option and esc key.
+	-- If this does happen, there's not much we can do except exit with a
+	-- hopefully useful stack trace.
+	assert(exit_code == 0)
+
+	local selected = {"base"}
+	if not options.no_kernel then
+		table.insert(selected, "kernel")
+	end
+	for component in output:gmatch("[^\n]+") do
+		table.insert(selected, component)
+	end
+
+	return selected
+end
+
+-- Returns a list of pkgbase packages selected by the user
 local function select_packages(pkg, options)
 	local components = {
-		["kernel"] = {},
-		["kernel-dbg"] = {},
-		["base"] = {},
-		["base-dbg"] = {},
-		["src"] = {},
-		["tests"] = {},
+		kernel = {},
+		kernel_dbg = {},
+		base = {},
+		base_dbg = {},
+		src = {},
+		tests = {},
 	}
 
 	for compat in all_libcompats:gmatch("%S+") do
 		components["lib" .. compat] = {}
-		components["lib" .. compat .. "-dbg"] = {}
+		components["lib" .. compat .. "_dbg"] = {}
 	end
 
 	local rquery = capture(pkg .. "rquery -U -r FreeBSD-base %n")
@@ -65,15 +170,15 @@ local function select_packages(pkg, options)
 			if package == "FreeBSD-kernel-generic" then
 				table.insert(components["kernel"], package)
 			elseif package == "FreeBSD-kernel-generic-dbg" then
-				table.insert(components["kernel-dbg"], package)
+				table.insert(components["kernel_dbg"], package)
 			end
 		elseif package:match(".*%-dbg$") then
-			table.insert(components["base-dbg"], package)
+			table.insert(components["base_dbg"], package)
 		else
 			local found = false
 			for compat in all_libcompats:gmatch("%S+") do
 				if package:match(".*%-dbg%-lib" .. compat .. "$") then
-					table.insert(components["lib" .. compat .. "-dbg"], package)
+					table.insert(components["lib" .. compat .. "_dbg"], package)
 					found = true
 					break
 				elseif package:match(".*%-lib" .. compat .. "$") then
@@ -94,9 +199,8 @@ local function select_packages(pkg, options)
 	assert(#components["base"] > 0)
 
 	local selected = {}
-	append_list(selected, components["base"])
-	if not options.no_kernel then
-		append_list(selected, components["kernel"])
+	for _, component in ipairs(select_components(components, options)) do
+		append_list(selected, components[component])
 	end
 
 	return selected