#!/bin/sh

FIRMWARE_DISTRO="vip-distro"
COM_DIAG_DISP_FIRM_DIST="dispfw"
INSTALL_DIRECTORY="/home/Dak/.update"
INSTALL_FILEPATH="$INSTALL_DIRECTORY/firmware.deb"
STOP_SERVICES_GUARD=/dev/shm/install_services_stopped
REBOOT="false"

HWLDID_5060=0x10
HWLDID_5050=0x13
HWLDID_5160=0x21
HWLDID_5150=0x23
HWLDID_5250=0x27

#need to set PATH variable for fcgi callers who have no environment
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin

LogInstallerInfo()
{
  INSTALL_START_TIME=$(date +%s)
  uptime | logger -p "user.notice" -t "INSTALL_START"

  PACKAGE_NAME=$(dpkg-deb -f "$INSTALL_FILEPATH" | grep "Package:" | cut -d: -f2 | tr -d ' ')
  PACKAGE_VERSION=$(dpkg-deb -f "$INSTALL_FILEPATH" | grep "Version:" | cut -d: -f2)
  logger -p "user.info" -t "INSTALL_info" "Detected installer: $PACKAGE_NAME"

  local installed_version=$(dpkg-query -W -f='${Version}' "$PACKAGE_NAME" 2>/dev/null)
  if [ -z "$installed_version" ]; then
    installed_version="not-installed"
  fi

  logger -p "user.info" -t "INSTALL_info" "Installed version: $installed_version, Installer version: $PACKAGE_VERSION"

  HWLDID="0x$(cat /proc/driver/processor/hwid)$(cat /proc/driver/processor/ldid)"
  logger -p "user.info" -t "INSTALL_info" "HWLDID: $HWLDID"

  LogDiskInfo "info"
  LogMemoryInfo "info"
}

LogDiskInfo() {
  local step_name=$1

  df -m / | awk '
    NR==2 {
      printf "DISK: Used=%dMB Avail=%dMB (%s)\n", $3, $4, $5
    }
  ' | logger -p "user.info" -t "INSTALL_$step_name"
}

LogMemoryInfo()
{
  local step_name=$1

  awk '
    /MemTotal:/     {total=$2}
    /MemFree:/      {free=$2}
    /^Cached:/      {cached=$2}
    /SReclaimable:/ {sreclaimable=$2}
    /AnonPages:/    {anon=$2}
    /Slab:/         {slab=$2}
    /Dirty:/        {dirty=$2}
    /Mlocked:/      {mlocked=$2}
    /Committed_AS:/ {committed=$2}
    /^Shmem:/       {shmem=$2}
    END {
      avail = free + cached + sreclaimable - shmem
      printf "MEMORY: Total=%dMB Available=%dMB (Free=%dkB Cached=%dkB SReclaimable=%dkB Shmem=%dkB)\n",
        total/1024, avail/1024, free, cached, sreclaimable, shmem

      printf "MEMORY Pinned: AnonPages=%dkB Slab=%dkB Mlocked=%dkB Dirty=%dkB Committed_AS=%dkB\n",
        anon, slab, mlocked, dirty, committed
    }
  ' /proc/meminfo | logger -p "user.info" -t "INSTALL_$step_name"
}

