Add detailed comments to all scripts and comprehensive README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
28allday 2026-03-28 12:22:00 +00:00
parent ae4d33cdc0
commit d083d88a72
4 changed files with 300 additions and 24 deletions

View file

@ -1,6 +1,31 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# NVIDIA open-kernel driver on Rocky Linux 10 (RTX 2000-series+) # ==============================================================================
# NVIDIA Open Kernel Driver Installer for Rocky Linux 10
#
# Installs NVIDIA's open-source kernel driver on Rocky Linux 10. This driver
# supports RTX 2000-series GPUs and newer (Turing, Ampere, Ada Lovelace, etc.).
#
# What this script does:
# 1. Enables the CRB (CodeReady Builder) and EPEL repositories
# 2. Installs kernel headers and DKMS build tools
# 3. Adds NVIDIA's official RHEL10 repository
# 4. Installs nvidia-driver with the open-source kernel module (via DKMS)
# 5. Checks for Secure Boot and advises on MOK key enrollment
#
# DKMS (Dynamic Kernel Module Support) automatically recompiles the NVIDIA
# module whenever the kernel is updated, so the driver survives kernel upgrades.
#
# Prerequisites:
# - Rocky Linux 10
# - NVIDIA GPU (RTX 2000-series or newer for open kernel driver)
# - Internet connection
#
# Usage:
# sudo ./NVIDIA_rocky.sh
# ==============================================================================
set -euo pipefail set -euo pipefail
# Trap errors and show which line failed — makes debugging much easier
trap 'echo "❌ Error on line $LINENO"; exit 1' ERR trap 'echo "❌ Error on line $LINENO"; exit 1' ERR
need_root() { need_root() {
@ -13,7 +38,9 @@ log() { printf "\n==> %s\n" "$*"; }
need_root need_root
# Robust arch detection (avoid `uname -i` which can be "unknown") # Detect CPU architecture to select the correct NVIDIA repo URL.
# We use uname -m instead of uname -i because -i can return "unknown"
# on some systems. x86_64 is standard desktop/server, aarch64 is ARM.
arch_m=$(uname -m) arch_m=$(uname -m)
case "$arch_m" in case "$arch_m" in
x86_64) archdir="x86_64" ;; x86_64) archdir="x86_64" ;;
@ -21,31 +48,50 @@ case "$arch_m" in
*) echo "Unsupported arch: $arch_m"; exit 1 ;; *) echo "Unsupported arch: $arch_m"; exit 1 ;;
esac esac
# Enable CRB (CodeReady Builder) — Rocky's equivalent of RHEL's optional repo.
# It provides development packages like kernel-devel that aren't in the base repo.
log "Ensuring DNF plugins and enabling CRB…" log "Ensuring DNF plugins and enabling CRB…"
dnf -y install dnf-plugins-core dnf -y install dnf-plugins-core
# CRB name is 'crb' on Rocky; fall back to helper if present
dnf config-manager --set-enabled crb || /usr/bin/crb enable || true dnf config-manager --set-enabled crb || /usr/bin/crb enable || true
# Enable EPEL (Extra Packages for Enterprise Linux) — community-maintained
# packages that fill gaps in the RHEL/Rocky base repos. Provides DKMS and
# other tools needed for building kernel modules.
log "Enabling EPEL…" log "Enabling EPEL…"
dnf -y install epel-release || \ dnf -y install epel-release || \
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
# Install kernel headers and build tools. These must match the RUNNING kernel
# version exactly — DKMS uses them to compile the NVIDIA module against
# the correct kernel source. If the kernel was recently updated but not
# rebooted, this may install headers for the old kernel.
log "Installing kernel build prerequisites for the running kernel…" log "Installing kernel build prerequisites for the running kernel…"
dnf -y install \ dnf -y install \
"kernel-devel-$(uname -r)" \ "kernel-devel-$(uname -r)" \
"kernel-headers-$(uname -r)" \ "kernel-headers-$(uname -r)" \
dkms make gcc elfutils-libelf-devel libglvnd-devel pciutils pkgconf mokutil dkms make gcc elfutils-libelf-devel libglvnd-devel pciutils pkgconf mokutil
# Add NVIDIA's official CUDA/driver repository. Rocky 10 uses the RHEL10 repo
# since Rocky is binary-compatible with RHEL. The repo URL includes the
# architecture (x86_64 or sbsa for ARM servers).
log "Adding NVIDIA's official RHEL10 repo (for Rocky 10)…" log "Adding NVIDIA's official RHEL10 repo (for Rocky 10)…"
repo_url="https://developer.download.nvidia.com/compute/cuda/repos/rhel10/${archdir}/cuda-rhel10.repo" repo_url="https://developer.download.nvidia.com/compute/cuda/repos/rhel10/${archdir}/cuda-rhel10.repo"
dnf config-manager --add-repo "${repo_url}" dnf config-manager --add-repo "${repo_url}"
dnf clean expire-cache dnf clean expire-cache
# Install the NVIDIA driver stack:
# nvidia-driver: Userspace driver (OpenGL, Vulkan, CUDA runtime)
# kmod-nvidia-open-dkms: Open-source kernel module (auto-rebuilds on kernel updates)
# nvidia-settings: GUI tool for configuring GPU settings
# dnf-plugin-nvidia: Optional DNF plugin that prevents driver/kernel mismatches
log "Installing NVIDIA open kernel driver (display + compute)…" log "Installing NVIDIA open kernel driver (display + compute)…"
dnf -y install nvidia-driver kmod-nvidia-open-dkms nvidia-settings dnf -y install nvidia-driver kmod-nvidia-open-dkms nvidia-settings
# Optional guardrails (OK if missing)
dnf -y install dnf-plugin-nvidia || true dnf -y install dnf-plugin-nvidia || true
# Check for Secure Boot. If enabled, the NVIDIA kernel module won't load
# unless its signing key (MOK — Machine Owner Key) is enrolled in the
# UEFI firmware. DKMS generates this key automatically, but the user must
# enroll it manually on the next reboot via the blue MOK management screen.
echo echo
if mokutil --sb-state 2>/dev/null | grep -qi enabled; then if mokutil --sb-state 2>/dev/null | grep -qi enabled; then
cat <<'SB' cat <<'SB'

