diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75c7d45..8a29218 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add `nvidia-ml-py` 13.595.45 to support list.
+- Add support for open kernel-module driver packages (e.g., `nvidia-driver-595-open`) in `install-nvidia-driver.sh` with new `--proprietary` and `--open` flags by [@XuehaiPan](https://github.com/XuehaiPan).
### Changed
diff --git a/README.md b/README.md
index 59d7b0e..dd04997 100644
--- a/README.md
+++ b/README.md
@@ -128,8 +128,9 @@ git clone --depth=1 https://github.com/XuehaiPan/nvitop.git && cd nvitop
# Optional for SSH users
sudo chvt 3 # or use keyboard shortcut: Ctrl-LeftAlt-F3
-bash install-nvidia-driver.sh --package=nvidia-driver-470 # install the R470 driver from ppa:graphics-drivers
+bash install-nvidia-driver.sh --package=nvidia-driver-595 # install the R595 driver from ppa:graphics-drivers
bash install-nvidia-driver.sh --latest # install the latest driver from ppa:graphics-drivers
+bash install-nvidia-driver.sh --latest --open # install the latest open-kernel-module driver
```
diff --git a/install-nvidia-driver.sh b/install-nvidia-driver.sh
index 85ff182..ba83f68 100755
--- a/install-nvidia-driver.sh
+++ b/install-nvidia-driver.sh
@@ -1,14 +1,17 @@
#!/bin/bash
# ==============================================================================
#
-# Usage: bash install-nvidia-driver.sh [--package=PKG] [--upgrade-only] [--latest] [--dry-run] [--yes] [--help]
+# Usage: bash install-nvidia-driver.sh [--package=PKG] [--upgrade-only] [--latest]
+# [--proprietary | --open] [--dry-run] [--yes] [--help]
#
# Examples:
#
# bash install-nvidia-driver.sh
-# bash install-nvidia-driver.sh --package=nvidia-driver-470
+# bash install-nvidia-driver.sh --package=nvidia-driver-595
+# bash install-nvidia-driver.sh --package=nvidia-driver-595-open
# bash install-nvidia-driver.sh --upgrade-only
# bash install-nvidia-driver.sh --latest
+# bash install-nvidia-driver.sh --latest --open
#
# ==============================================================================
# This file is part of nvitop, the interactive NVIDIA-GPU process viewer.
@@ -28,7 +31,7 @@
# limitations under the License.
# ==============================================================================
-# shellcheck disable=SC2016,SC2312
+# shellcheck disable=SC2016,SC2310,SC2312
set -u
set -e
@@ -66,16 +69,22 @@ tty_green="$(tty_mkbold 32)"
tty_yellow="$(tty_mkbold 33)"
tty_white="$(tty_mkbold 37)"
tty_bold="$(tty_mkbold 39)"
+tty_dim="$(tty_escape 90)"
tty_reset="$(tty_escape 0)"
function usage() {
cat < 595
+ # nvidia-driver-595-open -> 595
+ local d="${1#nvidia-driver-}"
+ echo "${d%-open}"
+}
+
+function driver-flavor() {
+ # nvidia-driver-595 -> proprietary
+ # nvidia-driver-595-open -> open
+ if [[ "$1" == *-open ]]; then
+ echo 'open'
+ else
+ echo 'proprietary'
+ fi
+}
+
+function toggle-open-suffix() {
+ # nvidia-driver-595 -> nvidia-driver-595-open
+ # nvidia-driver-595-open -> nvidia-driver-595
+ if [[ "$1" == *-open ]]; then
+ echo "${1%-open}"
+ else
+ echo "$1-open"
+ fi
+}
+
+function driver-available() {
+ # Predicate: is "$1" present in the global AVAILABLE_DRIVERS array?
+ local d
+ for d in "${AVAILABLE_DRIVERS[@]}"; do
+ if [[ "${d}" == "$1" ]]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
function apt-candidate-version() {
apt-cache policy "$1" | grep -F 'Candidate' | awk '{ print $2 }'
}
@@ -339,7 +407,7 @@ fi
# shellcheck disable=SC2207
AVAILABLE_DRIVERS=($(
apt-cache search --names-only nvidia-driver |
- awk '$1 ~ /^nvidia-driver-([0-9]+)$/ { print $1 }' |
+ awk '$1 ~ /^nvidia-driver-([0-9]+)(-open)?$/ { print $1 }' |
sort -V
))
@@ -347,22 +415,80 @@ if [[ "${#AVAILABLE_DRIVERS[@]}" -eq 0 ]]; then
abort "No available drivers found from APT."
fi
-LATEST_DRIVER="${AVAILABLE_DRIVERS[-1]}"
-LATEST_DRIVER_VERSION="$(apt-candidate-version "${LATEST_DRIVER}")"
+# `sort -V` orders `nvidia-driver-595` before `nvidia-driver-595-open`, so picking
+# `${AVAILABLE_DRIVERS[-1]}` would silently flip the default to open. Pick per-flavor instead.
+LATEST_PROPRIETARY_DRIVER=''
+LATEST_OPEN_DRIVER=''
+for driver in "${AVAILABLE_DRIVERS[@]}"; do
+ if [[ "$(driver-flavor "${driver}")" == 'open' ]]; then
+ LATEST_OPEN_DRIVER="${driver}"
+ else
+ LATEST_PROPRIETARY_DRIVER="${driver}"
+ fi
+done
-INSTALLED_DRIVER="$(apt-list-packages | awk '$2 ~ /nvidia-driver-([0-9]+)$/ { print $2 }')"
+INSTALLED_DRIVER="$(apt-list-packages | awk '$2 ~ /nvidia-driver-([0-9]+)(-open)?$/ { print $2 }')"
if [[ -n "${INSTALLED_DRIVER}" ]]; then
INSTALLED_DRIVER_VERSION="$(apt-installed-version "${INSTALLED_DRIVER}")"
INSTALLED_DRIVER_CANDIDATE_VERSION="$(apt-candidate-version "${INSTALLED_DRIVER}")"
+ INSTALLED_FLAVOR="$(driver-flavor "${INSTALLED_DRIVER}")"
else
INSTALLED_DRIVER_VERSION=''
INSTALLED_DRIVER_CANDIDATE_VERSION=''
+ INSTALLED_FLAVOR=''
fi
+# Effective flavor for picking LATEST_DRIVER: explicit `--proprietary`/`--open` wins; otherwise
+# preserve the installed driver's flavor; fresh hosts fall through to the `FLAVOR` default.
+EFFECTIVE_FLAVOR="${FLAVOR}"
+if [[ -z "${FLAVOR_EXPLICIT}" && -n "${INSTALLED_FLAVOR}" ]]; then
+ EFFECTIVE_FLAVOR="${INSTALLED_FLAVOR}"
+fi
+
+if [[ "${EFFECTIVE_FLAVOR}" == 'open' ]]; then
+ LATEST_DRIVER="${LATEST_OPEN_DRIVER}"
+ LATEST_FALLBACK="${LATEST_PROPRIETARY_DRIVER}"
+else
+ LATEST_DRIVER="${LATEST_PROPRIETARY_DRIVER}"
+ LATEST_FALLBACK="${LATEST_OPEN_DRIVER}"
+fi
+# Resolution-time fallback only matters when the script needs LATEST_DRIVER to compute
+# REQUESTED_DRIVER. An explicit `--package=PKG` is the user's authoritative override and is
+# allowed through even when the preserved flavor has no APT candidates.
+if [[ -z "${LATEST_DRIVER}" && -z "${REQUESTED_DRIVER}" ]]; then
+ if [[ -n "${FLAVOR_EXPLICIT}" ]]; then
+ abort "No \`${EFFECTIVE_FLAVOR}\` driver candidates available from APT. Drop \`--${FLAVOR}\`, run \`sudo apt-get update\` to refresh the package cache, or pass \`--package=PKG\` to override."
+ fi
+ if [[ -n "${INSTALLED_DRIVER}" ]]; then
+ # Never silently switch flavor on an installed system: falling back to the opposite
+ # kernel-module flavor would be a root-level migration the user did not ask for.
+ if [[ "${EFFECTIVE_FLAVOR}" == 'open' ]]; then
+ opposite_flavor='proprietary'
+ else
+ opposite_flavor='open'
+ fi
+ abort "No \`${EFFECTIVE_FLAVOR}\` driver candidates available from APT to upgrade \`${INSTALLED_DRIVER}\`. Run \`sudo apt-get update\` to refresh the cache, pass \`--${opposite_flavor}\` to switch flavors explicitly, or pass \`--package=PKG\` to override."
+ fi
+ # Fresh-install fallback: pick whichever flavor the PPA publishes rather than refusing.
+ warn "No \`${EFFECTIVE_FLAVOR}\` driver candidates available; falling back to \`${LATEST_FALLBACK}\` ($(driver-flavor "${LATEST_FALLBACK}") flavor)."
+ LATEST_DRIVER="${LATEST_FALLBACK}"
+fi
+LATEST_DRIVER_VERSION="$(apt-candidate-version "${LATEST_DRIVER}")"
+
if [[ -z "${REQUESTED_DRIVER}" ]]; then
if [[ -n "${LATEST}" || -z "${INSTALLED_DRIVER}" ]]; then
REQUESTED_DRIVER="${LATEST_DRIVER}"
REQUESTED_DRIVER_VERSION="${LATEST_DRIVER_VERSION}"
+ elif [[ -n "${FLAVOR_EXPLICIT}" && "${INSTALLED_FLAVOR}" != "${FLAVOR}" ]]; then
+ # Standalone `--open` / `--proprietary` (no `--latest`, no `--package`) on an
+ # opposite-flavor install: switch to the same-major counterpart, e.g.
+ # `--open` on `nvidia-driver-595` -> `nvidia-driver-595-open`.
+ counterpart="$(toggle-open-suffix "${INSTALLED_DRIVER}")"
+ if ! driver-available "${counterpart}"; then
+ abort "Cannot switch \`${INSTALLED_DRIVER}\` to ${FLAVOR} flavor: \`${counterpart}\` is not available from APT. Pass \`--latest\` to switch to the latest ${FLAVOR} branch instead."
+ fi
+ REQUESTED_DRIVER="${counterpart}"
+ REQUESTED_DRIVER_VERSION="$(apt-candidate-version "${REQUESTED_DRIVER}")"
else
REQUESTED_DRIVER="${INSTALLED_DRIVER}"
REQUESTED_DRIVER_VERSION="${INSTALLED_DRIVER_CANDIDATE_VERSION}"
@@ -372,14 +498,52 @@ else
if [[ -z "${REQUESTED_DRIVER_VERSION}" ]]; then
abort "Unable to locate package ${REQUESTED_DRIVER}."
fi
+ requested_flavor="$(driver-flavor "${REQUESTED_DRIVER}")"
+ if [[ -n "${FLAVOR_EXPLICIT}" && "${requested_flavor}" != "${FLAVOR}" ]]; then
+ suggested="$(toggle-open-suffix "${REQUESTED_DRIVER}")"
+ conflict_msg="Option \`--${FLAVOR}\` conflicts with package \`${REQUESTED_DRIVER}\` (${requested_flavor} flavor)."
+ if driver-available "${suggested}"; then
+ conflict_msg+=" Did you mean \`${suggested}\`?"
+ fi
+ abort "${conflict_msg}"
+ fi
+fi
+
+# `--upgrade-only` promises to keep the installed driver package, so reject any combination that
+# would change the package name (including a flavor flag that rewrote `REQUESTED_DRIVER` to the
+# counterpart, or an explicit `--package=` pointing at a different driver). Without this guard the
+# later install path would purge the existing NVIDIA stack and reinstall the new package — a
+# root-level system change masquerading as an upgrade.
+if [[ -n "${UPGRADE_ONLY}" && -n "${INSTALLED_DRIVER}" && "${INSTALLED_DRIVER}" != "${REQUESTED_DRIVER}" ]]; then
+ abort "Option \`--upgrade-only\` cannot change the driver package from \`${INSTALLED_DRIVER}\` to \`${REQUESTED_DRIVER}\`. Drop \`--upgrade-only\` to switch packages."
fi
ohai "Available NVIDIA drivers:"
for driver in "${AVAILABLE_DRIVERS[@]}"; do
+ flavor="$(driver-flavor "${driver}")"
prefix=" "
if [[ "${driver}" == "${REQUESTED_DRIVER}" ]]; then
prefix="--> "
fi
+
+ # Each flavor's branch tip gets a flavor-explicit annotation so "latest" cannot be confused
+ # with the user's effective `--latest` target under flavor preservation.
+ suffix=''
+ if [[ "${driver}" == "${LATEST_PROPRIETARY_DRIVER}" ]]; then
+ suffix=' (latest proprietary)'
+ elif [[ "${driver}" == "${LATEST_OPEN_DRIVER}" ]]; then
+ suffix=' (latest open)'
+ fi
+
+ # Dim other-flavor rows: picking one would require a purge+reinstall flavor switch. The
+ # REQUESTED_DRIVER row is exempt so the `--> ` arrow keeps priority on a deliberate switch.
+ dim=''
+ if [[ -n "${INSTALLED_FLAVOR}" &&
+ "${flavor}" != "${INSTALLED_FLAVOR}" &&
+ "${driver}" != "${REQUESTED_DRIVER}" ]]; then
+ dim=1
+ fi
+
if [[ "${driver}" == "${INSTALLED_DRIVER}" ]]; then
if [[ "${driver}" != "${REQUESTED_DRIVER}" ]]; then
prefix="<-- "
@@ -397,10 +561,12 @@ for driver in "${AVAILABLE_DRIVERS[@]}"; do
else
echo "${prefix}${tty_bold}${driver} [${INSTALLED_DRIVER_VERSION}]${tty_reset} ${tty_yellow}[installed]${tty_reset} (upgradable to [${INSTALLED_DRIVER_CANDIDATE_VERSION}])"
fi
+ elif [[ -n "${dim}" ]]; then
+ echo "${prefix}${tty_dim}${driver} [$(apt-candidate-version "${driver}")]${suffix}${tty_reset}"
elif [[ "${driver}" == "${LATEST_DRIVER}" ]]; then
- echo "${prefix}${tty_green}${driver} [${LATEST_DRIVER_VERSION}]${tty_reset} (latest)"
+ echo "${prefix}${tty_green}${driver} [${LATEST_DRIVER_VERSION}]${tty_reset}${suffix}"
else
- echo "${prefix}${driver} [$(apt-candidate-version "${driver}")]"
+ echo "${prefix}${driver} [$(apt-candidate-version "${driver}")]${suffix}"
fi
done
@@ -422,12 +588,24 @@ echo
if [[ -z "${INSTALLED_DRIVER}" ]]; then
ohai "Install the NVIDIA driver ${REQUESTED_DRIVER} [${REQUESTED_DRIVER_VERSION}]."
-elif [[ "${REQUESTED_DRIVER#nvidia-driver-}" -ge "${INSTALLED_DRIVER#nvidia-driver-}" ]]; then
+elif [[ "$(driver-major "${REQUESTED_DRIVER}")" -ge "$(driver-major "${INSTALLED_DRIVER}")" ]]; then
ohai "Upgrade the NVIDIA driver from ${INSTALLED_DRIVER} [${INSTALLED_DRIVER_VERSION}] to ${REQUESTED_DRIVER} [${REQUESTED_DRIVER_VERSION}]."
else
ohai "Downgrade the NVIDIA driver from ${INSTALLED_DRIVER} [${INSTALLED_DRIVER_VERSION}] to ${REQUESTED_DRIVER} [${REQUESTED_DRIVER_VERSION}]."
fi
+REQUESTED_FLAVOR="$(driver-flavor "${REQUESTED_DRIVER}")"
+if [[ -n "${INSTALLED_FLAVOR}" && "${INSTALLED_FLAVOR}" != "${REQUESTED_FLAVOR}" ]]; then
+ ohai "Switching kernel-module flavor from ${INSTALLED_FLAVOR} to ${REQUESTED_FLAVOR}."
+fi
+
+# Preflight the install plan before any teardown so an unsatisfiable APT transaction aborts
+# while the existing driver is still working. `--allow-change-held-packages` lets the simulate
+# plan past the existing apt-mark holds (the real `apt-mark unhold` runs in the teardown block).
+if [[ -n "${INSTALLED_DRIVER}" && "${INSTALLED_DRIVER}" != "${REQUESTED_DRIVER}" ]]; then
+ exec_cmd "sudo apt-get install --simulate --allow-change-held-packages --yes ${REQUESTED_DRIVER}"
+fi
+
DM_SERVICES=()
if [[ -n "$(sudo lsof -t /dev/nvidia* 2>/dev/null || true)" ]]; then
for dm in gdm3 lightdm; do
@@ -504,7 +682,8 @@ if [[ -n "${INSTALLED_DRIVER}" ]]; then
# Upgrade the existing driver packages
exec_cmd "sudo apt-get install --only-upgrade --yes ${NVIDIA_PACKAGES}"
else
- # Uninstall the existing driver packages
+ # The install plan was already validated above with `apt-get install --simulate` (before
+ # any destructive teardown), so this purge is safe to run.
exec_cmd "sudo apt-get purge --yes ${NVIDIA_PACKAGES}"
fi
fi