# Helper function to time steps and log performance metrics while keeping the UX progress output separate.
# To enable debugging with timing metrics:
#   (run_timed "step_name" command args...) 3>&1 | /usr/local/sbin/vpmplog -c
#   example: (run_timed "dpkg_install" dpkg -i "$INSTALL_FILEPATH") 3>&1 | /usr/local/sbin/vpmplog -c
run_timed() {
    local step_name=$1
    shift
    # 1. Run time on the command
    # 2. Redirect stdout (the progress for UX) to FD 3
    # 3. Filter performance metrics to logger:
    #       Maximum resident: The "high-water mark" of physical RAM usage (Physical truth for dpkg; Virtual mapping for apt).
    #           dpkg: Reliable measure of actual RAM needed for extraction.
    #           apt : Monitors the total "footprint" rather than unique bytes (don't need to have physically available)
    #       System/User time: Monitors CPU stress and computational overhead.
    #       Elapsed: Monitors real-world duration; reveals the "Niceness" delay.
    #       Page faults: Monitors memory thrashing; high counts predict BCH/IO instability.
    #       Context switches: Monitors "Politeness"; shows how often the update yielded to the Kernel/GC.
    local filter
    case "$step_name" in
      dpkg_install|apt_dist_upgrade)
        filter="Maximum resident|System time|User time|Percent of CPU|Elapsed|page faults|context switches"
        ;;
      *)
        filter="Maximum resident|Elapsed"
        ;;
    esac

    # sh -c redirects the command's stdout AND stderr to FD 3 (caller's vpmplog pipe)
    # 3>&- closes this process's copy of FD 3 to prevent child processes from inheriting it and keeping the vpmplog pipe open
    # time's own stderr stays on FD 2 → flows through the grep/logger pipe
    # The command's exit code is saved to /dev/shm/cmd_exit so callers can check it
    ( /usr/bin/time -v sh -c '"$@" >&3 2>&3 3>&-; echo $? > /dev/shm/cmd_exit' _ "$@" ) 2>&1 | grep -E "$filter" 3>&- | logger -p "user.info" -t "INSTALL_$step_name" 3>&-
    exec 3>&-

    LogDiskInfo "$step_name"
    LogMemoryInfo "$step_name"
}

InstallProgress()
{
  echo "$1" | /usr/local/sbin/vpmplog -c -prog
}

ErrorStatus()
{
  echo "$1" | /usr/local/sbin/vpmplog -c
  echo "$1" | /usr/local/sbin/vpmplog -c -err
  logger -p "user.err" -t "INSTALL" "$1"
}

InstallFile()
{
  if [ "$PACKAGE_NAME" = "$COM_DIAG_DISP_FIRM_DIST" ]; then
    # Skip the unsupported dispfw2.db to improve install time and reclaim flash space
    (dpkg -i --path-exclude=/usr/local/src/bitstreams/display-firmware/dispfw2.db "$INSTALL_FILEPATH" 2>&1; echo $? > /dev/shm/cmd_exit) | /usr/local/sbin/vpmplog -c
  else
    (dpkg -i "$INSTALL_FILEPATH" 2>&1; echo $? > /dev/shm/cmd_exit) | /usr/local/sbin/vpmplog -c
  fi

  rc=$(cat /dev/shm/cmd_exit 2>/dev/null || echo 255)
  if [ "$rc" != "0" ]; then
    ErrorExit "Install Failure! Incorrect or Corrupt firmware package (exit code: $rc)"
  fi
}

InstallCommonDiagnosticFirmware()
{
    if [ "$HWLDID" = "$HWLDID_5060" ] || [ "$HWLDID" = "$HWLDID_5050" ]; then
      StopServices # These have less RAM, optimize resources
    fi

    InstallFile

    if [ "$HWLDID" = "$HWLDID_5060" ] || [ "$HWLDID" = "$HWLDID_5050" ]; then
      RestartServices
    fi
}

DumpPackageList()
{
  dpkg -l | /usr/local/sbin/vpmplog -c
}

