#!/bin/bash
#Copyright 2005-2024, Lime Technology, Inc.
#License: GPLv2 only

# This is the 'mover' script used for moving files between pools and/or the main unRAID array.
# It is typically invoked via cron.

# The script is set up so that hidden directories (i.e., directory names beginning with a '.'
# character) at the topmost level of a pool or an array disk are not moved.  This behavior
# can be turned off by uncommenting the following line:
# shopt -s dotglob

# Files at the top level of a pool or an array disk are never moved.

# The 'find' command generates a list of all files and directories of a share.
# For each file, if the file is not "in use" by any process (as detected by 'in_use' command),
# then the file is moved, and upon success, deleted from the source disk.  If the file already
# exists on the target, it is not moved and the source is not deleted.  All meta-data of moved
# files/directories is preserved: permissions, ownership, extended attributes, and access/modified
# timestamps.

# If an error occurs in copying a file, the partial file, if present, is deleted and the
# operation continues on to the next file.

PIDFILE="/var/run/mover.pid"
DEBUGGING=""

move() {
  /usr/bin/find "$1" -depth 2>/dev/null | /usr/libexec/unraid/move $DEBUGGING
}

start() {
  if [ -f $PIDFILE ]; then
    if ps h $(cat $PIDFILE) | grep mover ; then
        echo "mover: already running"
        exit 1
    fi
  fi

  echo $$ >/var/run/mover.pid
  echo "mover: started"

  shopt -s nullglob

  for SHARECFG in /boot/config/shares/*.cfg ; do
    SHARE=$(basename "$SHARECFG" .cfg)
    source <(fromdos < "$SHARECFG")

    # maybe move from primary to secondary
    if [[ $shareUseCache = yes && -n $shareCachePool && -d "/mnt/$shareCachePool/$SHARE" ]]; then
      if [[ -n $shareCachePool2 ]]; then
        # secondary is a pool
        if [[ -d "/mnt/$shareCachePool2" ]]; then
          move "/mnt/$shareCachePool/$SHARE"
        fi
      else
        # secondary is the unRAID array, ensure at least one data disk is present
        disks=(/mnt/disk[1-9]/ /mnt/disk[1-9][0-9]/)
        if [[ ${#disks[@]} -gt 0 ]]; then
          move "/mnt/$shareCachePool/$SHARE"
        fi
      fi
    fi

    # maybe move from secondary to primary
    if [[ $shareUseCache = prefer ]]; then
      if [[ -n $shareCachePool2 ]]; then
        # secondary is a pool (primary is also a pool)
        if [[ -d "/mnt/$shareCachePool2/$SHARE" && -n $shareCachePool && -d "/mnt/$shareCachePool" ]]; then
          move "/mnt/$shareCachePool2/$SHARE"
        fi
      else
        # secondary is the unRAID array
        if [[ -n $shareCachePool && -d "/mnt/$shareCachePool" ]]; then
          for SHAREPATH in /mnt/disk[1-9]/"$SHARE"/ /mnt/disk[1-9][0-9]/"$SHARE"/ ; do
            move "$SHAREPATH"
          done
        fi
      fi
    fi
  done

  rm -f $PIDFILE
  echo "mover: finished"
}

empty() {
  if [ -f $PIDFILE ]; then
    if ps h $(cat $PIDFILE) | grep mover ; then
        echo "mover: already running"
        exit 1
    fi
  fi

  echo $$ >/var/run/mover.pid
  echo "mover: started"

  shopt -s nullglob

  for SHAREPATH in /mnt/$DISK/* ; do
    if [[ -d "$SHAREPATH" ]]; then
      find "$SHAREPATH" -depth 2>/dev/null | /usr/libexec/unraid/move -e $DEBUGGING
    fi
  done

  # output list of files which could not be moved
  shopt -s dotglob ; FILES=/mnt/$DISK/* ; shopt -u dotglob
  if [ ${#files[@]} -gt 2 ]; then
    echo "mover: not moved:"
    ls -1 --almost-all --recursive /mnt/$DISK
  fi

  rm -f $PIDFILE
  echo "mover: finished"
}

killtree() {
  local pid=$1 child
    
  for child in $(pgrep --ns $$ -P $pid); do
    killtree $child
  done
  [ $pid -ne $$ ] && kill -TERM $pid
}

stop() {
  if [ ! -f $PIDFILE ]; then
    echo "mover: not running"
    exit 0
  fi
  killtree $(cat $PIDFILE)
  sleep 2
  rm -f $PIDFILE
  echo "mover: stopped"
}

# Function to display usage
usage() {
    echo "Usage: $0 start [-e] <disk_name>"
    echo "       $0 stop|status"
    echo "       <disk_name> must match pattern 'disk[0-9]*'"
    exit 1
}

# validate disk name
validate_disk() {
    if [[ ! "$1" =~ ^disk[0-9]+$ ]]; then
        echo "Error: <disk_name> must match pattern 'disk[0-9]*'"
        usage
    fi
}

if [ "$#" -lt 1 ]; then
    usage
    exit 1
fi

case $1 in
start)
    if [ -z "$2" ]; then
        start
    elif [ "$2" == "-e" ]; then
        if [ -z "$3" ]; then
            usage
        else
            validate_disk "$3"
            DISK="$3"
            empty
        fi
    else
        usage
    fi
  ;;
stop)
  stop
  ;;
status)
  [ -f $PIDFILE ]
  ;;
*)
  usage
  ;;
esac