166
README.md Normal file
View file

@ -0,0 +1,166 @@
# Rocky Linux Setup
Setup scripts for getting a Rocky Linux 10 workstation ready for video editing with DaVinci Resolve.
Three scripts that should be run in order:
1. **NVIDIA_rocky.sh** — Install NVIDIA GPU drivers
2. **fonts.sh** — Install Microsoft core fonts (Arial, Times New Roman, etc.)
3. **rocky_resolve.sh** — Install DaVinci Resolve
## Requirements
- **OS**: Rocky Linux 10
- **GPU**: NVIDIA (RTX 2000-series or newer for open kernel driver)
- **DaVinci Resolve ZIP**: Downloaded from [blackmagicdesign.com](https://www.blackmagicdesign.com/products/davinciresolve) to `~/Downloads/`
## Quick Start
```bash
git clone https://github.com/28allday/Rocky-Linux-Setup.git
cd Rocky-Linux-Setup
# Step 1: Install NVIDIA drivers
sudo ./NVIDIA_rocky.sh
sudo reboot
# Step 2: Install fonts (after reboot)
sudo ./fonts.sh
# Step 3: Install DaVinci Resolve
sudo ./rocky_resolve.sh
```
## Scripts
### NVIDIA_rocky.sh — NVIDIA Driver Installation
Installs NVIDIA's open-source kernel driver via DKMS (auto-rebuilds on kernel updates).
**What it does:**
| Step | Action |
|------|--------|
| 1 | Enables CRB (CodeReady Builder) repository |
| 2 | Enables EPEL repository |
| 3 | Installs kernel headers and DKMS build tools |
| 4 | Adds NVIDIA's official RHEL10 repository |
| 5 | Installs `nvidia-driver`, `kmod-nvidia-open-dkms`, `nvidia-settings` |
| 6 | Checks Secure Boot status and advises on MOK enrollment |
**After running:** Reboot, then verify with `nvidia-smi`.
**Secure Boot:** If enabled, you'll need to enroll the DKMS signing key:
```bash
sudo mokutil --import /var/lib/dkms/mok.pub
# Set a one-time password, then confirm on the next boot screen
sudo reboot
```
### fonts.sh — Microsoft Core Fonts
Installs Microsoft TrueType core fonts (Arial, Times New Roman, Courier New, Verdana, etc.).
**What it does:**
| Step | Action |
|------|--------|
| 1 | Installs build tools (`rpm-build`, `cabextract`, `wget`) |
| 2 | Downloads the font RPM spec from SourceForge |
| 3 | Builds the font RPM locally with `rpmbuild` |
| 4 | Installs the built RPM |
| 5 | Updates the system font cache |
**Why:** DaVinci Resolve and many documents expect these fonts. Without them, text renders with wrong fonts or missing characters.
### rocky_resolve.sh — DaVinci Resolve
Installs DaVinci Resolve from the official ZIP download.
**What it does:**
| Step | Action |
|------|--------|
| 1 | Installs runtime dependencies (OpenGL, X11 libs, libxcrypt-compat) |
| 2 | Detects zlib-ng-compat and sets SKIP_PACKAGE_CHECK if needed |
| 3 | Finds and extracts the Resolve ZIP from `~/Downloads/` |
| 4 | Runs Blackmagic's installer |
| 5 | Moves conflicting GLib/Pango libraries to a backup directory |
| 6 | Symlinks libcrypt.so.1 into Resolve's lib directory |
| 7 | Creates user data directories with correct ownership |
| 8 | Verifies critical libraries are loadable |
**Rocky 10 specific fix:** Rocky 10 replaced the `zlib` package with `zlib-ng-compat`. The library (`libz.so.1`) is identical, but Resolve's installer checks for the RPM name "zlib" and fails. The script detects this and bypasses the check.
**Launch Resolve** as your normal user (not root):
```bash
/opt/resolve/bin/resolve
```
## Troubleshooting
### NVIDIA driver not loading after reboot
```bash
# Check if module is loaded
lsmod | grep nvidia
# Check for Nouveau conflicts
lsmod | grep nouveau
# Disable Nouveau if conflicting
sudo grubby --args="nouveau.modeset=0 rd.driver.blacklist=nouveau" --update-kernel=ALL
sudo reboot
```
### DaVinci Resolve crashes on launch
- Check logs: `~/.local/share/DaVinciResolve/logs/`
- Verify NVIDIA driver: `nvidia-smi`
- Verify libraries: `ldconfig -p | grep libGLU`
- Try launching from terminal for error output: `/opt/resolve/bin/resolve`
### Font RPM build fails
- Check you have internet access (fonts are downloaded from Microsoft servers)
- Make sure `cabextract` installed: `rpm -q cabextract`
- Check rpmbuild output for specific download failures
### "zlib not found" during Resolve install
This is the zlib-ng-compat issue. The script handles it automatically, but if running the installer manually:
```bash
SKIP_PACKAGE_CHECK=1 ./DaVinci_Resolve_*.run
```
## Uninstalling
### NVIDIA Driver
```bash
sudo dnf remove nvidia-driver kmod-nvidia-open-dkms nvidia-settings
sudo reboot
```
### DaVinci Resolve
```bash
sudo rm -rf /opt/resolve
sudo rm -f /usr/share/applications/DaVinciResolve.desktop
rm -rf ~/.local/share/DaVinciResolve
rm -rf ~/.config/Blackmagic\ Design
```
### Microsoft Fonts
```bash
sudo rpm -e msttcorefonts
sudo fc-cache -fv
```
## Credits
- [Rocky Linux](https://rockylinux.org/) - Enterprise Linux distribution
- [Blackmagic Design](https://www.blackmagicdesign.com/) - DaVinci Resolve
- [NVIDIA](https://www.nvidia.com/) - GPU drivers
## License
This project is provided as-is.

View file

@ -1,32 +1,55 @@
#!/bin/bash #!/bin/bash
# ==============================================================================
# Microsoft Core Fonts Installer for Rocky Linux
#
# Installs Microsoft's TrueType core fonts (Arial, Times New Roman, Courier
# New, Verdana, etc.) on Rocky Linux. These fonts are needed for proper
# document rendering, web compatibility, and applications like DaVinci Resolve
# that expect standard Windows fonts to be available.
#
# The fonts aren't distributed as a package — instead, this script downloads
# an RPM spec file from SourceForge, builds the font RPM locally using
# rpmbuild, then installs it. This is the standard approach on RHEL-based
# systems since Microsoft's license doesn't allow redistribution as a
# pre-built package.
#
# Usage:
# sudo ./fonts.sh
# ==============================================================================
# Exit on any error set -e # Exit on any error
set -e
# 1. Ensure running as root (or via sudo) # Root is required for installing packages and system fonts.
if [[ "$(id -u)" -ne 0 ]]; then if [[ "$(id -u)" -ne 0 ]]; then
echo "Error: This script must be run as root (use sudo or login as root)." echo "Error: This script must be run as root (use sudo or login as root)."
exit 1 exit 1
fi fi
# 2. Install necessary tools if not already installed # Install the tools needed to download and build the font RPM:
# rpm-build: RPM package building tools (rpmbuild command)
# cabextract: Extracts Microsoft .cab archives (fonts are distributed in .cab files)
# wget: Downloads the spec file and font archives from the internet
# ttmkfdir: Creates font directory indexes for X11 font paths
REQUIRED_PKGS=(rpm-build cabextract wget) REQUIRED_PKGS=(rpm-build cabextract wget)
# Include ttmkfdir (or mkfontscale) if available/needed for font installation
REQUIRED_PKGS+=(ttmkfdir) REQUIRED_PKGS+=(ttmkfdir)
echo "Installing required packages: ${REQUIRED_PKGS[*]}" echo "Installing required packages: ${REQUIRED_PKGS[*]}"
dnf install -y "${REQUIRED_PKGS[@]}" 2>/dev/null || yum install -y "${REQUIRED_PKGS[@]}" dnf install -y "${REQUIRED_PKGS[@]}" 2>/dev/null || yum install -y "${REQUIRED_PKGS[@]}"
# 3. Download the Microsoft core fonts spec file from SourceForge # Download the RPM spec file that defines how to build the font package.
# This spec file tells rpmbuild where to download each font's .cab archive
# from Microsoft's servers and how to extract and install them.
SPEC_URL="http://corefonts.sourceforge.net/msttcorefonts-2.5-1.spec" SPEC_URL="http://corefonts.sourceforge.net/msttcorefonts-2.5-1.spec"
echo "Downloading spec file from $SPEC_URL" echo "Downloading spec file from $SPEC_URL"
wget -O /tmp/msttcorefonts.spec "$SPEC_URL" wget -O /tmp/msttcorefonts.spec "$SPEC_URL"
# 4. Build the RPM package for Microsoft core fonts # Build the RPM. rpmbuild downloads the font archives from Microsoft,
# extracts the .ttf files, and packages them into an installable RPM.
# The built RPM lands in ~/rpmbuild/RPMS/noarch/.
echo "Building RPM package for Microsoft TrueType core fonts..." echo "Building RPM package for Microsoft TrueType core fonts..."
rpmbuild -bb /tmp/msttcorefonts.spec rpmbuild -bb /tmp/msttcorefonts.spec
# 5. Install the generated RPM (containing the TrueType font files) # Find and install the built RPM.
FONT_RPM="$(find ~/rpmbuild/RPMS/noarch -name 'msttcorefonts*-*.noarch.rpm' -print -quit)" FONT_RPM="$(find ~/rpmbuild/RPMS/noarch -name 'msttcorefonts*-*.noarch.rpm' -print -quit)"
if [[ -f "$FONT_RPM" ]]; then if [[ -f "$FONT_RPM" ]]; then
echo "Installing $FONT_RPM" echo "Installing $FONT_RPM"
@ -36,7 +59,8 @@ else
exit 1 exit 1
fi fi
# 6. Update font cache so the system recognizes new fonts # Rebuild the font cache so applications can discover the newly installed fonts.
# fc-cache scans font directories and builds indexes for fast font lookup.
echo "Updating font cache..." echo "Updating font cache..."
fc-cache -fv fc-cache -fv

View file

@ -1,20 +1,41 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ==============================================================================
# DaVinci Resolve Installer for Rocky Linux 10
# #
# install_resolve_rocky10_from_zip_fixed_v6.sh # Installs DaVinci Resolve on Rocky Linux 10 with NVIDIA GPU support.
# DaVinci Resolve installer for Rocky/RHEL 10 (NVIDIA) installing from ZIP in ~/Downloads. # Handles the specific compatibility issues on RHEL-based systems:
# v6: add libXt (fixes 'libXt.so.6' missing for USD.plugin), keep zlib-ng compatibility skip. # - zlib-ng-compat vs legacy zlib (Rocky 10 uses zlib-ng which confuses
# Resolve's package checker — we bypass it with SKIP_PACKAGE_CHECK)
# - GLib/Pango library conflicts (same issue as Arch/Mint — bundled
# versions conflict with system libraries)
# - libcrypt.so.1 compatibility symlink
# - libXt for USD plugin support
# #
# Prerequisites:
# - Rocky Linux 10 with NVIDIA drivers installed (run NVIDIA_rocky.sh first)
# - DaVinci Resolve Linux ZIP downloaded to ~/Downloads/
#
# Usage:
# sudo ./rocky_resolve.sh
# ==============================================================================
set -Eeuo pipefail set -Eeuo pipefail
# -E: ERR traps inherited by functions/subshells
# -e: Exit on error
# -u: Error on unset variables
# -o pipefail: Pipe fails if any command in it fails
log() { echo -e "[resolve-install] $*"; } log() { echo -e "[resolve-install] $*"; }
die() { echo -e "\e[31mERROR:\e[0m $*" >&2; exit 1; } die() { echo -e "\e[31mERROR:\e[0m $*" >&2; exit 1; }
# Root required # Root is required for installing packages and writing to /opt/resolve.
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
die "Please run as root (e.g., sudo -i && bash $0)" die "Please run as root (e.g., sudo -i && bash $0)"
fi fi
# Target user # Figure out the real user — when running with sudo, we need to find their
# home directory to locate the Resolve ZIP in ~/Downloads/. Falls back to
# the first user in /home/ if SUDO_USER isn't set (e.g. running as root directly).
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
TARGET_USER="$SUDO_USER" TARGET_USER="$SUDO_USER"
else else
@ -28,7 +49,13 @@ RESOLVE_PREFIX="/opt/resolve"
log "Target user: ${TARGET_USER}" log "Target user: ${TARGET_USER}"
log "Downloads folder: ${DOWNLOADS_DIR}" log "Downloads folder: ${DOWNLOADS_DIR}"
# Repos & deps # Install runtime dependencies that Resolve needs:
# xcb-util-cursor: X11 cursor handling
# mesa-libGLU: OpenGL Utility Library (3D rendering)
# libxcrypt-compat: Legacy libcrypt.so.1 (Rocky 10 uses libxcrypt v2)
# zlib: Compression library
# libXt: X Toolkit library (fixes missing libXt.so.6 for USD plugin)
# libXrandr/etc: X11 extensions for display management
log "Enabling EPEL and installing required packages..." log "Enabling EPEL and installing required packages..."
if ! rpm -q epel-release &>/dev/null; then if ! rpm -q epel-release &>/dev/null; then
dnf -y install epel-release || dnf -y install "https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm" dnf -y install epel-release || dnf -y install "https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm"
@ -36,7 +63,11 @@ fi
dnf -y install unzip xcb-util-cursor mesa-libGLU libxcrypt-compat zlib libXt || die "Failed to install required packages." dnf -y install unzip xcb-util-cursor mesa-libGLU libxcrypt-compat zlib libXt || die "Failed to install required packages."
dnf -y install libXrandr libXinerama libXcursor libXi fontconfig freetype || true dnf -y install libXrandr libXinerama libXcursor libXi fontconfig freetype || true
# Determine if we need to bypass BM's package checker (legacy 'zlib' rpm name) # Rocky Linux 10 uses zlib-ng-compat instead of the legacy 'zlib' package.
# zlib-ng is a drop-in replacement that provides the same libz.so.1 library,
# but Resolve's built-in package checker looks for an RPM literally named
# "zlib" and fails when it doesn't find it. Setting SKIP_PACKAGE_CHECK=1
# tells the installer to skip this check.
NEED_SKIP=0 NEED_SKIP=0
if ! rpm -q zlib &>/dev/null; then if ! rpm -q zlib &>/dev/null; then
if rpm -q zlib-ng-compat &>/dev/null && [[ -e /usr/lib64/libz.so.1 || -e /lib64/libz.so.1 ]]; then if rpm -q zlib-ng-compat &>/dev/null && [[ -e /usr/lib64/libz.so.1 || -e /lib64/libz.so.1 ]]; then
@ -93,7 +124,11 @@ else
fi fi
fi fi
# Post-install tweaks # Post-install library conflict resolution.
# Resolve bundles old versions of GLib and Pango that conflict with the
# system versions on Rocky 10. Moving them to a backup directory forces
# Resolve to use the system libraries instead, which are newer and
# compatible (stable C ABI). This fixes crashes and "symbol not found" errors.
if [[ -d "${RESOLVE_PREFIX}/libs" ]]; then if [[ -d "${RESOLVE_PREFIX}/libs" ]]; then
log "Applying GLib/Pango conflict workaround in ${RESOLVE_PREFIX}/libs ..." log "Applying GLib/Pango conflict workaround in ${RESOLVE_PREFIX}/libs ..."
pushd "${RESOLVE_PREFIX}/libs" >/dev/null pushd "${RESOLVE_PREFIX}/libs" >/dev/null
@ -111,7 +146,9 @@ else
log "WARNING: ${RESOLVE_PREFIX}/libs not found. Was the install path different?" log "WARNING: ${RESOLVE_PREFIX}/libs not found. Was the install path different?"
fi fi
# libcrypt link # Create a symlink for libcrypt.so.1 inside Resolve's lib directory.
# Rocky 10 provides this via libxcrypt-compat, but Resolve may not find
# it in the system path due to its custom RPATH settings.
if [[ -e /usr/lib64/libcrypt.so.1 ]]; then if [[ -e /usr/lib64/libcrypt.so.1 ]]; then
ln -sf /usr/lib64/libcrypt.so.1 "${RESOLVE_PREFIX}/libs/libcrypt.so.1" && \ ln -sf /usr/lib64/libcrypt.so.1 "${RESOLVE_PREFIX}/libs/libcrypt.so.1" && \
log "Linked /usr/lib64/libcrypt.so.1 into ${RESOLVE_PREFIX}/libs" log "Linked /usr/lib64/libcrypt.so.1 into ${RESOLVE_PREFIX}/libs"
@ -119,7 +156,9 @@ else
log "WARNING: /usr/lib64/libcrypt.so.1 not found. libxcrypt-compat may not have installed correctly." log "WARNING: /usr/lib64/libcrypt.so.1 not found. libxcrypt-compat may not have installed correctly."
fi fi
# Logs + user dirs # Create Resolve's data directories with correct ownership. These store
# projects, preferences, cache, and logs. They must be owned by the real
# user (not root) since Resolve should be launched as a normal user.
install -d -m 1777 "${RESOLVE_PREFIX}/logs" install -d -m 1777 "${RESOLVE_PREFIX}/logs"
for d in \ for d in \
"${USER_HOME}/.local/share/DaVinciResolve" \ "${USER_HOME}/.local/share/DaVinciResolve" \
@ -131,7 +170,8 @@ do
chown -R "${TARGET_USER}:${TARGET_USER}" "$d" || true chown -R "${TARGET_USER}:${TARGET_USER}" "$d" || true
done done
# Sanity # Final sanity checks — verify that critical libraries are findable by the
# dynamic linker. If these fail, Resolve will crash on launch.
if ! ldconfig -p | grep -q "libGLU.so.1"; then if ! ldconfig -p | grep -q "libGLU.so.1"; then
die "libGLU.so.1 not found even after mesa-libGLU install." die "libGLU.so.1 not found even after mesa-libGLU install."
fi fi