Sync2Repository()
{
  local pkgs_cnt=$(grep -c ".deb$" /usr/local/src/packages/lists/vip-distro_armel.list)
  logger -p "user.info" -t "INSTALL" "Repository Packages: $pkgs_cnt"

  local APT_LOCAL_REPO="-o Dir::Etc::SourceList=/etc/apt/sources.list.d/local.list -o Dir::Etc::SourceParts=none"

  (apt-get $APT_LOCAL_REPO update 2>&1; echo $? > /dev/shm/cmd_exit) | /usr/local/sbin/vpmplog -c
  rc=$(cat /dev/shm/cmd_exit 2>/dev/null || echo 255)
  if [ "$rc" != "0" ]; then
    ErrorExit "Install Failure! apt-get update error (exit code: $rc)"
  fi

  (apt-get $APT_LOCAL_REPO -f --force-yes install vip5060 -y 2>&1; echo $? > /dev/shm/cmd_exit) | /usr/local/sbin/vpmplog -c
  rc=$(cat /dev/shm/cmd_exit 2>/dev/null || echo 255)
  if [ "$rc" != "0" ]; then
    ErrorExit "Install Failure! apt-get install error (exit code: $rc)"
  fi

  (apt-get $APT_LOCAL_REPO -f --force-yes dist-upgrade -y 2>&1; echo $? > /dev/shm/cmd_exit) | /usr/local/sbin/vpmplog -c
  rc=$(cat /dev/shm/cmd_exit 2>/dev/null || echo 255)
  if [ "$rc" != "0" ]; then
    ErrorExit "Install Failure! apt-get dist-upgrade error (exit code: $rc)"
  fi

  (apt-get $APT_LOCAL_REPO autoremove --purge -y 2>&1; echo $? > /dev/shm/cmd_exit) | /usr/local/sbin/vpmplog -c
  rc=$(cat /dev/shm/cmd_exit 2>/dev/null || echo 255)
  if [ "$rc" != "0" ]; then
    ErrorExit "Install Failure! apt-get autoremove error (exit code: $rc)"
  fi
}

StopServices()
{
  # Only stop if services were not previously stopped
  [ -e "$STOP_SERVICES_GUARD" ] && return 0

  # Stop services to optimize resources/speed for the install process
  # VIP-50x0s have ~92MB total, VIP-51x0/5250s have ~219MB total

  log_statement="Stopping services..."
  logger -p "user.info" -t "INSTALL" "$log_statement"
  echo "$log_statement" | /usr/local/sbin/vpmplog -c

  # busmanagerd and syscontrol.fcgi are left running for the installation process
  # ordering of stops follows shutdown sequence (/etc/rc0.d)
  /etc/init.d/watchdog stop
  /etc/init.d/ledcio stop
  /etc/init.d/vsftpd stop
  /etc/init.d/atf.fcgi stop
  /etc/init.d/calibration.fcgi stop
  /etc/init.d/calibration2.0.fcgi stop
  /etc/init.d/commissioning.fcgi stop
  /etc/init.d/defaultcontent.fcgi stop
  /etc/init.d/diagnostics.fcgi stop
  /etc/init.d/diagnostics2.0.fcgi stop
  /etc/init.d/extdiag.fcgi stop
  /etc/init.d/imaging.fcgi stop
  /etc/init.d/opi.fcgi stop
  /etc/init.d/powercontrol.fcgi stop
  /etc/init.d/registration.fcgi stop
  /etc/init.d/selftest-fcgi stop
  /etc/init.d/oaaamonitoring stop
  /etc/init.d/cron stop
  /etc/init.d/inputwatchdog stop
  /etc/init.d/playerd stop
  if [ -f /run/php5-fpm.pid ]; then
    # /etc/init.d/php5-fpm doesn't handle stop correctly
    local pid
    pid="$(cat /run/php5-fpm.pid 2>/dev/null || true)"
    if [ -n "$pid" ] && echo "$pid" | grep -q '^[0-9][0-9]*$' && [ -r "/proc/$pid/comm" ] && grep -qE '^php5-fpm$' "/proc/$pid/comm"; then
        kill "$pid" >/dev/null 2>&1 || true
    fi
  fi
  /etc/init.d/powercontrol stop
  /etc/init.d/rescuebutton stop
  /etc/init.d/thermalmgrd stop
  /etc/init.d/discoveryclientd stop
  /etc/init.d/discoveryserverd stop
  /etc/init.d/ntpd stop
  /etc/init.d/sensorsd stop
  if [ "$PACKAGE_NAME" != "$COM_DIAG_DISP_FIRM_DIST" ]; then # leave diagmgrd running to preserve the diagnostic database for bootloading purposes
    /etc/init.d/diagmgrd stop
    rm -f /dev/shm/diagnostics.db
  fi
  /etc/init.d/dimmgrd stop
  /etc/init.d/dnswatchdogd stop
  /etc/init.d/pl56diag stop
  /etc/init.d/vcb2cfg stop
  /etc/init.d/ttabled stop

  # Dropping caches (using 1) clears out any "junk" data from the stopped apps to give the cleanest possible slate and not require the kernel to evict old data from the cache to reclaim space during the install.
  sync
  echo 1 > /proc/sys/vm/drop_caches

  LogMemoryInfo "stop_services"

  touch "$STOP_SERVICES_GUARD"
}

