#! /bin/bash

#Usage: /usr/bin/qwdctl controls the availability of virtual machines for the qemu-web-desktop/DARTS service.
#
#  Each entry in the configuration file '/etc/qemu-web-desktop/machines.conf' 
#  spans on 1 to 3 lines:
#
#  -  [name.ext or URL or file path] 
#  -  url=[URL to ISO, QCOW2, VDI, VMDK, RAW, VHD/VHDX, QED virtual machine disk, optional]
#  -  description=[description to be shown in the service page] 
#
#  Images listed in the configuration file without a 'url=' parameter are
#  expected to be downloaded by hand and installed into
#  '/var/lib/qemu-web-desktop/machines' by the local administrator. Then, just 
#  specify the [name.ext] and description.
#  When the description is missing, the VM file name is used.
#  Additional machine descriptions can be given in '/etc/qemu-web-desktop/config.d/*.ini' files.
# 
# use: 'qwdctl help' to get help about usage and commands/options.

# set -e

bin=$(dirname $0)
p=$(basename $0)

if command -v brew 2>&1 >/dev/null; then
  # using brew / Apple Mac
  PREFIX=`brew --prefix`
  GREP=$PREFIX/opt/grep/libexec/gnubin/grep
  EDITOR=nano
else
  GREP=grep
fi

# files to process
F=$PREFIX/etc/qemu-web-desktop/machines.conf
machine_conf=$F
if [ ! -f $F ]; then
  echo "$p: WARNING: can not find QWD machine.conf file ($F)."
fi
conf_d=$PREFIX/etc/qemu-web-desktop/config.d

F=$PREFIX/usr/share/qemu-web-desktop/html/desktop/index.html
index_html=$F
if [ -f      $PREFIX/var/www/qemu-web-desktop/html/desktop/index.html ]; then
  index_html=$PREFIX/var/www/qemu-web-desktop/html/desktop/index.html
elif [ ! -f  $F ]; then
  echo "$p: WARNING: can not find QWD index.html file ($F)."
fi

# set to yes if apache/httpd include_module does not properly show list in index.html
for i in $*; do 
  case "$i" in
    --machine-insert|--machine_insert|machine-insert|machine_insert)
      QWDCTL_INSERT=yes # for refresh/download
    ;;
    --yes|-y)
      QWDCTL_YES=1  # assume Yes for gpu and purge
    ;;
  esac
done

# generated files, should be linked into /usr/share/qemu-web-desktop/html/desktop/
F=$PREFIX/var/lib/qemu-web-desktop
qwdprefix=$F
if [ ! -d $F ]; then
  echo "$p: WARNING: can not find QWD machines directory ($F)."
fi
machine_html=$qwdprefix/machines.html

# search for local config
if [ -f "$bin/../config.pl" ]; then
  # using local configuration
  echo "$bin/../config.pl"
  config_pl=$(realpath "$bin/../config.pl")
else
  F=$PREFIX/etc/qemu-web-desktop/config.pl
  config_pl=$F
  if [ ! -f $F ]; then
    echo "$p: WARNING: can not find QWD config.pl file ($F)."
  fi
fi

# search for local cgi/.pl script
if [ -x "$bin/../cgi-bin/qemu-web-desktop.pl" ]; then
  cgi=$(realpath "$bin/../cgi-bin/qemu-web-desktop.pl")
elif [ -x "$cgi/qemu-web-desktop.pl" ]; then
  # local (git)
  cgi="$cgi/qemu-web-desktop.pl"
elif [ -x "$PREFIX/srv/http/cgi-bin/qemu-web-desktop.pl" ]; then
  # Arch/httpd default
  cgi=$PREFIX/srv/http/cgi-bin/qemu-web-desktop.pl
elif [ -x "$PREFIX/var/www/cgi-bin/qemu-web-desktop.pl" ]; then
  # RedHat/httpd and Brew default
  cgi=$PREFIX/var/www/cgi-bin/qemu-web-desktop.pl
else
  # Debian default
  cgi=$PREFIX/usr/lib/cgi-bin/qemu-web-desktop.pl
fi

# identify type of system
if [ -e $PREFIX/etc/initramfs-tools/modules ]; then
	# Debian-class
	DIST=Debian
else
	# Arch-class
	DIST=Arch
fi

# find machines: check for confget/crudini
if   command -v confget 2>&1 >/dev/null; then
  parse_ini=confget
elif command -v crudini 2>&1 >/dev/null; then
  parse_ini=crudini
else
  case "$1" in
    download|update|--download|refresh|--refresh)
      echo "$p: $1: WARNING: confget/crudini not found. Install any of these."
    ;;
  esac
fi

