From ef0ab8ded0d283ee3226040b165d6fe059b6e1b7 Mon Sep 17 00:00:00 2001 From: Lucas Holt Date: Sat, 5 Sep 2026 11:51:15 -0400 Subject: [PATCH] release: add mport packages to DVD installs Stage a verified package dependency closure and the full blessed mport index on DVD images. Add a bsdinstall package menu that installs curated or searched packages into the target with mport and seeds its repository index. AI-Assisted-by: OpenAI Codex (GPT-5) Signed-off-by: Lucas Holt --- release/Makefile | 15 +- release/packages.md | 99 +++++++++++++ release/release.conf.sample | 11 +- release/release.sh | 39 +++-- release/scripts/pkg-stage.sh | 204 ++++++++++++++++----------- release/tools/gce.conf | 2 +- usr.sbin/bsdinstall/bsdinstall.8 | 10 ++ usr.sbin/bsdinstall/scripts/Makefile | 2 +- usr.sbin/bsdinstall/scripts/auto | 22 ++- usr.sbin/bsdinstall/scripts/mount | 6 +- usr.sbin/bsdinstall/scripts/packages | 202 ++++++++++++++++++++++++++ usr.sbin/bsdinstall/scripts/umount | 10 +- 12 files changed, 500 insertions(+), 122 deletions(-) create mode 100644 release/packages.md create mode 100755 usr.sbin/bsdinstall/scripts/packages diff --git a/release/Makefile b/release/Makefile index 7f3bea9d644..3bc725164b1 100644 --- a/release/Makefile +++ b/release/Makefile @@ -19,7 +19,9 @@ # Variables affecting the build process: # WORLDDIR: location of src tree -- must have built world and default kernel # (by default, the directory above this one) -# PORTSDIR: location of ports tree to distribute (default: /usr/ports) +# PORTSDIR: location of mports tree to distribute (default: /usr/mports) +# MPORT_REPOSITORY: blessed mport package repository to stage on dvd1.iso +# DVD_PACKAGE_ROOTS: application packages offered by bsdinstall # XTRADIR: xtra-bits-dir argument for /mkisoimages.sh # NOPKG: if set, do not distribute third-party packages # NOPORTS: if set, do not distribute ports tree @@ -36,6 +38,8 @@ WORLDDIR?= ${.CURDIR}/.. PORTSDIR?= /usr/mports +MPORT_REPOSITORY?= ${PORTSDIR}/Packages/${TARGET_ARCH}/All +DVD_PACKAGE_ROOTS?= midnightbsd-desktop bash zsh sudo screen tmux nano vim rsync git .if !defined(TARGET) || empty(TARGET) TARGET= ${MACHINE} @@ -222,7 +226,7 @@ release.iso: disc1.iso disc1.iso: disc1 sh ${.CURDIR}/${TARGET}/mkisoimages.sh -b ${VOLUME_LABEL}_CD ${.TARGET} disc1 ${XTRADIR} -dvd1.iso: dvd pkg-stage +dvd1.iso: pkg-stage sh ${.CURDIR}/${TARGET}/mkisoimages.sh -b ${VOLUME_LABEL}_DVD ${.TARGET} dvd ${XTRADIR} bootonly.iso: bootonly @@ -240,11 +244,12 @@ packagesystem: base.txz kernel.txz ${EXTRA_PACKAGES} sh ${.CURDIR}/scripts/make-manifest.sh *.txz > MANIFEST touch ${.TARGET} -pkg-stage: +pkg-stage: dvd .if !defined(NOPKG) || empty(NOPKG) - env PORTSDIR=${PORTSDIR} REPOS_DIR=${.CURDIR}/pkg_repos/ \ + env ROOTDIR=${.OBJDIR}/dvd \ + MPORT_REPOSITORY=${MPORT_REPOSITORY} \ + DVD_PACKAGE_ROOTS="${DVD_PACKAGE_ROOTS}" \ sh ${.CURDIR}/scripts/pkg-stage.sh - mkdir -p ${.OBJDIR}/dvd/packages/repos/ .endif touch ${.TARGET} diff --git a/release/packages.md b/release/packages.md new file mode 100644 index 00000000000..0886fc59d45 --- /dev/null +++ b/release/packages.md @@ -0,0 +1,99 @@ +# Packages on MidnightBSD installation media + +MidnightBSD DVD images can carry a selected subset of a normal, blessed mport +repository. The DVD contains the complete repository index but only the +package bundles needed for the installer choices and their runtime dependency +closure. This keeps the image bounded while leaving the installed system with +a complete package catalog for later network use. + +## Repository input + +Build and bless the packages with Magus before building the release. The +directory passed as `MPORT_REPOSITORY` must contain the uncompressed +`index.db` produced by `magus-bless` and every `.mport` bundle referenced by +that index. The index and bundles must all come from the same Magus run and +match the release architecture and OS version. + +The default repository location is: + +```text +/usr/mports/Packages/${TARGET_ARCH}/All +``` + +`/usr/mports` is the canonical MidnightBSD mports path. A release +configuration can override the repository and top-level choices: + +```sh +MPORT_REPOSITORY="/path/to/blessed/repository" +DVD_PACKAGE_ROOTS="midnightbsd-desktop bash zsh sudo screen tmux nano vim rsync git" +``` + +`NOPKG` continues to disable third-party package staging. Package payloads +are currently added only to `dvd1.iso`. + +## Staging + +`release/scripts/pkg-stage.sh` validates every configured root against the +blessed index, follows the `depends` table recursively, and copies the exact +bundle selected by each package name and version. It rejects ambiguous or +missing index entries, missing bundles, unsafe filenames, and SHA-256 +mismatches. A staging failure stops the DVD build rather than producing +incomplete offline media. + +The resulting `/packages` directory contains: + +- `index.db`: the complete blessed repository index, including remote entries + and mirrors. +- `installer.tsv`: curated application roots and their default checklist + state. `midnightbsd-desktop` is selected by default. +- `catalog.tsv`: a sanitized, searchable catalog of application packages. +- `.mport` files: only the configured roots and their recursive dependencies. + +Only application roots (`packages.type = 0`) are offered. The type stored in +the full index is not changed. + +## Installer behavior + +bsdinstall mounts the DVD package directory read-only at `/packages` inside +the target system. Its package screen provides a curated checklist plus a +name-and-description search of the full catalog. Search results are marked +as either present on the DVD or requiring the network. + +The complete index is copied to `/var/db/mport/index.db` if the target does not +already have one. Selected packages are installed with: + +```sh +mport -U -c "$BSDINSTALL_CHROOT" install -y package-name +``` + +`mport install` is preferable to `mport add` here. It uses the blessed index, +checks package hashes, resolves exact dependency versions, marks dependencies +automatic, uses `/packages` before attempting a download, and supports the +advanced network search. `-U` prevents an index refresh during installation +without disabling downloads for bundles absent from the DVD. + +Local curated packages must have their complete dependency closure on the +DVD. Network-only installation failures are reported and return the user to +the package menu; they do not invalidate an otherwise completed base-system +installation. + +## Future system packages + +Installing the base operating system as mport packages is deliberately out of +scope. The media layout and full index preserve `packages.type`, so a future +pkgbase-like installer can use system packages (`type = 1`) without changing +the third-party application menu. Base-system extraction and application +selection should remain separate until that work is designed and implemented. + +## Validation checklist + +- Exercise dependency cycles, shared dependencies, missing bundles, duplicate + roots, invalid types, and incorrect hashes with a synthetic index. +- Confirm that a disconnected installation of `midnightbsd-desktop` uses only + DVD bundles and records its dependencies as automatic. +- Confirm that remote search results install when networking and DNS are + configured and fail cleanly otherwise. +- Boot-test `dvd1.iso`, revisit Packages from Final Configuration, and verify + package-media unmounting. +- Verify that disc1, bootonly, and memory-stick images do not gain the DVD + package payload. diff --git a/release/release.conf.sample b/release/release.conf.sample index 88aaf65a5ab..cfc2abdfcca 100644 --- a/release/release.conf.sample +++ b/release/release.conf.sample @@ -46,6 +46,13 @@ PORTBRANCH="master" #NOPORTS= #NOSRC= #WITH_DVD= + +## Set the directory containing index.db and package bundles from one blessed +## Magus run. The default is /usr/mports/Packages/${TARGET_ARCH}/All. +#MPORT_REPOSITORY="/usr/mports/Packages/amd64/All" + +## Override the top-level application packages offered by bsdinstall. +#DVD_PACKAGE_ROOTS="midnightbsd-desktop bash zsh sudo screen tmux nano vim rsync git" #WITH_COMPRESSED_IMAGES= ## Set to '1' to disable multi-threaded xz(1) compression. @@ -77,8 +84,8 @@ PORTBRANCH="master" ## the chroot. This is intended for use when /usr/src already exists. #SRC_UPDATE_SKIP= -## Set to a non-empty value skip checkout or update of /usr/ports in -## the chroot. This is intended for use when /usr/ports already exists. +## Set to a non-empty value skip checkout or update of /usr/mports in +## the chroot. This is intended for use when /usr/mports already exists. #PORTS_UPDATE_SKIP= ## Set to pass additional flags to make(1) for the build chroot setup, such diff --git a/release/release.sh b/release/release.sh index 23e3528d757..90560d2e855 100755 --- a/release/release.sh +++ b/release/release.sh @@ -108,6 +108,11 @@ env_setup() { WITH_DVD= WITH_COMPRESSED_IMAGES= + # A blessed mport repository and optional package-root override used when + # constructing dvd1.iso. + MPORT_REPOSITORY= + DVD_PACKAGE_ROOTS= + # Set to non-empty value to build virtual machine images as part of # the release. WITH_VMIMAGES= @@ -194,6 +199,12 @@ env_check() { KERNCONF=\"${KERNEL}\" ${CONF_FILES} ${SRCPORTS} \ WITH_DVD=${WITH_DVD} WITH_VMIMAGES=${WITH_VMIMAGES} \ WITH_CLOUDWARE=${WITH_CLOUDWARE} XZ_THREADS=${XZ_THREADS}" + if [ -n "${MPORT_REPOSITORY}" ]; then + RELEASE_RMAKEFLAGS="${RELEASE_RMAKEFLAGS} MPORT_REPOSITORY=${MPORT_REPOSITORY}" + fi + if [ -n "${DVD_PACKAGE_ROOTS}" ]; then + RELEASE_RMAKEFLAGS="${RELEASE_RMAKEFLAGS} DVD_PACKAGE_ROOTS=\"${DVD_PACKAGE_ROOTS}\"" + fi return 0 } # env_check() @@ -211,10 +222,10 @@ chroot_setup() { fi fi if [ -z "${NOPORTS}" ] && [ -z "${PORTS_UPDATE_SKIP}" ]; then - if [ -d "${CHROOTDIR}/usr/ports/.git" ]; then - git -C ${CHROOTDIR}/usr/ports pull -q + if [ -d "${CHROOTDIR}/usr/mports/.git" ]; then + git -C ${CHROOTDIR}/usr/mports pull -q else - ${VCSCMD} ${PORT} -b ${PORTBRANCH} ${CHROOTDIR}/usr/ports + ${VCSCMD} ${PORT} -b ${PORTBRANCH} ${CHROOTDIR}/usr/mports fi fi @@ -238,7 +249,7 @@ extra_chroot_setup() { [ -e /etc/resolv.conf -a ! -e ${CHROOTDIR}/etc/resolv.conf ] && \ cp /etc/resolv.conf ${CHROOTDIR}/etc/resolv.conf # Run ldconfig(8) in the chroot directory so /var/run/ld-elf*.so.hints - # is created. This is needed by ports-mgmt/pkg. + # is created. This is needed when building software from mports. eval chroot ${CHROOTDIR} /etc/rc.d/ldconfig forcerestart # If MAKE_CONF and/or SRC_CONF are set and not character devices @@ -254,9 +265,9 @@ extra_chroot_setup() { _gitcmd="$(which git)" if [ -z "${NOGIT}" -a -z "${_gitcmd}" ]; then - # Install git from ports if the ports tree is available; - # otherwise install the pkg. - if [ -d ${CHROOTDIR}/usr/ports ]; then + # Install git from mports if the mports tree is available; + # otherwise install it with mport. + if [ -d ${CHROOTDIR}/usr/mports ]; then # Trick the ports 'run-autotools-fixup' target to do the right # thing. _OSVERSION=$(chroot ${CHROOTDIR} /usr/bin/uname -U) @@ -270,19 +281,19 @@ extra_chroot_setup() { PBUILD_FLAGS="OSVERSION=${_OSVERSION} BATCH=yes" PBUILD_FLAGS="${PBUILD_FLAGS} UNAME_r=${UNAME_r}" PBUILD_FLAGS="${PBUILD_FLAGS} OSREL=${REVISION}" - PBUILD_FLAGS="${PBUILD_FLAGS} WRKDIRPREFIX=/tmp/ports" + PBUILD_FLAGS="${PBUILD_FLAGS} WRKDIRPREFIX=/tmp/mports" PBUILD_FLAGS="${PBUILD_FLAGS} DISTDIR=/tmp/distfiles" eval chroot ${CHROOTDIR} env OPTIONS_UNSET=\"${GITUNSETOPTS}\" \ ${PBUILD_FLAGS} \ - make -C /usr/ports/devel/git FORCE_PKG_REGISTER=1 \ - WRKDIRPREFIX=/tmp/ports \ + make -C /usr/mports/devel/git FORCE_PKG_REGISTER=1 \ + WRKDIRPREFIX=/tmp/mports \ DISTDIR=/tmp/distfiles \ install clean distclean else eval chroot ${CHROOTDIR} env ASSUME_ALWAYS_YES=yes \ - pkg install -y devel/git + mport install -y git eval chroot ${CHROOTDIR} env ASSUME_ALWAYS_YES=yes \ - pkg clean -y + mport clean fi fi @@ -294,11 +305,11 @@ extra_chroot_setup() { PBUILD_FLAGS="OSVERSION=${_OSVERSION} BATCH=yes" PBUILD_FLAGS="${PBUILD_FLAGS} UNAME_r=${UNAME_r}" PBUILD_FLAGS="${PBUILD_FLAGS} OSREL=${REVISION}" - PBUILD_FLAGS="${PBUILD_FLAGS} WRKDIRPREFIX=/tmp/ports" + PBUILD_FLAGS="${PBUILD_FLAGS} WRKDIRPREFIX=/tmp/mports" PBUILD_FLAGS="${PBUILD_FLAGS} DISTDIR=/tmp/distfiles" for _PORT in ${EMBEDDEDPORTS}; do eval chroot ${CHROOTDIR} env ${PBUILD_FLAGS} make -C \ - /usr/ports/${_PORT} \ + /usr/mports/${_PORT} \ FORCE_PKG_REGISTER=1 deinstall install clean distclean done fi diff --git a/release/scripts/pkg-stage.sh b/release/scripts/pkg-stage.sh index 8e3e1f35e11..f25378ce5a9 100755 --- a/release/scripts/pkg-stage.sh +++ b/release/scripts/pkg-stage.sh @@ -1,92 +1,124 @@ #!/bin/sh # +# SPDX-License-Identifier: BSD-2-Clause # +# Stage a subset of a blessed mport repository on installation media. +# + +set -eu + +ROOTDIR=${ROOTDIR:-"$PWD/dvd"} +MPORT_REPOSITORY=${MPORT_REPOSITORY:-"/usr/mports/Packages/$(uname -p)/All"} +DVD_PACKAGE_ROOTS=${DVD_PACKAGE_ROOTS:-"midnightbsd-desktop bash zsh sudo screen tmux nano vim rsync git"} + +INDEX=${MPORT_REPOSITORY}/index.db +PACKAGE_DEST=${ROOTDIR}/packages +TAB=$(printf '\t') + +fail() +{ + echo "pkg-stage: $*" >&2 + exit 1 +} + +validate_atom() +{ + case "$1" in + ""|*[!A-Za-z0-9_.,+~:-]*) + fail "unsafe package name or version: $1" + ;; + esac +} + +case "$ROOTDIR" in +""|/) + fail "refusing unsafe ROOTDIR: $ROOTDIR" + ;; +esac + +[ -r "$INDEX" ] || fail "missing blessed mport index: $INDEX" +[ -n "$DVD_PACKAGE_ROOTS" ] || fail "DVD_PACKAGE_ROOTS is empty" +command -v sqlite3 >/dev/null 2>&1 || fail "sqlite3 is required" +command -v sha256 >/dev/null 2>&1 || fail "sha256 is required" +duplicate=$(sqlite3 -readonly "$INDEX" \ + "SELECT pkg FROM packages GROUP BY pkg HAVING count(*) > 1 LIMIT 1;") +[ -z "$duplicate" ] || + fail "package '$duplicate' has multiple entries in the blessed index" + +workdir=$(mktemp -d -t pkg-stage.XXXXXX) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM +roots=${workdir}/roots +closure_raw=${workdir}/closure.raw +closure=${workdir}/closure +: > "$roots" +: > "$closure_raw" -set -e - -export ASSUME_ALWAYS_YES="YES" -export PKG_DBDIR="/tmp/pkg" -export PERMISSIVE="YES" -export REPO_AUTOUPDATE="NO" -export ROOTDIR="$PWD/dvd" -export PKGCMD="/usr/sbin/pkg -d --rootdir ${ROOTDIR}" -export PORTSDIR="${PORTSDIR:-/usr/ports}" - -_DVD_PACKAGES="devel/git@lite -graphics/drm-kmod -graphics/drm-510-kmod -misc/freebsd-doc-all -net/mpd5 -net/rsync -ports-mgmt/pkg -shells/bash -shells/zsh -security/sudo -sysutils/screen -sysutils/seatd -sysutils/tmux -www/firefox -www/links -x11/gnome -x11/kde -x11/sddm -x11/xorg -x11-wm/sway" - -# If NOPORTS is set for the release, do not attempt to build pkg(8). -if [ ! -f ${PORTSDIR}/Makefile ]; then - echo "*** ${PORTSDIR} is missing! ***" - echo "*** Skipping pkg-stage.sh ***" - echo "*** Unset NOPORTS to fix this ***" - exit 0 -fi - -if [ ! -x /usr/local/sbin/pkg ]; then - /etc/rc.d/ldconfig restart - /usr/bin/make -C ${PORTSDIR}/ports-mgmt/pkg install clean -fi - -export PKG_ABI=$(pkg --rootdir ${ROOTDIR} config ABI) -export PKG_ALTABI=$(pkg --rootdir ${ROOTDIR} config ALTABI 2>/dev/null) -export PKG_REPODIR="packages/${PKG_ABI}" - -/bin/mkdir -p ${ROOTDIR}/${PKG_REPODIR} -if [ ! -z "${PKG_ALTABI}" ]; then - ln -s ${PKG_ABI} ${ROOTDIR}/packages/${PKG_ALTABI} -fi - -# Ensure the ports listed in _DVD_PACKAGES exist to sanitize the -# final list. -for _P in ${_DVD_PACKAGES}; do - if [ -d "${PORTSDIR}/${_P%%@*}" ]; then - DVD_PACKAGES="${DVD_PACKAGES} ${_P}" - else - echo "*** Skipping nonexistent port: ${_P%%@*}" - fi +for root in $DVD_PACKAGE_ROOTS; do + validate_atom "$root" + count=$(sqlite3 -readonly "$INDEX" \ + "SELECT count(*) FROM packages WHERE pkg='$root' AND type=0;") + [ "$count" -eq 1 ] || + fail "application root '$root' has $count matching index entries" + + row=$(sqlite3 -readonly -noheader -tabs "$INDEX" \ + "SELECT pkg, version, + coalesce(nullif(replace(replace(replace(comment, char(9), ' '), + char(10), ' '), char(13), ' '), ''), pkg) + FROM packages WHERE pkg='$root' AND type=0;") + state=off + [ "$root" = midnightbsd-desktop ] && state=on + printf '%s\t%s\n' "$row" "$state" >> "$roots" + + version=$(printf '%s\n' "$row" | awk -F "$TAB" '{ print $2 }') + validate_atom "$version" + sql="WITH RECURSIVE closure(pkg, version) AS (VALUES('$root', '$version') UNION SELECT d.d_pkg, d.d_version FROM depends AS d JOIN closure AS c ON d.pkg=c.pkg AND d.version=c.version) SELECT pkg, version FROM closure;" + sqlite3 -readonly -noheader -tabs "$INDEX" "$sql" >> "$closure_raw" done -# Make sure the package list is not empty. -if [ -z "${DVD_PACKAGES}" ]; then - echo "*** The package list is empty." - echo "*** Something is very wrong." - # Exit '0' so the rest of the build process continues - # so other issues (if any) can be addressed as well. - exit 0 -fi - -# Print pkg(8) information to make debugging easier. -${PKGCMD} -vv -${PKGCMD} update -f -${PKGCMD} fetch -o ${PKG_REPODIR} -d ${DVD_PACKAGES} - -# Create the 'Latest/pkg.txz' symlink so 'pkg bootstrap' works -# using the on-disc packages. -export LATEST_DIR="${ROOTDIR}/${PKG_REPODIR}/Latest" -mkdir -p ${LATEST_DIR} -ln -s ../All/$(${PKGCMD} rquery %n-%v pkg).pkg ${LATEST_DIR}/pkg.pkg -ln -sf pkg.pkg ${LATEST_DIR}/pkg.txz - -${PKGCMD} repo ${PKG_REPODIR} - -# Always exit '0', even if pkg(8) complains about conflicts. -exit 0 +sort -u "$closure_raw" > "$closure" + +rm -rf "$PACKAGE_DEST" +mkdir -p "$PACKAGE_DEST" +install -m 0644 "$INDEX" "$PACKAGE_DEST/index.db" +install -m 0644 "$roots" "$PACKAGE_DEST/installer.tsv" + +sqlite3 -readonly -noheader -tabs "$INDEX" \ + "SELECT pkg, version, + coalesce(nullif(replace(replace(replace(comment, char(9), ' '), + char(10), ' '), char(13), ' '), ''), pkg), bundlefile + FROM packages WHERE type=0 ORDER BY pkg, version;" \ + > "$PACKAGE_DEST/catalog.tsv" +chmod 0644 "$PACKAGE_DEST/catalog.tsv" + +while IFS="$TAB" read -r pkg version; do + validate_atom "$pkg" + validate_atom "$version" + count=$(sqlite3 -readonly "$INDEX" \ + "SELECT count(*) FROM packages WHERE pkg='$pkg' AND version='$version';") + [ "$count" -eq 1 ] || + fail "dependency '$pkg-$version' has $count matching index entries" + + row=$(sqlite3 -readonly -noheader -tabs "$INDEX" \ + "SELECT bundlefile, hash FROM packages WHERE pkg='$pkg' AND version='$version';") + bundle=${row%%"$TAB"*} + hash=${row#*"$TAB"} + case "$bundle" in + ""|*/*|*[!A-Za-z0-9_.,+~:-]*) + fail "unsafe bundle filename for '$pkg-$version': $bundle" + ;; + *.mport) + ;; + *) + fail "unexpected bundle filename for '$pkg-$version': $bundle" + ;; + esac + + source_file=${MPORT_REPOSITORY}/${bundle} + [ -f "$source_file" ] || fail "missing bundle: $source_file" + actual_hash=$(sha256 -q "$source_file") + [ "$actual_hash" = "$hash" ] || fail "hash mismatch for $bundle" + install -m 0644 "$source_file" "$PACKAGE_DEST/$bundle" +done < "$closure" + +package_count=$(wc -l < "$closure" | tr -d ' ') +echo "Staged $package_count packages and the full blessed index in $PACKAGE_DEST" diff --git a/release/tools/gce.conf b/release/tools/gce.conf index dc4d9fd370e..621d8eb69a0 100644 --- a/release/tools/gce.conf +++ b/release/tools/gce.conf @@ -104,7 +104,7 @@ EOF _INSTALLED_PACKAGES=$(chroot ${DESTDIR} pkg info -o -q -a) for PACKAGE in ${_INSTALLED_PACKAGES}; do chroot ${DESTDIR} \ - make -C /usr/ports/${PACKAGE} fetch + make -C /usr/mports/${PACKAGE} fetch done fi if [ -c "${DESTDIR}/dev/null" ]; then diff --git a/usr.sbin/bsdinstall/bsdinstall.8 b/usr.sbin/bsdinstall/bsdinstall.8 index adbda9ebf50..13e124696ea 100644 --- a/usr.sbin/bsdinstall/bsdinstall.8 +++ b/usr.sbin/bsdinstall/bsdinstall.8 @@ -248,6 +248,16 @@ in the new system to set the root user's password. Interactively invokes .Xr adduser 8 in the new system. +.It Cm packages +Installs applications into the new system from an mport repository staged in +.Pa /packages . +The interface offers a curated list of packages stored on the installation +media and can search the complete repository index for packages available from +network mirrors. +Selected packages are installed with their dependencies, and the media index +is copied to +.Pa /var/db/mport/index.db +when the new system does not already have an index. .It Cm time Interactively sets the time, date, and time zone of the new system. .It Cm services diff --git a/usr.sbin/bsdinstall/scripts/Makefile b/usr.sbin/bsdinstall/scripts/Makefile index f901cd94bf6..da4db856981 100644 --- a/usr.sbin/bsdinstall/scripts/Makefile +++ b/usr.sbin/bsdinstall/scripts/Makefile @@ -1,7 +1,7 @@ SCRIPTS= auto adduser bootconfig checksum config entropy \ fetchmissingdists hardening hostname jail keymap mirrorselect mount \ - netconfig netconfig_ipv4 netconfig_ipv6 rootpass script services time \ + netconfig netconfig_ipv4 netconfig_ipv6 packages rootpass script services time \ umount wlanconfig zfsboot BINDIR= ${LIBEXECDIR}/bsdinstall diff --git a/usr.sbin/bsdinstall/scripts/auto b/usr.sbin/bsdinstall/scripts/auto index bf30da3131e..128e76a3dc8 100755 --- a/usr.sbin/bsdinstall/scripts/auto +++ b/usr.sbin/bsdinstall/scripts/auto @@ -353,6 +353,9 @@ trap true SIGINT # This section is optional if [ "$NETCONFIG_DONE" != yes ]; then bsdinstall netconfig # Don't check for errors -- the user may cancel fi +if [ -r /packages/installer.tsv ]; then + bsdinstall packages +fi bsdinstall time bsdinstall services bsdinstall hardening @@ -362,10 +365,7 @@ dialog --backtitle "MidnightBSD Installer" --title "Add User Accounts" --yesno \ bsdinstall adduser finalconfig() { - exec 3>&1 - REVISIT=$(dialog --backtitle "MidnightBSD Installer" \ - --title "Final Configuration" --no-cancel --menu \ - "Setup of your MidnightBSD system is nearly complete. You can now modify your configuration choices. After this screen, you will have an opportunity to make more complex changes using a shell." 0 0 0 \ + set -- \ "Exit" "Apply configuration and exit installer" \ "Add User" "Add a user to the system" \ "Root Password" "Change root password" \ @@ -373,7 +373,15 @@ finalconfig() { "Network" "Networking configuration" \ "Services" "Set daemons to run on startup" \ "System Hardening" "Set security options" \ - "Time Zone" "Set system timezone" 2>&1 1>&3) + "Time Zone" "Set system timezone" + if [ -r /packages/installer.tsv ]; then + set -- "$@" "Packages" "Install additional applications" + fi + + exec 3>&1 + REVISIT=$(dialog --backtitle "MidnightBSD Installer" \ + --title "Final Configuration" --no-cancel --menu \ + "Setup of your MidnightBSD system is nearly complete. You can now modify your configuration choices. After this screen, you will have an opportunity to make more complex changes using a shell." 0 0 0 "$@" 2>&1 1>&3) exec 3>&- case "$REVISIT" in @@ -393,6 +401,10 @@ finalconfig() { bsdinstall netconfig finalconfig ;; + "Packages") + bsdinstall packages + finalconfig + ;; "Services") bsdinstall services finalconfig diff --git a/usr.sbin/bsdinstall/scripts/mount b/usr.sbin/bsdinstall/scripts/mount index b4c46a627e5..691d47a2492 100755 --- a/usr.sbin/bsdinstall/scripts/mount +++ b/usr.sbin/bsdinstall/scripts/mount @@ -53,8 +53,8 @@ done mkdir $BSDINSTALL_CHROOT/dev 2>/dev/null mount -t devfs devfs $BSDINSTALL_CHROOT/dev -# If installing from the DVD, mount packages where they'll be accessible +# If installing from the DVD, mount packages at mport's media path. if [ -d /packages ]; then - mkdir -p $BSDINSTALL_CHROOT/dist/packages - mount -t nullfs /packages $BSDINSTALL_CHROOT/dist/packages + mkdir -p "$BSDINSTALL_CHROOT/packages" + mount -t nullfs -o ro /packages "$BSDINSTALL_CHROOT/packages" fi diff --git a/usr.sbin/bsdinstall/scripts/packages b/usr.sbin/bsdinstall/scripts/packages new file mode 100755 index 00000000000..e47e603652a --- /dev/null +++ b/usr.sbin/bsdinstall/scripts/packages @@ -0,0 +1,202 @@ +#!/bin/sh +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Install packages from a blessed mport repository on installation media. +# + +: ${BSDINSTALL_CHROOT:=/mnt} +: ${BSDINSTALL_TMPETC:=/tmp/bsdinstall_etc} +: ${DIALOG:=dialog} + +MEDIA_DIR=/packages +INDEX=${MEDIA_DIR}/index.db +INSTALLER_LIST=${MEDIA_DIR}/installer.tsv +CATALOG=${MEDIA_DIR}/catalog.tsv +TAB=$(printf '\t') + +[ -r "$INDEX" ] && [ -r "$INSTALLER_LIST" ] && [ -r "$CATALOG" ] || exit 0 + +workdir=$(mktemp -d -t bsdinstall-packages.XXXXXX) || exit 1 +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +valid_package() +{ + case "$1" in + ""|*[!A-Za-z0-9_.,+~:-]*) return 1 ;; + *) return 0 ;; + esac +} + +valid_bundle() +{ + case "$1" in + ""|*/*|*[!A-Za-z0-9_.,+~:-]*) return 1 ;; + *.mport) return 0 ;; + *) return 1 ;; + esac +} + +copy_index() +{ + target_dir=${BSDINSTALL_CHROOT}/var/db/mport + mkdir -p "$target_dir" || return 1 + if [ ! -e "$target_dir/index.db" ]; then + install -m 0644 "$INDEX" "$target_dir/index.db" || return 1 + fi +} + +prepare_resolver() +{ + resolver= + if [ -s "$BSDINSTALL_TMPETC/resolv.conf" ]; then + resolver=$BSDINSTALL_TMPETC/resolv.conf + elif [ -s /etc/resolv.conf ]; then + resolver=/etc/resolv.conf + fi + [ -n "$resolver" ] || return 1 + cp "$resolver" "$BSDINSTALL_CHROOT/etc/resolv.conf" +} + +is_installed() +{ + /usr/sbin/mport -c "$BSDINSTALL_CHROOT" info -e "$1" >/dev/null 2>&1 +} + +install_package() +{ + pkg=$1 + bundle=$2 + valid_package "$pkg" || return 1 + valid_bundle "$bundle" || return 1 + + if [ ! -f "$MEDIA_DIR/$bundle" ]; then + prepare_resolver || { + $DIALOG --backtitle "MidnightBSD Installer" \ + --title "Network Required" --msgbox \ + "The package $pkg is not on the DVD and no resolver configuration is available." 0 0 + return 1 + } + fi + + output=${workdir}/mport-output + $DIALOG --backtitle "MidnightBSD Installer" --title "Packages" \ + --infobox "Installing $pkg and its dependencies..." 0 0 + if /usr/sbin/mport -U -c "$BSDINSTALL_CHROOT" install -y "$pkg" \ + > "$output" 2>&1; then + return 0 + fi + + if [ -n "${debugFile:-}" ]; then + cat "$output" >> "${debugFile#+}" + fi + $DIALOG --backtitle "MidnightBSD Installer" --title "Package Installation Failed" \ + --textbox "$output" 20 76 + return 1 +} + +curated_packages() +{ + selection=${workdir}/selected + set -- + while IFS="$TAB" read -r pkg version description state; do + valid_package "$pkg" || continue + if is_installed "$pkg"; then + state=on + description="$description [installed]" + fi + set -- "$@" "$pkg" "$version - $description" "$state" + done < "$INSTALLER_LIST" + [ "$#" -gt 0 ] || return 0 + + exec 3>&1 + choices=$($DIALOG --backtitle "MidnightBSD Installer" --title "Package Selection" \ + --separate-output --checklist \ + "Select packages to install. Required dependencies are installed automatically." \ + 0 0 0 "$@" 2>&1 1>&3) + result=$? + exec 3>&- + [ "$result" -eq 0 ] && [ -n "$choices" ] || return 0 + printf '%s\n' "$choices" > "$selection" + + while IFS= read -r pkg; do + bundle=$(awk -F "$TAB" -v name="$pkg" '$1 == name { print $4; exit }' "$CATALOG") + install_package "$pkg" "$bundle" + done < "$selection" +} + +search_packages() +{ + exec 3>&1 + term=$($DIALOG --backtitle "MidnightBSD Installer" --title "Search Packages" \ + --inputbox "Enter text from a package name or description:" 0 0 \ + 2>&1 1>&3) + result=$? + exec 3>&- + [ "$result" -eq 0 ] && [ -n "$term" ] || return 0 + + results=${workdir}/search-results + awk -F "$TAB" -v needle="$term" ' + BEGIN { needle = tolower(needle); count = 0 } + index(tolower($1 " " $3), needle) != 0 { + print + count++ + if (count == 101) + exit + }' "$CATALOG" > "$results" + + [ -s "$results" ] || { + $DIALOG --backtitle "MidnightBSD Installer" --title "Search Packages" \ + --msgbox "No packages matched '$term'." 0 0 + return 0 + } + + set -- + count=0 + while IFS="$TAB" read -r pkg version description bundle; do + count=$((count + 1)) + [ "$count" -le 100 ] || break + valid_package "$pkg" && valid_bundle "$bundle" || continue + location=Network + [ -f "$MEDIA_DIR/$bundle" ] && location=DVD + set -- "$@" "$pkg" "$version - $description [$location]" + done < "$results" + [ "$#" -gt 0 ] || return 0 + + exec 3>&1 + choice=$($DIALOG --backtitle "MidnightBSD Installer" --title "Search Results" \ + --menu "Select a package to install. Refine the search if results were truncated." \ + 0 0 0 "$@" 2>&1 1>&3) + result=$? + exec 3>&- + [ "$result" -eq 0 ] || return 0 + bundle=$(awk -F "$TAB" -v name="$choice" '$1 == name { print $4; exit }' "$results") + install_package "$choice" "$bundle" +} + +copy_index || { + $DIALOG --backtitle "MidnightBSD Installer" --title "Packages" \ + --msgbox "Could not install the mport repository index in the new system." 0 0 + exit 1 +} + +while :; do + exec 3>&1 + action=$($DIALOG --backtitle "MidnightBSD Installer" --title "Packages" \ + --cancel-label "Done" --menu \ + "Install applications from the DVD or search the full MidnightBSD package catalog." \ + 0 0 0 \ + "Curated" "Select packages included on the DVD" \ + "Search" "Search DVD and network packages" \ + "Done" "Return to the installer" 2>&1 1>&3) + result=$? + exec 3>&- + [ "$result" -eq 0 ] || break + case "$action" in + Curated) curated_packages ;; + Search) search_packages ;; + Done) break ;; + esac +done + +exit 0 diff --git a/usr.sbin/bsdinstall/scripts/umount b/usr.sbin/bsdinstall/scripts/umount index 345cd1938b0..b68b16ac3f6 100755 --- a/usr.sbin/bsdinstall/scripts/umount +++ b/usr.sbin/bsdinstall/scripts/umount @@ -25,12 +25,12 @@ # SUCH DAMAGE. # -# If we mounted the DVD packages, unmount them -if [ -d $BSDINSTALL_CHROOT/dist/packages ]; then - umount $BSDINSTALL_CHROOT/dist/packages - rmdir $BSDINSTALL_CHROOT/dist/packages - rmdir $BSDINSTALL_CHROOT/dist +# If we mounted the DVD packages, unmount them. +PACKAGE_MOUNT="$BSDINSTALL_CHROOT/packages" +if mount | awk -v target="$PACKAGE_MOUNT" '$3 == target { found=1 } END { exit !found }'; then + umount "$PACKAGE_MOUNT" fi +rmdir "$PACKAGE_MOUNT" 2>/dev/null || true TMP_FSTAB=${TMPDIR:-"/tmp"}/bsdinstall-tmp-fstab