RestartServices()
{
  # Only start if services were previously stopped
  [ -e "$STOP_SERVICES_GUARD" ] || return 0

  local log_statement="Starting services..."
  logger -p "user.info" -t "INSTALL" "$log_statement"
  echo "$log_statement" | /usr/local/sbin/vpmplog -c

  # ordering of starts follows startup sequence (/etc/rc5.d)
  /etc/init.d/ttabled restart
  /etc/init.d/vsftpd restart
  if [ "$PACKAGE_NAME" != "$COM_DIAG_DISP_FIRM_DIST" ]; then
    /etc/init.d/diagmgrd restart
  fi
  /etc/init.d/ntpd restart
  /etc/init.d/sensorsd restart
  /etc/init.d/powercontrol restart
  /etc/init.d/thermalmgrd restart
  /etc/init.d/cron restart
  /etc/init.d/atf.fcgi restart
  /etc/init.d/calibration.fcgi restart
  /etc/init.d/calibration2.0.fcgi restart
  /etc/init.d/commissioning.fcgi restart
  /etc/init.d/defaultcontent.fcgi restart
  /etc/init.d/diagnostics.fcgi restart
  /etc/init.d/diagnostics2.0.fcgi restart
  /etc/init.d/extdiag.fcgi restart
  /etc/init.d/imaging.fcgi restart
  /etc/init.d/opi.fcgi restart
  if [ "$HWLDID" = "$HWLDID_5250" ]; then
      /etc/init.d/php5-fpm restart # for ECCB UX
  fi
  /etc/init.d/powercontrol.fcgi restart
  /etc/init.d/registration.fcgi restart
  /etc/init.d/selftest-fcgi restart
  /etc/init.d/discoveryserverd restart
  /etc/init.d/discoveryclientd restart
  /etc/init.d/oaaamonitoring restart
  /etc/init.d/pl56diag restart
  /etc/init.d/vcb2cfg restart
  /etc/init.d/dimmgrd restart
  /etc/init.d/dnswatchdogd restart
  /etc/init.d/inputwatchdog restart
  /etc/init.d/ledcio restart
  /etc/init.d/playerd restart
  /etc/init.d/rescuebutton restart
  /etc/init.d/watchdog restart

  log_statement="Finished Restarting Apps"
  logger -p "user.info" -t "INSTALL" "$log_statement"
  echo "$log_statement" | /usr/local/sbin/vpmplog -c

  rm -f "$STOP_SERVICES_GUARD"
}

VerifyInstall()
{
  logger -p "user.info" -t "INSTALL" "Verifying installation"
  /usr/local/sbin/vpmplog -v
}

Reboot()
{
  if [ "$REBOOT" = "true" ]; then
    /usr/local/sbin/vpmplog -r

    sync
    # Give the JFFS2 GC thread a moment to breathe
    # Also allows UX to get the reboot message, it polls at 5 seconds
    sleep 10
    sync
    reboot
  fi
}