case "$1" in
# ------------------------------------------------------------------------------
    --download|download|update)
      # scan the machines.conf + INI file for [name.ext] and download them when URL are given.
    
	    mkdir -p $qwdprefix/machines || true
	    
	    for F in $machine_conf $conf_d/*.ini $conf_d/*.conf; do
	      if [ ! -f "$F" ]; then continue; fi
	      if [ "x$parse_ini" = "xconfget" ]; then
          sections=$(confget -f $F -q sections);
        else
          sections=$(crudini --get $F);
        fi
	      for i in $sections ; do
	          b=$(basename $i)
	          if [ -f "$i" ]; then
	            if [! -f "$qwdprefix/machines/downloads/$b" ]; then
	              # copy local VM file
	              echo "$p: Copy $i into $qwdprefix/machines/downloads/$b"
	              mkdir -p $qwdprefix/machines/downloads/$b || true
	              cp $i $qwdprefix/machines/downloads/$b/
	              i=$b
	            fi
	          elif [[ "$i" =~ ^"http" ]]; then
	            # identify distant VM given as "name.ext"
	            u=$i
	            i=$(basename $u)
	          elif [ "x$parse_ini" = "xconfget" ]; then
	            u=$(confget -f $F -s $i url)
	          else
	            u=$(crudini --get $F $i url || true)
	          fi
	          mkdir -p $qwdprefix/machines/downloads/$i || true
	          # download http stuff
	          if [ "$u" ] ; then
	            echo "$p: Download $u"
		          (cd $qwdprefix/machines/downloads/$i; wget -N $u)
	          fi
	          # create link if relevant
            vm=$(ls -t $qwdprefix/machines/downloads/$i/* 2>/dev/null | head -1)
            if [ -e "$vm" ] ; then
	            ln -sf $vm $qwdprefix/machines/$i
            fi
	      done
	    done
	    $p refresh
	    ;;
# ------------------------------------------------------------------------------
	  --edit|edit)
	    # edit the VM/machine list, the service configuration file or the service web page.
	  
	    # default to edit machine file
	    FILE=$2
	    case "$2" in
		    machine|machines|vm|list)
		    FILE=$machine_conf
		    echo "$p: Edit machines: WARNING: rather use '$p enable|disable VM'"
		    ;;
		    landing|web|index|html)
		    FILE=$index_html
		    ;;
		    config|service)
		    echo "$p: Edit config: WARNING: rather edit local files in $conf_d"
		    FILE=$config_pl
		    ;;
	    esac
	    echo "$p: Edit $FILE"
	    editor "$FILE" || if [ "x$EDITOR" = "x" ]; then edit "$FILE"; else $EDITOR "$FILE"; fi
	    # trigger 'download' command
	    $p download
	    ;;
# ------------------------------------------------------------------------------
    --refresh|refresh)
      # scan the machines.conf file, and generate the machines.html that lists
      # available images to show in the qemu-web-desktop main form.
    
	    mkdir -p $qwdprefix/snapshots || true
	    if getent passwd _qemu-web-desktop > /dev/null 2>&1; then
	      chown _qemu-web-desktop $qwdprefix/snapshots # when using MPM ITK
	    fi
	    mkdir -p $qwdprefix/machines || true
	    cd $qwdprefix/machines
	    echo "$p: $1 -> $machine_html"
	    
	    # list of machines
	    t=$(mktemp $machine_html.XXXXXX)
	    has_vm=no
	    chmod 644 $t
	    chmod a+rx $qwdprefix
	    for F in $machine_conf $conf_d/*.ini $conf_d/*.conf; do
	      if [ ! -f "$F" ]; then continue; fi
	      echo "$p: Reading  $F" 
	      if [ "x$parse_ini" = "xconfget" ]; then
          sections=$(confget -f $F -q sections);
        else
          sections=$(crudini --get $F);
        fi
	      for i in $sections; do
	          if [ "x$parse_ini" = "xconfget" ]; then
	            d=$(confget -f $F -s $i description)
	          else
	            d=$(crudini --get $F $i description)
	          fi
	          if [ "x$d" = "x" ]; then  # when no description is given, use file name
	            d= $i
	          fi
	          u=$i
	          i=$(basename $i)
	          if [ -e $i ] ; then
        	    if [ "$d" ]; then
		          	# add entry when VM file and descr are given
		          	if [ "$i" = "$u" ]; then
		          	  echo "Found    $i '$d'"
		          	else
		          	  echo "Found    $i '$d' (from $u)"
		          	fi
			          echo "<option value='$i'>$d</option>" >> $t
			          has_vm=yes
		          fi
		        else
		          echo "$p: WARNING: $F [$i] is not available."
	          fi
	      done
	    done
	    if [ $has_vm = no ]; then
	      echo "$p: WARNING: No VM is configured. Use 'qwdctl enable VM|FILE|URL' to add entries."
	      echo '<option value="monitor">[ No VM has been configured. Use "qwdctl enable VM" as admin ]</option>' > $t
	    fi
	    mv $t $machine_html
	    # handle case when apache mod_include does not work...
	    if [ "x$QWDCTL_INSERT" = "xyes" ]; then
	      echo "$p: Updating $index_html"
		    t=$(mktemp $index_html.XXXXXX)
		    lead="<\!--BEGIN_MACHINE_LIST-->"
		    tail="<\!--END_MACHINE_LIST-->"
		    sed -e "/$lead/,/$tail/{ /$lead/{p; r $machine_html
            }; /$tail/p; d }" $index_html > $t
        mv $t $index_html
		    chmod a+r $index_html
	    fi
	    ;;
# ------------------------------------------------------------------------------
	  --status|status)
	    # list running sessions.
	  
	    echo "$p status"
	    echo "  config:   $config_pl"
	    echo "            $conf_d/"
	    echo "  machines: $machine_conf"
	    echo "            $machine_html"
	    echo "            $qwdprefix/machines/"
	    echo "  landing:  $index_html"
	    ip_address=$(hostname -I | awk '{print $1}')
	    echo "  service:  http://$ip_address/qemu-web-desktop/"
	    echo " "
	    # Loop through machine lists
	    active_sections=()
      inactive_sections=()
      for F in "$machine_conf" "$conf_d"/*.ini "$conf_d"/*.conf; do
          # Skip if the file does not exist (e.g., no .ini or .conf files)
          [ -f "$F" ] || continue
          # Extract active sections
              while IFS= read -r section; do
                  active_sections+=("$section")
              done < <(grep -E '^\[[^]]+\]' "$F" | sed -E 's/^\[([^]]+)\]/\1/')

              # Extract inactive sections
              while IFS= read -r section; do
                  inactive_sections+=("$section")
              done < <(grep -E '^;\[[^]]+\]' "$F" | sed -E 's/^;\[([^]]+)\]/\1/')
      done
	    
	    if [ -z "${active_sections[*]}" ]; then
	      echo "Available machines (use '$p enable VM|FILE|URL' to add one):"
	      echo "  None"
	    else
	      echo "Available machines (disable with '$p disable VM'):"
	      printf '  %s\n' "${active_sections[@]}"
	    fi
	    echo 
	    if [ ! -z "${inactive_sections[*]}" ]; then
	      echo "Disabled machines (enable with '$p enable VM')"
	      printf '  %s\n' "${inactive_sections[@]}"
	      echo 
	    fi
	    orphan=0
	    echo "Orphan machine files (not active nor running, purge with '$p purge VM'):"
      for file in "$qwdprefix/machines"/*; do
          [ -f "$file" ] || continue
          filename=$(basename "$file")
          # Check if filename is not in active or inactive lists
          if ! printf '%s\n' "${active_sections[@]}" "${inactive_sections[@]}" | grep -q "^${filename}$"; then
              orphan=1
              echo "  $filename"
          fi
      done
      if [ $orphan == 0 ]; then echo "  None"; fi
	    echo " "
	    echo "Running sessions:"
	    echo "session_ID:user:VM            | #cpu | #mem[MB]"
	    echo "------------------------------|------|---------"
	    t=$(ps aux | $GREP qemu-web-desktop)
	    name=$(echo "$t" | $GREP -oP '(?<=\-name )[^ ]*' )
	    cpu=$(echo "$t"  | $GREP -oP '(?<=\-smp )[^ ]*' )
	    mem=$(echo "$t"  | $GREP -oP '(?<=\-m )[^ ]*' )
	    table=$(printf '%s\n' "$name" "$cpu" "$mem" | pr -3 -T)
	    u=$(echo "$table" | uniq )
	    echo "$u"
	    ;;
# ------------------------------------------------------------------------------
	  --start|start)
	    # start the given VM or ISO file (full path) in a browser.
	  
	    # 2nd arg is the VM
	    if [ "x$2" = "x" ]; then
	      echo "Usage: $p $1 VM [options]"
	      echo "Configuration: $config_pl"
	      echo "Script:        $cgi"
	      echo "Availabe options are:"
	      $cgi -h
	      exit 1
	    fi
	    DIR=$(dirname $2)
	    DIR=$(realpath $DIR)
	    VM=$(basename $2)
	    shift 2
	    # snapshot_alloc_cpu snapshot_alloc_mem
	    echo "$p: Launching $DIR/$VM $@"
	    echo "Connect to (log in /tmp/${VM}.log):"
	    $cgi --dir_snapshots=/tmp --dir_cfg=/tmp --dir_machines=$DIR --machine=$VM --oneshot=1 --service_max_mem_fraction_nb_per_user=0.8 --service_max_cpu_fraction_nb_per_user=0.8 --snapshot_alloc_disk=30 $@ 2>&1 | tee /tmp/${VM}.log | awk '/URL:/ { print $4 }'
	    echo "Cleaning remaining sessions..."
	    $cgi --service_purge=1 --dir_snapshots=/tmp --dir_cfg=/tmp 2>&1 | $GREP -i "stop"
	    echo "Done $VM"
	    ;;
# ------------------------------------------------------------------------------
	  --stop|stop)
	    # stop sessions matching TOKEN. Some snapshot files may be left-over.
	  
	    if [ "x$2" = "x" ]; then
	      echo "$p: Stopping all local VMs"
	      $cgi --service_purge=1 --dir_snapshots=/tmp --dir_cfg=/tmp 2>&1 | $GREP -i "stop"
	      exit 0
	    fi
	    t=$(ps aux | $GREP qemu-web-desktop | $GREP $2)
	    name=$(echo "$t" | $GREP -oP '(?<=\-name )[^ ]*' )
	    pid=$( echo "$t" | awk '{ print $2 }' )
	    table=$(printf '%s\n' "$name" | pr -1 -Ts'\t')
	    u=$(echo "$table" | uniq )
	    echo "$p: Stopping:"
	    echo "$u"
	    kill $pid
	    ;; 
	    
# ------------------------------------------------------------------------------
	  --gpu|gpu|gpu-lock)
	    # configure GPU for pass-through. The GPU_ids are e.g. '10de:1d01'.
	      
	    GPU_ids=$2
      	
      # get CPU model
	    CPU_type=
	    $GREP -qi "intel"     /proc/cpuinfo && CPU_type=intel_iommu
	    $GREP -qi "amd"       /proc/cpuinfo && CPU_type=amd_iommu
	    if [ "x$CPU_type" = "x" ]; then
		    echo "$p: ERROR: GPU support can currently only be configured for AMD and Intel CPUs."
		    exit 1
	    fi
      	
      # list available GPUs
	    echo "$p: Available GPUs. The GPU_ids are usually shown as [vendor:model]"
	    GPU_avail=`lspci -nnv | $GREP -i "VGA\|3d controller"`
	    echo "$GPU_avail"
	    GPU_nb=$(echo "$GPU_avail" | wc -l)
	    if [ "$GPU_nb" -lt "2" ]; then
		    echo "$p: WARNING: you need at least 2 GPU models, and keep one for your display. PROCEEDING MAY LEAD TO NO-DISPLAY."
	    fi
	    echo "$p: NOTE: Make sure to keep one GPU for your current display."
	    if [ "x$GPU_ids" = "x" ]; then
		    read -p ">> Enter vendor:model ID e.g. 10de:1d01 (Ctrl-C to abort): " GPU_ids
	    fi
	    if [ "x$GPU_ids" = "x" ]; then
		    echo "Usage: $p gpu GPU_ids";
		    echo "  The GPU_ids should be e.g. '10de:1d01' or '10de:1d01,10de:1d01'"
		    exit 1
	    fi

	    GPU_firstID=`echo $GPU_ids | cut -d ',' -f1`
	    echo $GPU_avail | $GREP -q "$GPU_firstID" || GPU_avail=no
	    if [ "x$GPU_avail" = "xno" ]; then
		    echo "$p: ERROR: GPU $GPU_ids model not available."
		    exit 1
	    fi

	    # display message and wait for confirmation
	    echo "Ready to configure GPU $GPU_ids for $CPU_type (set VFIO pass-through)."
	    echo "All these GPU models will be detached from the server and made usable for virtualization."
	    echo "WARNING: Make sure you keep a display for your system."
	    echo "The following files will be modified ($DIST):"
	    echo "  /etc/default/grub      (GRUB_CMDLINE_LINUX_DEFAULT) update"
	    echo "  /etc/security/limits.conf                           append"
	    echo "  /etc/modprobe.d/vfio.conf                           create"
	    echo "  /etc/udev/rules.d/10-qemu-hw-users.rules            create"
	    if [ "x$DIST" = "xDebian" ]; then
	      echo "  /etc/initramfs-tools/modules                        append"
	      echo "  /etc/systemd/system/apache2.service.d/override.conf create"
	    else
	      echo "  /etc/mkinitcpio.conf                                append"
	      echo "  /etc/systemd/system/httpd.service.d/override.conf   create"
	      echo "  NOTE: require package 'mkinitcpio'"
	    fi
	    if [ -z "$QWDCTL_YES" ]; then
	      read -p ">> Continue (y/N)?" choice
	      case "$choice" in 
		      y|Y ) echo "Proceeding";;
		      *) echo "Aborting." && exit 1;;
	      esac
	    fi

	    # GRUB
	    FILE=$PREFIX/etc/default/grub
	    $GREP -i "^GRUB_CMDLINE_LINUX_DEFAULT" $FILE | $GREP "_iommu" && echo "$p; ERROR: $FILE seems to already contain IOMMU keyword. Please check content and/or clean above files. You may use: $p gpu_unlock" && exit 1
	    DATE=$(date +"%Y-%m-%d-%H-%M")
	    echo "$p: Updating  $FILE (old stored as $FILE.$DATE)"
	    cp $FILE $FILE.$DATE
	    sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT=\"/GRUB_CMDLINE_LINUX_DEFAULT=\"$CPU_type=on iommu=pt vfio-pci.ids=$GPU_ids /g" $FILE

	    # VFIO modules
	    FILE=$PREFIX/etc/modprobe.d/vfio.conf
	    echo "$p: Creating  $FILE"
	    dd status=none of=${FILE} << EOF
# $FILE
options vfio-pci ids=$GPU_ids disable_vga=1
EOF
	
	    # Initramfs modules
	    if [ "x$DIST" = "xDebian" ]; then
		    FILE=$PREFIX/etc/initramfs-tools/modules
		    MODULES=$(cat << EOF
# $FILE for qemu-web-desktop
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
vhost-netdev
EOF
        )
	    else
		    FILE=$PREFIX/etc/mkinitcpio.conf
		    if ! command -v mkinitcpio 2>&1 >/dev/null; then
	        echo "$p: ERROR: Missing mkinitcpio. Install it, and then retry."
	        exit 1
	      fi
		    MODULES=$(cat << EOF
# $FILE for qemu-web-desktop
MODULES+=(vfio vfio_iommu_type1 vfio_pci)
HOOKS+=(modconf)
EOF
        )
	    fi
	    # append only if token not found in FILE
	    echo "$p: Appending $FILE"
	    if [ -e $FILE  ]; then cp $FILE $FILE.$DATE; fi
	    # append only if token not found in FILE
	    $GREP -q "vfio_pci" "$FILE" || echo "$MODULES" >> "$FILE"

	    # udev Rules
	    FILE=$PREFIX/etc/udev/rules.d/10-qemu-hw-users.rules
	    if [ -e $FILE ]; then
		    echo "$p: WARNING:  $FILE is kept (already there)."
	    else
		    echo "$p: Creating  $FILE"
		    dd status=none of=${FILE} << EOF
# $FILE for qemu-web-desktop
SUBSYSTEM=="vfio", OWNER="root", GROUP="kvm"
EOF
	    fi
  
	    # limits: append at end
	    FILE=$PREFIX/etc/security/limits.conf
	    echo "$p: Appending $FILE"
	    LIMITS=$(cat << EOF
# $FILE for qemu-web-desktop
*    soft memlock 20000000
*    hard memlock 20000000
@kvm soft memlock unlimited
@kvm hard memlock unlimited
EOF
      )
	    # append only if token not found in FILE
	    $GREP -q "kvm soft memlock unlimited" "$FILE" || echo "$LIMITS" >> "$FILE"

	    # update apache2 mem allocation limit
	    if [ -e $PREFIX/etc/systemd/system/httpd.service ]; then
		    DIR=$PREFIX/etc/systemd/system/httpd.service.d/
	    else
		    DIR=$PREFIX/etc/systemd/system/apache2.service.d/
	    fi
	    mkdir -p $DIR
	    FILE=$DIR/override.conf
	    echo "$p: Creating  $FILE"
	    dd status=none of=${FILE} << EOF
# $FILE for qemu-web-desktop
[Service]
LimitMEMLOCK=infinity
EOF

	    echo "$p: Updating  GRUB boot, kernel modules, rules"
	    if [ "x$DIST" = "xDebian" ]; then
		    update-initramfs -u
		    update-grub
	    else
		    mkinitcpio -p linux
		    grub-mkconfig -o /boot/grub/grub.cfg || echo "WARNING: Failed to update GRUB"
	    fi
	    udevadm control --reload-rules
	    udevadm trigger
	    systemctl daemon-reload

	    # request to uncomment GPU section in index.html
	    echo "-----------------------------------------------------------------"
	    echo "Check the above files, then UNCOMMENT the GPU section in the file"
	    echo "  $index_html"
	    echo "You may use 'sudo $p edit web' to do so."
	    echo "To release the GPU back to the server use: 'sudo $p gpu_unlock'"
	    echo "Now please reboot to activate changes."
	    echo "-----------------------------------------------------------------"
	    ;;
	    
# ------------------------------------------------------------------------------
	  --gpu-unlock|gpu-unlock)
	    # unlock/re-attach all GPU's to server (uninstall pass-through).
	
	    echo "$p: Cleaning existing GPU virtualization (remove VFIO pass-through)."
	    echo "All detached/locked GPUs will be returned to the server."
	    echo "The following files will be modified:"
	    echo "  /etc/default/grub      (GRUB_CMDLINE_LINUX_DEFAULT) update"
	    echo "  /etc/security/limits.conf                           kept"
	    echo "  /etc/modprobe.d/vfio.conf                           remove"
	    echo "  /etc/udev/rules.d/10-qemu-hw-users.rules            remove"
	    if [ "x$DIST" = "xDebian" ]; then
      echo "  /etc/initramfs-tools/modules                        update"
      echo "  /etc/systemd/system/apache2.service.d/override.conf remove"
	    else
      echo "  /etc/mkinitcpio.conf                                update"
      echo "  /etc/systemd/system/httpd.service.d/override.conf   remove"
      echo "  NOTE: require package 'mkinitcpio'"
	    fi
	    if [ -z "$QWDCTL_YES" ]; then
	      read -p ">> Continue (y/N)?" choice
	      case "$choice" in 
		      y|Y ) echo "Proceeding";;
		      *) echo "Aborting." && exit 1;;
	      esac
	    fi
	    
	    # restore default GRUB options
	    FILE=$PREFIX/etc/default/grub
	    DATE=$(date +"%Y-%m-%d-%H-%M")
	    echo "$p: Updating  $FILE (old stored as $FILE.$DATE)"
	    cp $FILE $FILE.$DATE
	    sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT/#GRUB_CMDLINE_LINUX_DEFAULT/g" $FILE
	    echo "GRUB_CMDLINE_LINUX_DEFAULT=\"quiet splash\"" >> $FILE
	    
	    # comment modules
	    if [ "x$DIST" = "xDebian" ]; then
	      FILE=$PREFIX/etc/initramfs-tools/modules
	    else
	      if ! command -v mkinitcpio 2>&1 >/dev/null; then
	        echo "$p: ERROR: Missing mkinitcpio to proceed. Install it, and then retry."
	        exit 1
	      fi
	      FILE=$PREFIX/etc/mkinitcpio.conf
	    fi
	    if [ -e $FILE ]; then
		    echo "$p: Updating  $FILE (old stored as $FILE.$DATE)"
		    cp $FILE $FILE.$DATE
		    if [ "x$DIST" = "xDebian" ]; then
		      sed -i "s/^vfio/#vfio/g"                 $FILE
		      sed -i "s/^vhost-netdev/#vhost-netdev/g" $FILE
		    else
		      sed -i "s/^MODULES+=/#MODULES+=/g"       $FILE
		      sed -i "s/^HOOKS+=/#HOOKS+=/g"           $FILE
		    fi
	    fi

	    rm -f $PREFIX/etc/modprobe.d/vfio.conf
	    rm -f $PREFIX/etc/udev/rules.d/10-qemu-hw-users.rules
	    if [ "x$DIST" = "xDebian" ]; then
	    rm -f $PREFIX/etc/systemd/system/apache2.service.d/override.conf
	    else
	    rm -f $PREFIX/etc/systemd/system/httpd.service.d/override.conf
	    fi
	    
	    if [ "x$DIST" = "xDebian" ]; then
		    update-initramfs -u
		    update-grub
	    else
		    mkinitcpio -p linux
		    grub-mkconfig -o $PREFIX/boot/grub/grub.cfg  || echo "$p: WARNING: Failed to update GRUB"
	    fi
	    udevadm control --reload-rules
	    udevadm trigger
	    systemctl daemon-reload
	    
	    # request to uncomment GPU section in index.html
	    echo "-----------------------------------------------------------------"
	    echo "Check the above files, then COMMENT the GPU section in the file"
	    echo "  $index_html"
	    echo "You may use 'sudo $p edit web' to do so."
	    echo "Now please reboot to activate changes."
	    echo "-----------------------------------------------------------------"
	    ;;
# ------------------------------------------------------------------------------
    --enable|enable)
      # add/enable/uncomment a VM as a config.d/ini file.
      VM=$2
      if [ "$VM" = "" ]; then
        echo "ERROR: usage: $p enable VMNAME|FILE|URL {'DESCRIPTION'}"
        exit 1
      fi
      DESCR=$3
      b=$(basename $VM)
      if [ "$DESCR" = "" ]; then
        DESCR=$b
      fi
      FILE=
      
      # search for existing entry
      for F in $machine_conf $conf_d/*.ini $conf_d/*.conf; do
        if [ ! -f $F ]; then continue; fi
	      if $GREP -q "\[$VM\]" "$F"; then
	        # uncomment found $VM in $FILE
	        FILE=$F
          echo "$p: Uncomment [$VM] in $FILE"
          sed -i "/^;\[$VM\]/,/^\(\[.*\]\|;\[.*\]\)/{
    /^;\[$VM\]/s/^;//;
    /^;\(description\|url\)=/s/^;//;
}" "$FILE"
	      fi # found
      done
      
	    u=  # remote URL if relevant
      if [ -z "$FILE" ]; then
        # new file if does not exist
        FILE="$conf_d/$b.ini"
        # check for remote URL
        if [[ "$VM" =~ ^"http" ]]; then
          # identify distant VM given as "name.ext"
          u=$VM
          VM=$(basename $u)
        elif [-f $VM ]; then
          # copy local VM file
          echo "$p: Copy $VM into $qwdprefix/machines/"
          cp $VM $qwdprefix/machines/
          VM=$(basename $VM)
        fi
        echo "$p: Create $FILE"
        dd status=none of=${FILE} << EOF
; $FILE        	    
; This file lists virtual machines to be inserted in the qemu-web-desktop login page.
;
;  Supported virtual machine formats include: ISO, QCOW2, VDI, VMDK, RAW, VHD/VHDX, QED
;
;  Each entry in the configuration file '/etc/qemu-web-desktop/machines.conf'
;  spans on 3 lines:
;  -  [name.ext or FILEPATH or URL] 
;  -  url=[URL to virtual machine disk, optional]
;  -  description=[description to be shown in the service page] 
;
;  Images listed in the configuration file without a 'url=' parameter are
;  expected to be downloaded by hand and installed into
;  '/var/lib/qemu-web-desktop/machines' by the local administrator. Then, just 
;  specify the [name.ext] and description.
;
; Then run 'qwdctl download' or 'qwdctl refresh' commands.
[$VM]
description=$DESCR
EOF
        if [ "$u" ]; then
          # add URL
          echo "url=$u" >> $FILE
        fi
      fi
      $p download
      ;;
	
# ------------------------------------------------------------------------------
    --disable|disable)
      # remove (comment) a VM entry.
      VM=$2
      if [ "$VM" = "" ]; then
        echo "ERROR: usage: $p disable VM"
        exit 1
      fi
      # search for existing entry
      for F in $machine_conf $conf_d/*.ini $conf_d/*.conf; do
        if [ ! -f $F ]; then continue; fi
        echo "$p: Reading  $F" 
        b=$(basename $VM)
        v=
        if   $GREP -q "\[$b\]" "$F"; then
          v=$b
        elif $GREP -q "\[$VM\]" "$F"; then
          v=$VM
        fi
        if [ ! "$v" = "" ]; then
          echo "$p: Comment $v in $F"
          sed -i "/^\[$v\]/,/^\(\[.*\]\|;\[.*\]\)/{
    /^\[$v\]/s/^/;/;
    /^\(description\|url\)=/s/^/;/;
}" "$F"
        fi
      done
      $p download
      ;;
      
# ------------------------------------------------------------------------------
    --purge|purge)
      # clean unused and disabled VM files.
      VM=$2 # optional name of VM file to match
      
      # scan VM files in $qwdprefix/machines
      for F in $qwdprefix/machines/*; do
        b=$(basename $F)
        if [ ! -f "$F" ]; then continue; fi
        # when VM is given, we must only check when F == VM
        if [[ -n "$VM" && ! "$b" == "$VM"* ]]; then
          continue
        fi
        # continue $F is currently running
        t=$(ps -u _qemu-web-desktop -o comm= | $GREP -q "$b")
        if [ "$t" ]; then
          echo "$p: VM $b is running as: $t"
          continue
        fi
        # or $F if is active (not commented) in machine conf
        FOUND=no
        for C in $machine_conf $conf_d/*.ini $conf_d/*.conf; do
          if [ ! -f $C ]; then continue; fi
          l=$($GREP -v '^;' $C)
          if [[ "$l" == *"$b"* ]];then
            FOUND=yes
            echo "$p: VM $b is enabled in $C"
            continue
          fi
        done
        if [ "$FOUND" = "yes" ]; then
          continue
        fi
        # now remove $F
        if [ -z "$QWDCTL_YES" ]; then
	        read -p ">> Remove inactive $F (y/N)?" choice
	        case "$choice" in 
		        y|Y ) echo "Proceeding";;
		        *) echo "Keeping." && continue;;
	        esac
	      fi
	      rm -f $F
	    done
      ;;
      
# ------------------------------------------------------------------------------
	  --version|version|-v)
	    # show qemu-web-desktop version.
	  
	    $cgi -v
	    exit 1
	    ;;
# ------------------------------------------------------------------------------
	  --help|help|-h)
	    # show help.
	    
	    echo "usage: $p [help|download|refresh|status|start VM|stop|gpu|gpu-unlock|enable|disable|purge|edit] ..."
	    echo " "
	    echo "  controls the availability of virtual machines for the "
	    echo "  qemu-web-desktop/DARTS service."
	    echo "  In addition, the status of the running sessions can be displayed"
	    echo "  and it is possible to start virtual machines manually and display"
	    echo "  them in a browser."
	    echo 
	    echo "OPTIONS:"
	    echo "  --help|help|-h"
	    echo "      show this help."
	    echo 
	    echo "  --download|download|update"
	    echo "      scan the $machine_conf file for [name.ext] and download them when URL are given."
	    echo "      a 'refresh' is then performed. Virtual machine images are stored into $qwdprefix/machines."
	    echo "      Additional machine descriptions can be given in $conf_d/*.ini files."
	    echo " "
	    echo "  --edit|edit [machines|config|web]"
	    echo "      edit the VM/machine list, the service configuration file or the service web page."
	    echo "      In the case of the VM list, the '$p download' command is triggered automatically after edit."
	    echo "      Set the \$EDITOR variable to select the text editor to use."
	    echo "      In case the '$p edit machines' has no effect on the index.html service landing page,"
	    echo "      specify set 'QWDCTL_INSERT=yes' or option '--machine-insert'."
	    echo "      It is recommended to use the '--enable' and '--disable' to manage the VM/machine list."
	    echo 
	    echo "  --gpu|gpu VENDOR:MODEL"
	    echo "      configure GPU for pass-through. The GPU_ids are e.g. '10de:1d01'."
	    echo " "
	    echo "  --gpu-unlock|gpu-unlock"
	    echo "      unlock/re-attach all GPU's to server (uninstall pass-through)."
	    echo " "
	    echo "  --machines-insert"
	    echo "      used with download/refresh, force to modify the index.html service"
	    echo "      landing page instead of using an include."
	    echo "      Same as QWDCTL_INSERT=yes shell variable."
	    echo 
	    echo "  --refresh|refresh"
	    echo "      scan the $machine_conf file, and generate the $machine_html that lists"
	    echo "      available images to show in the qemu-web-desktop main form."
	    echo "      Additional machine descriptions can be given in $conf_d/*.ini files."
	    echo " "
	    echo "  --status|status"
	    echo "      list running sessions, as well as enabled and disabled machines."
	    echo " "
	    echo "  --start|start VM ..."
	    echo "      start the given VM or ISO file (full path) in a browser. Connect to it with"
	    echo "      the displayed URL. Further arguments are passed to the service (see config.pl)"
	    echo "      e.g. --snapshot_alloc_mem=1 (in GB) and --snapshot_alloc_cpu=2."
	    echo "      Changes are lost except when specifying option --snapshot_use_master=1,"
	    echo "      requiring write access. When running ISO's you may also specify"
	    echo "      --snapshot_alloc_disk=40 (in GB)."
	    echo "      Use '$p --start' to get the full list of default VM settings."
	    echo " "
	    echo "  --stop|stop TOKEN"
	    echo "      stop sessions matching TOKEN. Some snapshot files may be left-over."
	    echo " "
	    echo "  --enable|enable VMNAME|FILE|URL {'DESCRIPTION'}"
	    echo "      add or (re)activate a VM name.ext, file or URL. Optional description is given as a string."
	    echo 
	    echo "  --disable|disable VMNAME"
	    echo "      deactivate a VM."
	    echo 
	    echo "  --purge|purge {VMNAME}"
	    echo "      remove VM files which are inactive and not used. When VM argument is "
	    echo "      given, only files starting by 'VM' are checked."
	    echo 
	    echo "  --version|version|-v"
	    echo "      show $p version."
	    echo 
	    echo "  --yes|-y"
	    echo "      assume yes to questions. Same as QWDCTL_YES=1 shell variable."
	    echo 
	    echo "AUTHOR:"
	    echo "  Written by Roland Mas and Emmanuel Farhi."
	    echo 
	    echo "FILES:"
	    echo "  - $config_pl"
	    echo 
	    echo "  - $conf_d/*.pl"
	    echo 
	    echo "  - $machine_conf"
	    echo "  - $conf_d/*.ini"
	    echo "  Entries should contain lines:"
	    echo "    [name.ext or URL]"
	    echo "    description=<name of machine to appear in the form>"
	    echo "  In addition, any line with:"
	    echo "    url=<link>"
	    echo "  will retrieve the given file with: $p download"
	    echo "  When the description is missing, the VM file name is used."
	    echo "  Supported virtual machine formats: ISO, QCOW2, VDI, VMDK, RAW, VHD/VHDX, QED" 
	    echo 
	    echo "  - $qwdprefix/machines"
	    echo "  - $machine_html"
	    echo 
	    echo "  - /usr/share/qemu-web-desktop/html/desktop"
	    echo " "
	    echo "  - $index_html"
	    echo 
	    echo "  - https://gitlab.com/soleil-data-treatment/soleil-software-projects/qemu-web-desktop"
	    echo " "
	    echo "ENVIRONMENT VARIABLES:"
	    echo "  EDITOR            Set the text editor to use, e.g. nano. Default is to use 'editor' or \$EDITOR or 'edit'."
	    echo "  QWDCTL_INSERT=yes is equivalent to setting --machine-insert."
	    echo "  QWDCTL_YES=1      is equivalent to setting --yes (skip confirm)."
	    echo " "

	    exit 1
	    ;;
# ------------------------------------------------------------------------------
	  *)
	  
	    echo "Usage: $p [help|download|refresh|status|start VM|stop|gpu|gpu-unlock|enable VM|disable VM|purge|edit] ..."
	    exit 1
esac

# ------------------------------------------------------------------------------

