Merge branch 'develop' into feature/zram-memory-profile

This commit is contained in:
MacRimi
2026-10-16 17:27:04 +02:00
79 changed files with 5421 additions and 764 deletions
+30 -14
View File
@@ -456,6 +456,34 @@ PROXMENUX_NVIDIA_SERVICE_LEGACY_STATE="${PROXMENUX_NVIDIA_SERVICE_LEGACY_STATE:-
# remove them when migrating a host that ran the older init-top hook.
PROXMENUX_VFIO_BIND_LEGACY_HOOK="${PROXMENUX_VFIO_BIND_LEGACY_HOOK:-${PROXMENUX_ETC_ROOT}/initramfs-tools/scripts/init-top/proxmenux-vfio-bind}"
# The udev rule the state file asks for.
_proxmenux_vfio_bind_render_udev_rule() {
{
echo "# ProxMenux: per-BDF VFIO driver override"
echo "# Auto-generated from $PROXMENUX_VFIO_BIND_STATE"
echo "# DO NOT EDIT MANUALLY — regenerated by add_gpu_vm.sh / switch_gpu_mode*.sh"
while IFS= read -r bdf; do
[[ -z "$bdf" ]] && continue
[[ "$bdf" == \#* ]] && continue
# KERNEL match expects the "0000:XX:YY.Z" form
local full="$bdf"
[[ "$full" != 0000:* ]] && full="0000:${full}"
echo "SUBSYSTEM==\"pci\", KERNEL==\"${full}\", ATTR{driver_override}=\"vfio-pci\""
done < "$PROXMENUX_VFIO_BIND_STATE"
}
}
# True when the udev rule on disk is the one the state file asks for, so a
# rule removed or edited by hand is written again on the next run.
_proxmenux_vfio_bind_udev_rule_current() {
if [[ ! -s "$PROXMENUX_VFIO_BIND_STATE" ]]; then
[[ ! -e "$PROXMENUX_VFIO_BIND_UDEV_RULE" ]]
return
fi
[[ -f "$PROXMENUX_VFIO_BIND_UDEV_RULE" ]] \
&& cmp -s <(_proxmenux_vfio_bind_render_udev_rule) "$PROXMENUX_VFIO_BIND_UDEV_RULE"
}
_proxmenux_vfio_bind_write_udev_rule() {
# Always nuke the obsolete init-top hook from earlier attempts (if it
# still exists) so a stale copy in initramfs can't run alongside the
@@ -470,19 +498,7 @@ _proxmenux_vfio_bind_write_udev_rule() {
fi
mkdir -p "$(dirname "$PROXMENUX_VFIO_BIND_UDEV_RULE")"
{
echo "# ProxMenux: per-BDF VFIO driver override"
echo "# Auto-generated from $PROXMENUX_VFIO_BIND_STATE"
echo "# DO NOT EDIT MANUALLY — regenerated by add_gpu_vm.sh / switch_gpu_mode*.sh"
while IFS= read -r bdf; do
[[ -z "$bdf" ]] && continue
[[ "$bdf" == \#* ]] && continue
# KERNEL match expects the "0000:XX:YY.Z" form
local full="$bdf"
[[ "$full" != 0000:* ]] && full="0000:${full}"
echo "SUBSYSTEM==\"pci\", KERNEL==\"${full}\", ATTR{driver_override}=\"vfio-pci\""
done < "$PROXMENUX_VFIO_BIND_STATE"
} > "$PROXMENUX_VFIO_BIND_UDEV_RULE"
_proxmenux_vfio_bind_render_udev_rule > "$PROXMENUX_VFIO_BIND_UDEV_RULE"
udevadm control --reload-rules >/dev/null 2>&1 || true
}
@@ -549,7 +565,7 @@ _proxmenux_vfio_bind_add_bdfs() {
changed=true
fi
done
if $changed; then
if $changed || ! _proxmenux_vfio_bind_udev_rule_current; then
_proxmenux_vfio_bind_write_udev_rule
_proxmenux_nvidia_vfio_policy_sync || true
_proxmenux_mark_host_config_changed
+11 -2
View File
@@ -96,20 +96,29 @@ def evaluate(suite, apply=False):
pve = []
ceph = []
debian = False
wrong_pve = []
wrong_ceph = []
for path, index, row in entries:
if not row['Enabled'] or 'deb' not in row['Types']:
continue
components = set(row['Components'])
if components & PVE_CHANNELS:
if suite not in row['Suites']:
raise ValueError('PVE repository suite does not match the installed version; no repository changed.')
wrong_pve.append(f"{' '.join(sorted(components & PVE_CHANNELS))} in {path} (suite {' '.join(row['Suites'])})")
pve.append((path, index, row))
if 'main' in components and suite in row['Suites'] and not components & PVE_CHANNELS:
debian = True
if 'enterprise' in components and any('/ceph-' in uri for uri in row['URIs']):
if suite not in row['Suites']:
raise ValueError('Enterprise Ceph repository suite does not match this PVE version; correct it in the Proxmox repository UI before switching.')
wrong_ceph.append(f"{path} (suite {' '.join(row['Suites'])})")
ceph.append((path, index, row))
# Name the stale sources, never their URIs: those can carry credentials.
if wrong_pve:
raise ValueError(f"This host runs Proxmox VE on {suite}, but these Proxmox VE repositories are for another suite: "
f"{'; '.join(wrong_pve)}. Disable or remove them in Node > Updates > Repositories and run the update again; no repository changed.")
if wrong_ceph:
raise ValueError(f"This host runs Proxmox VE on {suite}, but these Enterprise Ceph repositories are for another suite: "
f"{'; '.join(wrong_ceph)}. Correct them in Node > Updates > Repositories before switching; no repository changed.")
if any(set(row['Components']) & {'pve-no-subscription', 'pve-test', 'pvetest'} for _, _, row in pve):
return 'preserve'
+5
View File
@@ -339,6 +339,11 @@ evaluate_host_reboot_requirement() {
needs_change=true
fi
done
# The state alone is not enough: its udev rule may have been removed.
if declare -F _proxmenux_vfio_bind_udev_rule_current >/dev/null 2>&1 \
&& ! _proxmenux_vfio_bind_udev_rule_current; then
needs_change=true
fi
_file_has_exact_line "softdep nvidia pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_drm pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_modeset pre: vfio-pci" "$vfio_conf" || needs_change=true
@@ -67,6 +67,28 @@ grep -q 'ids=8086:7ad0' "$PROXMENUX_VFIO_CONF"
! grep -qi '10de:228b' "$PROXMENUX_VFIO_CONF"
_proxmenux_all_nvidia_in_vfio
[[ -f "$PROXMENUX_NVIDIA_VFIO_BLACKLIST" ]]
_proxmenux_vfio_bind_udev_rule_current
# A rule removed by hand while the state still lists the GPU is written again
# when the GPU is added once more, and the host is marked for a reboot.
rm -f "$PROXMENUX_VFIO_BIND_UDEV_RULE"
! _proxmenux_vfio_bind_udev_rule_current
HOST_CONFIG_CHANGED=false
_proxmenux_vfio_bind_add_bdfs 0000:01:00.0 0000:01:00.1
grep -qF 'KERNEL=="0000:01:00.0"' "$PROXMENUX_VFIO_BIND_UDEV_RULE"
grep -qF 'KERNEL=="0000:02:00.1"' "$PROXMENUX_VFIO_BIND_UDEV_RULE"
_proxmenux_vfio_bind_udev_rule_current
[[ "$HOST_CONFIG_CHANGED" == true ]]
# A rule that already matches the state is left alone.
HOST_CONFIG_CHANGED=false
_proxmenux_vfio_bind_add_bdfs 0000:01:00.0
[[ "$HOST_CONFIG_CHANGED" == false ]]
# A rule edited by hand is replaced by the one the state asks for.
echo '# edited' >> "$PROXMENUX_VFIO_BIND_UDEV_RULE"
_proxmenux_vfio_bind_add_bdfs 0000:01:00.0
! grep -qF '# edited' "$PROXMENUX_VFIO_BIND_UDEV_RULE"
# Returning only one GPU to the host must keep the other exact BDF in VFIO,
# remove the global NVIDIA blacklist, retain NVIDIA softdeps, and restore any
+22 -1
View File
@@ -63,6 +63,13 @@ ALLOW_HELPER_WITH_CUSTOM="${ALLOW_HELPER_WITH_CUSTOM:-0}"
DOCKER_STANDALONE_TARGETS="${DOCKER_STANDALONE_TARGETS:-}"
UPDATE_DOCKER_ENGINE="${UPDATE_DOCKER_ENGINE:-0}"
# Reject an abandoned local-only mode rather than silently installing if an
# older launcher still exports it. The check now has its own non-install job.
if [[ "${CHECK_APPLY_OS:-0}" != "0" ]]; then
echo "ERROR: combined check-and-update was removed; run the explicit package check first." >&2
exit 4
fi
if [[ ! "$VMID" =~ ^[1-9][0-9]*$ ]]; then
echo "ERROR: VMID must be a positive integer." >&2
exit 1
@@ -117,12 +124,26 @@ extract_helper_slug_from_wrapper() {
# One update per CT at a time, regardless of whether it came from the
# UI or the scheduler. The descriptor remains open for this process.
LOCK_DIR="${PROXMENUX_LOCK_DIR:-/run/lock}"
exec 9>"${LOCK_DIR}/proxmenux-lxc-update-${VMID}.lock"
exec 9>>"${LOCK_DIR}/proxmenux-lxc-update-${VMID}.lock"
if ! flock -n 9; then
echo "ERROR: another ProxMenux update is already running for CT $VMID." >&2
exit 7
fi
if [[ -s "${LOCK_DIR}/proxmenux-lxc-update-${VMID}.lock" ]]; then
if ! python3 -c 'import sys; sys.path.insert(0, "/usr/local/share/proxmenux/monitor-app/usr/bin"); import lxc_check_job; lxc_check_job.check_control_task(int(sys.argv[1]))' "$VMID"; then
echo "ERROR: container control completion is not confirmed; nothing was changed." >&2
exit 7
fi
fi
if [[ "${REQUIRE_CHECKED_OS:-0}" == "1" ]]; then
if ! python3 -c 'import sys; sys.path.insert(0, "/usr/local/share/proxmenux/monitor-app/usr/bin"); import lxc_check_job; lxc_check_job.checked_os(int(sys.argv[1]))' "$VMID"; then
echo "ERROR: a fresh successful positive OS check is required; nothing was installed." >&2
exit 4
fi
fi
STARTED_AT=$(date -Iseconds)
NODE=$(hostname)
echo "=== ProxMenux LXC update — CT $VMID on $NODE ==="
@@ -0,0 +1,6 @@
#!/bin/bash
# Install OS packages only after a valid positive check. The shared runner
# verifies that precondition under its CT lock before any lifecycle effect.
export REQUIRE_CHECKED_OS=1 TARGET=os RUN_HELPER=0 UPDATE_COMMAND=""
export ALLOW_HELPER_WITH_CUSTOM=0 DOCKER_STANDALONE_TARGETS="" UPDATE_DOCKER_ENGINE=0
exec /bin/bash "$(dirname -- "$(readlink -f -- "$0")")/apply_updates.sh"
+1 -1
View File
@@ -73,7 +73,7 @@ show_menu() {
4 "$(translate "Disk Manager")" \
5 "$(translate "Storage & Share Manager")" \
6 "$(translate "Proxmox VE Helper Scripts")" \
o "$(translate "OCI manager Apps (beta)")" \
o "$(translate "OCI manager Apps (Technology Preview)")" \
7 "$(translate "Network Management")" \
8 "$(translate "Security")" \
9 "$(translate "Utilities and Tools")" \
+2 -2
View File
@@ -36,8 +36,8 @@
"keywords": ""
},
{
"title": "OCI manager Apps (beta)",
"menu": "OCI manager Apps (beta)",
"title": "OCI manager Apps (Technology Preview)",
"menu": "OCI manager Apps (Technology Preview)",
"script": "oci/oci_manager_apps.sh",
"keywords": "docker container image contenedor imagen apps"
},
+27 -2
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# ==========================================================
# ProxMenux - OCI manager Apps (beta)
# ProxMenux - OCI manager Apps (Technology Preview)
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
@@ -37,7 +37,7 @@ ensure_oci_dependencies() {
[[ ${#missing[@]} -eq 0 ]] && return 0
show_proxmenux_logo
msg_title "$(translate "OCI manager Apps (beta)")"
msg_title "$(translate "OCI manager Apps (Technology Preview)")"
msg_info "$(translate "Installing required packages...")"
apt-get update >/dev/null 2>&1
local status
@@ -56,6 +56,31 @@ ensure_oci_dependencies() {
stop_spinner
}
# Proxmox VE runs OCI images as LXC containers from 9.1 on.
pve_supports_oci() {
local version major minor
version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
major=${version%%.*}
minor=${version#*.}
[[ $major =~ ^[0-9]+$ && $minor =~ ^[0-9]+$ ]] || return 1
(( major > 9 || (major == 9 && minor >= 1) ))
}
if ! pve_supports_oci; then
show_proxmenux_logo
msg_title "$(translate "OCI manager Apps (Technology Preview)")"
msg_error "$(translate "OCI containers require Proxmox VE 9.1 or later.")"
PVE_VERSION=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9.]+' | head -1)
[[ -n $PVE_VERSION ]] && msg_info2 "$(translate "Current version:") $PVE_VERSION"
if [[ -t 0 ]]; then
echo
msg_success "$(translate "Press Enter to return to the menu...")"
read -r
[[ $# -eq 0 ]] && exec bash "$LOCAL_SCRIPTS/menus/main_menu.sh"
fi
exit 1
fi
if [[ ! -f "$OCI_ENGINE_DIR/src/proxmenux_oci/__main__.py" ]]; then
msg_error "$(translate "The OCI engine is not installed. Update ProxMenux and try again.")"
exit 1
+21 -29
View File
@@ -113,10 +113,10 @@ function select_linux_iso_official() {
"Ubuntu 24.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.4-live-server-amd64.iso"
"Ubuntu 22.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso"
"Ubuntu 20.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-live-server-amd64.iso"
"Debian 13|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.4.0-amd64-DVD-1.iso"
"Debian 13|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/13.6.0/amd64/iso-dvd/debian-13.6.0-amd64-DVD-1.iso"
"Debian 12|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/12.13.0/amd64/iso-dvd/debian-12.13.0-amd64-DVD-1.iso"
"Debian 11|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-dvd/debian-11.11.0-amd64-DVD-1.iso"
"Debian 13 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.4.0-amd64-netinst.iso"
"Debian 13 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/13.6.0/amd64/iso-cd/debian-13.6.0-amd64-netinst.iso"
"Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/12.13.0/amd64/iso-cd/debian-12.13.0-amd64-netinst.iso"
"Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso"
"Fedora Workstation 42|Desktop|ProxMenux|https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso"
@@ -170,18 +170,15 @@ function select_linux_iso_official() {
function select_linux_cloudinit() {
# The maintained Debian and Ubuntu helpers select their own release.
local CLOUDINIT_OPTIONS=(
"1" "Arch Linux (Cloud-Init automated) │ Helper-Scripts"
"2" "Debian 13 (Cloud-Init automated) │ Helper-Scripts"
"3" "Debian 12 (Cloud-Init automated) │ Helper-Scripts"
"4" "Ubuntu 22.04 (Cloud-Init automated) │ Helper-Scripts"
"5" "Ubuntu 24.04 (Cloud-Init automated) │ Helper-Scripts"
"6" "Ubuntu 24.10 (Cloud-Init automated) │ Helper-Scripts"
"7" "Ubuntu 25.04 (Cloud-Init automated) │ Helper-Scripts"
"8" "$(translate "Return to Main Menu")"
"1" "Arch Linux (Cloud-Init automated) │ Helper-Scripts"
"2" "Debian (Cloud-Init automated) │ Helper-Scripts"
"3" "Ubuntu (Cloud-Init automated) │ Helper-Scripts"
"4" "$(translate "Return to Main Menu")"
)
local script_selection
local script_selection script_url script_content
script_selection=$(dialog --backtitle "ProxMenux" --title "$(translate "Cloud-Init Automated Installers")" \
--menu "\n$(translate "Select a pre-configured Linux VM script to execute:")" 20 78 10 \
"${CLOUDINIT_OPTIONS[@]}" 3>&1 1>&2 2>&3)
@@ -190,34 +187,29 @@ function select_linux_cloudinit() {
case "$script_selection" in
1)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh")
script_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh"
;;
2)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-13-vm.sh")
script_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh"
;;
3)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
script_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu-vm.sh"
;;
4)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh")
;;
5)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh")
;;
6)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh")
;;
7)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2504-vm.sh")
echo -e
echo -e "after installation, checkout:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/272"
echo -e
;;
8)
return
;;
*)
return 1
;;
esac
# Do not execute a failed or partial download through process substitution.
if ! script_content=$(curl -fsSL --connect-timeout 5 --max-time 60 "$script_url") || [[ -z "${script_content//[[:space:]]/}" ]]; then
dialog --title "Helper Scripts" --msgbox "$(translate "Error: Failed to download the script.")" 8 70
return 1
fi
bash -c "$script_content" || return $?
msg_success "$(translate "Press Enter to return to menu...")"
read -r
+4 -3
View File
@@ -100,7 +100,7 @@ function resolve_truenas_core_iso() {
}
function resolve_omv_iso() {
local default_ver="8.1.1"
local default_ver="8.3.1"
local detected_ver=""
if _has_curl; then
@@ -157,13 +157,14 @@ function resolve_xigmanas_iso() {
}
function resolve_rockstor_iso() {
local default_file="Rockstor-Leap15.6-generic.x86_64-5.0.15-0.install.iso"
local default_file="Rockstor-Leap15.6-generic.x86_64-5.1.0-0.install.iso"
local detected_file=""
local base_url="https://rockstor.com/downloads/installer/leap/15.6/x86_64"
if _has_curl; then
detected_file=$(
curl -fsSL "${base_url}/" 2>/dev/null \
# The installer directory forbids listings; use the official downloads page.
curl -fsSL "https://rockstor.com/dls/index.html" 2>/dev/null \
| grep -Eo 'Rockstor-Leap15\.6-generic\.x86_64-[0-9]+\.[0-9]+\.[0-9]+-[0-9]+\.install\.iso' \
| _latest_version_from_lines
)
+25 -23
View File
@@ -259,6 +259,25 @@ function run_gpu_passthrough_wizard() {
# Download beside the destination, publishing only a complete non-empty file.
function download_vm_iso() {
local iso_url="$1" iso_path="$2"
local staging_dir download_status=1
local -a wget_options=(--no-verbose --show-progress)
[[ "$iso_url" == *"sourceforge.net"* ]] && wget_options+=(--content-disposition)
staging_dir=$(mktemp -d "${iso_path}.part.XXXXXX") || return 1
if wget "${wget_options[@]}" -O "$staging_dir/media.iso" "$iso_url" \
&& [[ -s "$staging_dir/media.iso" ]] \
&& { [[ ! -f "$iso_path" ]] || chmod --reference="$iso_path" "$staging_dir/media.iso"; } \
&& mv -fT -- "$staging_dir/media.iso" "$iso_path"; then
download_status=0
fi
rm -f -- "$staging_dir/media.iso"
rmdir -- "$staging_dir" || return 1
return "$download_status"
}
# ==========================================================
# Create VM
# ==========================================================
@@ -269,18 +288,9 @@ function create_vm() {
local ISO_DIR="/var/lib/vz/template/iso"
if [[ -n "$ISO_PATH" && -n "$ISO_URL" && ! -f "$ISO_PATH" ]]; then
if [[ -n "$ISO_PATH" && -n "$ISO_URL" ]] && [[ ! -f "$ISO_PATH" || ! -s "$ISO_PATH" ]]; then
if [[ "$ISO_URL" == *"sourceforge.net"* ]]; then
wget --content-disposition --show-progress -O "$ISO_PATH" "$ISO_URL"
else
wget --no-verbose --show-progress -O "$ISO_PATH" "$ISO_URL"
fi
if [[ -f "$ISO_PATH" ]]; then
if download_vm_iso "$ISO_URL" "$ISO_PATH"; then
ISO_VOLID="${ISO_VOLID:-local:iso/$(basename "$ISO_PATH")}"
msg_ok "$(translate "ISO image downloaded")"
else
@@ -662,21 +672,13 @@ fi
case "$VIRTIO_OPTION" in
1)
if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then
if whiptail --title "ProxMenux" --yesno "$(translate "A VirtIO ISO already exists. Do you want to overwrite it?")" 10 60; then
wget -q --show-progress -O "$VIRTIO_DIR/virtio-win.iso" "$VIRTIO_DOWNLOAD_URL"
if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then
msg_ok "$(translate "VirtIO driver ISO downloaded successfully.")"
else
msg_error "$(translate "Failed to download VirtIO driver ISO.")"
fi
fi
else
wget -q --show-progress -O "$VIRTIO_DIR/virtio-win.iso" "$VIRTIO_DOWNLOAD_URL"
if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then
if [[ ! -f "$VIRTIO_DIR/virtio-win.iso" || ! -s "$VIRTIO_DIR/virtio-win.iso" ]] \
|| whiptail --title "ProxMenux" --yesno "$(translate "A VirtIO ISO already exists. Do you want to overwrite it?")" 10 60; then
if download_vm_iso "$VIRTIO_DOWNLOAD_URL" "$VIRTIO_DIR/virtio-win.iso"; then
msg_ok "$(translate "VirtIO driver ISO downloaded successfully.")"
else
msg_error "$(translate "Failed to download VirtIO driver ISO.")"
continue
fi
fi