CheckVipPackageVersion()
{
  dpkg --compare-versions "$PACKAGE_VERSION" ge "2022.03.22-b2416" # Min version required to maintain VCS connectivity due to certificate
  if [ "$?" != "0" ]; then 
    ErrorExit "Install Failure! $PACKAGE_VERSION is less than the minimum required version [2022.03.22-b2416] for VCS connectivity."
  fi

  dpkg --compare-versions "$PACKAGE_VERSION" le "2025.09.02-b2498" # This handles backdating, 2025.09.02-b2498 and less did not stop services in preinst
  if [ "$?" = "0" ]; then
    StopServices
  fi
}

InstallSetup()
{
  echo "fwinstall" > /dev/shm/installing

  #dpkg-deb requires this directory
  mkdir -p /tmp

  LogInstallerInfo

  #clean up any potentially unconfigured packages which will prevent new installs from occuring, also rotates vpmplog (no -c switch)
  dpkg --configure -a | /usr/local/sbin/vpmplog

  InstallProgress 10
}

InstallTeardown()
{
  logger -p "user.info" -t "INSTALL" "Cleaning up installation files"
  find "$INSTALL_DIRECTORY" -name "*.deb" -type f -delete
  rm -f /dev/shm/cmd_exit
  rm -f /dev/shm/installing
  InstallProgress 100

  LogDiskInfo "install_teardown"
  LogMemoryInfo "install_teardown"

  local elapsed=$(( $(date +%s) - INSTALL_START_TIME ))
  local minutes=$(( elapsed / 60 ))
  local seconds=$(( elapsed % 60 ))
  log_statement="Total install time: ${minutes}m ${seconds}s"
  logger -p "user.info" -t "INSTALL" "$log_statement"
  echo "$log_statement" | /usr/local/sbin/vpmplog -c

  uptime | logger -p "user.notice" -t "INSTALL_END"
}

ErrorExit()
{
  RestartServices
  ErrorStatus "$1"
  InstallTeardown
  exit 1
}

WriteSerialNumber()
{
  mkdir -p /home/Dak/.settings/
  cat /proc/driver/processor/mac | cut -b 7-12 > /home/Dak/.settings/serial#
}

case "$1" in
  start)
  #store the serial number for future consumption, this is the first of the user level run scripts so it makes sense to do it here
  WriteSerialNumber

  if [ -e "$INSTALL_FILEPATH" ]; then
    InstallSetup

    if [ "$PACKAGE_NAME" = "$FIRMWARE_DISTRO" ]; then
      CheckVipPackageVersion
      InstallProgress 25
      InstallFile
      InstallProgress 50
      Sync2Repository
      InstallProgress 75
      DumpPackageList
      InstallProgress 85
      VerifyInstall
      InstallProgress 95
      REBOOT="true"
    elif [ "$PACKAGE_NAME" = "$COM_DIAG_DISP_FIRM_DIST" ]; then
      InstallCommonDiagnosticFirmware
      /usr/local/sbin/prolinkbootload --diagdb /dev/shm/diagnostics.db --dispfwdb /usr/local/src/bitstreams/display-firmware/dispfw.db --auto
    else
      ErrorExit "Install Failure!"
    fi

    InstallTeardown
    Reboot
  fi
  ;;

  nobootload) #only update diagnostic interpreter
  if [ -e "$INSTALL_FILEPATH" ]; then
    InstallSetup

    if [ "$PACKAGE_NAME" = "$COM_DIAG_DISP_FIRM_DIST" ]; then
      InstallProgress 50
      InstallCommonDiagnosticFirmware
      InstallProgress 80
      VerifyInstall
    else
      ErrorExit "Install Failure!"
    fi

    InstallTeardown
  fi
  ;;

  *)
  echo "Usage: /etc/init.d/fwinstall {start|nobootload}"
  exit 1
  ;;
esac

exit 0
