#!/bin/bash
# License: GPL 
# Author: Aaron Burling <aaron_burling _at_ lkstevens wednet edu; burlingaaron _at_ gmail com> and Steven Shiau <steven _at_ clonezilla org>
# Description: Program to preload a tarball/zip file or copy files from cifs/nfs for live system from boot parameter "ocs_preload":
# Type 1, i.e. tarball/zip file:
# ocs_preload=[http|https|ftp|tftp|file]://[HOST_NAME_or_IP_ADD]/path/to/your_tarball_or_script
# Type 2, i.e. files on a cifs/nfs directory using mount command:
# ocs_preload="mount -t cifs //YOUR_CIFS_SERVER/path/to/ /path/to/mounting_point"
# ocs_preload="mount -t nfs YOUR_CIFS_SERVER:/path/to/ /path/to/mounting_point"
# Support file format: tar.gz, tgz, tar.bz2, tbz2, tar.xz, txz, zip, .sh
# Support network file system: cifs (samba) and nfs2/3/4
# E.g. You can put the following in the boot parameter:
#      ocs_preload=tftp://192.168.100.254/my-custom.tgz
#      ocs_preload=http://192.168.200.254/my-custom.tar.xz
#      ocs_preload=ftp://192.168.250.254/my-custom.zip
#      ocs_preload=file:///lib/live/mount/medium/my-custom.tar.bz2
#      ocs_preload=tftp://192.168.100.254/my-custom.sh
#      ocs_preload="mount -t cifs //192.168.120.2/images/ /tmp/cifs -o user=administrator,password=yourpasswd" 
#      ocs_preload4="mount -t nfs 192.168.120.254:/home/partimag/script/ /tmp/nfs -o ro" 
# Multiple ocs_preload* are avaiable, just append a number after that. E.g.
# ocs_prealod=... ocs_preload1=... ocs_preload2=...
# Then in Clonezilla live ocs-live-preload will be run automatically and the file assigned by ocs_preload will be downloaded and extracted to /opt. Its mode will be set automatically, too. i.e. set as mode 755 and Unix format script.

#
DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
. /etc/drbl/drbl-ocs.conf
. $DRBL_SCRIPT_PATH/sbin/ocs-functions

# Settings
cmdl_file_def="/proc/cmdline"
dest="/opt"
rm_tarball_flag=""
force_to_run="no"

# Functions
USAGE() {
    echo "$ocs - preload a tarball/zip file or copy files from cifs/nfs for live system from boot parameter \"ocs_preload\""
    echo "Usage:"
    echo "To run $ocs:"
    echo "$ocs [OPTION]"
    echo "Options:"
    echo "-c, --cmdline-file   Assign the kernel boot parameter file. If not assigned, \"$cmdl_file_def\" will be used."
    echo "-f, --force          Force to run $ocs, no matter it has been run successfully or not."
    echo "Type 1, i.e. for tarball/zip file:"
    echo "ocs_preload=[http|https|ftp|tftp|file]://[HOST_NAME_or_IP_ADD]/path/to/your_tarball_or_script"
    echo "Type 2, i.e. for files on a cifs/nfs directory using mount command:"
    echo "ocs_preload="mount -t cifs //YOUR_CIFS_SERVER/path/to/ /path/to/mounting_point""
    echo "ocs_preload="mount -t nfs YOUR_CIFS_SERVER:/path/to/ /path/to/mounting_point""
    echo "Support file format: tar.gz, tgz, tar.bz2, tbz2, tar.xz, txz, zip, .sh"
    echo "Support network file system: cifs (samba) and nfs2/3/4"
    echo "E.g. You can put the following in the boot parameter:"
    echo "     ocs_preload=tftp://192.168.100.254/my-custom.tgz"
    echo "     ocs_preload=http://192.168.200.254/my-custom.tar.xz"
    echo "     ocs_preload=ftp://192.168.250.254/my-custom.zip"
    echo "     ocs_preload=file:///lib/live/mount/medium/my-custom.tar.bz2"
    echo "     ocs_preload=tftp://192.168.100.254/my-custom.sh"
    echo "     ocs_preload="mount -t cifs //192.168.120.2/images/ /tmp/cifs -o user=administrator,password=yourpasswd" "
    echo "     ocs_preload4="mount -t nfs 192.168.120.254:/home/partimag/script/ /tmp/nfs -o ro" "
    echo "Multiple ocs_preload* are available, just append a number after that. E.g."
    echo "ocs_prealod=... ocs_preload1=... ocs_preload2=..."
    echo "Then in Clonezilla live $ocs will be run automatically and the file assigned by ocs_preload will be downloaded and extracted to /opt. Its mode will be set automatically, too. i.e. set as mode 755 and Unix format script."
} # end of USAGE
#
parse_cifs_mount_command() {
  # Function to parse cifs mount command to get the remote source and mount point, like:
  # ocs_preload="mount -t cifs //tech.drbl.org/clonezilla-samba-preload /ocs_preload_tmp -o username=jack,password=abcdefg"
  # or
  # ocs_preload="mount -o username=jack,password=abcdefg -t cifs //tech.drbl.org/clonezilla-samba-preload /ocs_preload_tmp"
  # We want to extract //tech.drbl.org/clonezilla-samba-preload and /ocs_preload_tmp
  # The return global variables are: $cifs_rmt_src and $cifs_mnt_pnt
  while [ $# -gt 0 ]; do
   case "$1" in
      mount|mount.cifs) shift;;
      -t) shift; shift;;  # -t cifs
      -o) shift; shift;;  # -o username=...,password=...
      -*) echo "${0}: ${1}: invalid option for parse_cifs_mount_command"
          echo "$msg_program_stop"
          [ "$save_restore_error_log" = "yes" ] && copy_error_log
          exit 2 ;;
      *)  break ;;
    esac
  done
  cifs_rmt_src="$1"
  cifs_mnt_pnt="$2"
} # end of parse_cifs_mount_command
#
parse_nfs_mount_command() {
  # Function to parse nfs mount command to get the remote source and mount point, like:
  # ocs_preload="mount -t nfs 192.168.120.254:/clonezilla-samba-preload /ocs_preload_tmp -o ro,nolock,nocto,actimeo=3600"
  # or
  # ocs_preload="mount -t nfs -o ro,nolock,nocto,actimeo=3600 192.168.120.254:/clonezilla-samba-preload /ocs_preload_tmp"
  # We want to extract 192.168.120.254/clonezilla-samba-preload and /ocs_preload_tmp
  # The return global variables are: $nfs_rmt_src and $nfs_mnt_pnt

  # mount.nfs or mount.nfs4:
  # mount.nfs/mount.nfs4 remotetarget dir [-rvVwfnsih] [-o nfsoptions]
  # options:
  # -r              Mount file system readonly
  # -v              Verbose
  # -V              Print version
  # -w              Mount file system read-write
  # -f              Fake mount, do not actually mount
  # -n              Do not update /etc/mtab
  # -s              Tolerate sloppy mount options rather than fail
  # -h              Print this help
  # nfsoptions      Refer to mount.nfs(8) or nfs(5)

  while [ $# -gt 0 ]; do
   case "$1" in
      mount|mount.nfs|mount.nfs4) shift;;
      -t) shift; shift;;
      -o) shift; shift;;
      -r|-v|-w|-n|-s) shift;;
      -*) echo "${0}: ${1}: invalid option for parse_nfs_mount_command"
          echo "$msg_program_stop"
          [ "$save_restore_error_log" = "yes" ] && copy_error_log
          exit 2 ;;
      *)  break ;;
    esac
  done
  nfs_rmt_src="$1"
  nfs_mnt_pnt="$2"
} # end of parse_nfs_mount_command
#
extract_tarball() {
  # rm_tarball_flag is global variable
  local url download_file local_f download_file_absp
  local ip rfile lfile rc=1
  url="$1"

  if [ -z "$url" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "No \"url\" assigned in function extract_tarball!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "$msg_program_stop!"
    return 1
  fi
  
  # Fetch the file
  # Part of the codes in the following are borrowed from live-boot package.
  download_file="$(basename ${url})"	
  case "$url" in
  	file://*)
      		local_f="$(echo $url | sed -e "s|file://||g")"
  		download_file_absp="${local_f}"
  		rm_tarball_flag="no" # keep it since it's local file
  		;;
  	tftp*)
  		ip="$(dirname $url | sed -e 's|tftp://||g' -e 's|/.*$||g')"
  		rfile="$(echo $url | sed -e "s|tftp://$ip||g")"
  		lfile="$(basename $url)"
  		echo "Trying busybox tftp -g -b 65464 -r $rfile -l ${dest}/$lfile $ip"
  		busybox tftp -g -b 65464 -r $rfile -l ${dest}/$lfile $ip
  		download_file_absp="${dest}/${lfile}"
  		rm_tarball_flag="yes" # remove it since it's a downloaded file
  		;;
  	*)
  		echo "Trying wget ${url} -O ${dest}/$(basename ${url})"
  		wget "${url}" -O "${dest}/${download_file}"
  		download_file_absp="${dest}/${download_file}"
  		rm_tarball_flag="yes" # remove it since it's a downloaded file
  		;;
  esac
  
  echo $msg_delimiter_star_line
  if [ -e "$download_file_absp" ]; then
  	echo "Putting $download_file_absp... to ${dest}/"
  	case "$download_file" in
  		*tar)           tar -xvf $download_file_absp -C ${dest}/
			        rc=$? ;;
  		*tar.gz|*tgz)   tar -xvzf $download_file_absp -C ${dest}/
			        rc=$? ;;
  		*tar.bz2|*tbz2) tar -xvjf $download_file_absp -C ${dest}/
			        rc=$? ;;
  		*tar.xz|*txz)   tar -xvJf $download_file_absp -C ${dest}/
			        rc=$? ;;
  		*.zip)          unzip -o $download_file_absp -d ${dest}/ 
			        rc=$?
				convert_dos_format_script_from_zip_file $download_file_absp;;
  		*.sh)           set_mode_755_and_unix_format $download_file_absp
			        rc=$?
				# keep it since it's will be used directly.
  				rm_tarball_flag="no" ;;
  		*)
  			[ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
  			echo "Unknown format for download file \"$download_file_absp\"".
  			[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  			echo "$msg_program_stop!"
  			return 1
  	esac
  	# Clean the tarball
  	if [ "$rm_tarball_flag" = "yes" ]; then
  		echo "Remove the downloaded file..."
  		rm -vf $download_file_absp
  	fi
  else
  	[ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
  	echo "Preload file not found! Perhaps ocs_preload failed?"
  	[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  	echo "$msg_program_stop!"
  	return 1
  fi
  
  echo "File(s) put in directory: ${dest}."
  echo ""
  return $rc
} # end of extract_tarball
#
replicate_files_from_netfs() {
  local netfs_mnt_cmd cifs_cp_flist_tmp rc=1
  netfs_mnt_cmd="$*"
  case "$netfs_mnt_cmd" in
  	mount*-t*cifs*|mount.cifs*)
                cifs_cp_flist_tmp="$(mktemp /tmp/cifs_cp_flist.XXXXXX)"
		cifs_rmt_src=""
		cifs_mnt_pnt=""
		parse_cifs_mount_command $netfs_mnt_cmd # Obtain cifs_rmt_src and cifs_mnt_pnt
		if [ -z "$cifs_rmt_src" -o -z "$cifs_mnt_pnt" ]; then
    		  [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
                  echo "Failed to parse $url to obtain Samba server or mounting point!"
    		  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
                  echo "$msg_program_stop!"
                  return 1
		fi
		echo "Creating Samba (CIFS) mounting point $cifs_mnt_pnt..."
		mkdir -p $cifs_mnt_pnt
		echo "Mounting Samba (CIFS) preload share $cifs_rmt_src: $netfs_mnt_cmd"
                eval $netfs_mnt_cmd
                echo "Copy Contents of Samba preload share to /opt ..."
                cp -rv $cifs_mnt_pnt/* /opt | tee $cifs_cp_flist_tmp
                rc="${PIPESTATUS[0]}"
                echo "Unmount Samba preload share..."
		umount $cifs_mnt_pnt
		convert_dos_format_script_from_cp_rv_list $cifs_cp_flist_tmp
		rm -f $cifs_cp_flist_tmp
  		;;
  	mount*-t*nfs*|mount.nfs|mount.nfs4)
                nfs_cp_flist_tmp="$(mktemp /tmp/nfs_cp_flist.XXXXXX)"
		nfs_rmt_src=""
		nfs_mnt_pnt=""
		parse_nfs_mount_command $netfs_mnt_cmd # Obtain nfs_rmt_src and nfs_mnt_pnt
		if [ -z "$nfs_rmt_src" -o -z "$nfs_mnt_pnt" ]; then
    		  [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
                  echo "Failed to parse $url to obtain NFS server or mounting point!"
    		  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
                  echo "$msg_program_stop!"
                  return 1
		fi
		echo "Creating NFS mounting point $nfs_mnt_pnt..."
		mkdir -p $nfs_mnt_pnt
		echo "Mounting NFS preload share $nfs_rmt_src: $netfs_mnt_cmd"
                eval $netfs_mnt_cmd
                echo "Copy Contents of NFS preload share to /opt ..."
                cp -rv $nfs_mnt_pnt/* /opt | tee $nfs_cp_flist_tmp
                rc="${PIPESTATUS[0]}"
                echo "Unmount NFS preload share..."
		umount $nfs_mnt_pnt
		convert_dos_format_script_from_cp_rv_list $nfs_cp_flist_tmp
		rm -f $nfs_cp_flist_tmp
  		;;
  esac
  return $rc
} # end of replicate_files_from_netfs
#
convert_dos_format_script_from_zip_file() {
  local zip_file="$1"
  local zip_flist i_exist
  zip_flist_tmp="$(mktemp /tmp/zflist.XXXXXX)"
  extract_flist_tmp="$(mktemp /tmp/extract_flist.XXXXXX)"
  if [ -z "$zip_file" ]; then
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo "No \"zip_file\" assigned in function convert_dos_format_script_from_zip_file!"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo "$msg_program_stop!"
    return 1
  fi
  # Sometimes zip will convert file name to lowercase, so we have to find it without case sensitive
  echo "Searching the shell script in ${dest} extracted from $zip_file for setting mode..."
  # File names in zip file might contain whitespace, like:
  # $ unzip -l Example.zip
  # Archive:  Example.zip
  #   Length      Date    Time    Name
  # ---------  ---------- -----   ----
  #        84  2016-05-17 08:57   a b c.sh
  #        97  2016-05-17 08:58   a-b-c.sh
  #        84  2016-05-17 08:47   Subfolder/a b c.sh
  #        97  2016-05-17 08:48   Subfolder/a-b-c.sh
  # ---------                     -------
  #       362                     4 files
  unzip -l $zip_file | tail -n +4 | head -n -2 | awk -F" " '{$1=$2=$3=""; print $0}' > $zip_flist_tmp
  find ${dest} -print > $extract_flist_tmp
  while read i; do
    i_exist="$(grep -Ei "^${dest}/${i}" $extract_flist_tmp)"
    if [ -n "$i_exist" ]; then
      if [ -n "$(LC_ALL=C file "$i_exist" | grep -i "shell script")" ]; then
         echo "Setting mode for \"$i_exist\" to 755..."
         set_mode_755_and_unix_format "$i_exist"
      fi
    fi
  done < $zip_flist_tmp
  rm -f $zip_flist_tmp $extract_flist_tmp
} # end of convert_dos_format_script_from_zip_file
#
convert_dos_format_script_from_cp_rv_list() {
  # chmod and conver to unix format for the copied files list
  # i.e. the files list got from: cp -rv source dest
  # The list is like (it might contain whitespace in file name):
  # '/tmp/cifs/my-script.sh.txt' -> '/opt/my-script.sh.txt'
  # '/tmp/cifs/your script.sh.txt' -> '/opt/your script.sh.txt'
  local cp_rv_flist="$1"
  local cp_flist_tmp kfl
  cp_flist_tmp="$(mktemp /tmp/cp_flist.XXXXXX)"
  LC_ALL=C awk -F"->" '{print $2}' $cp_rv_flist > $cp_flist_tmp
  while read kfl; do
    kfl="$(eval echo "$kfl")"
    if [ -n "$kfl" ]; then
      if [ -n "$(LC_ALL=C file "$kfl" | grep -i "shell script")" ]; then
         echo "Setting mode for \"$kfl\" to 755..."
         set_mode_755_and_unix_format "$kfl"
      fi
    fi
  done < $cp_flist_tmp
  rm -f $cp_flist_tmp
} # end of convert_dos_format_script_from_cp_rv_list

#################
##### MAIN ######
#################

ocs_file="$0"
ocs=`basename $ocs_file`
#
while [ $# -gt 0 ]; do
 case "$1" in
   -c|--cmdline-file)
      shift
      if [ -z "$(echo $1 |grep ^-.)" ]; then
        # skip the -xx option, in case 
        cmdl_file="$1"
        shift
      fi
      [ -z "$cmdl_file" ] && echo "-c is used, but no cmdl_file assigned." && exit 1
      ;;
   -f|--force) force_to_run="yes"; shift;;
   -*)     echo "${0}: ${1}: invalid option" >&2
           USAGE >& 2
           exit 2 ;;
   *)      break ;;
 esac
done

check_if_root
ask_and_load_lang_set

[ -z "$cmdl_file" ] && cmdl_file="$cmdl_file_def"
if [ ! -e "$cmdl_file" ]; then
  [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
  echo "Kernel cmdline file ($cmdl_file) does _NOT_ exist!"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  echo "$msg_program_stop!"
  exit 1
fi

#
ocs_preload_list="$(grep -Ewo "ocs_preload[[:digit:]]*" $cmdl_file | uniq | sort -V)"
ocs_preload_list="$(echo $ocs_preload_list)"  # in one line

if [ -z "$ocs_preload_list" ]; then
  exit 0
else
  echo "Found ocs_preload* parameter in boot parameters..."
  echo "The order to run: $ocs_preload_list"
fi

if [ -z "$ocs_preload_list" ]; then
	[ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
	echo "Boot parameter \"ocs_preload\" not found!"
	[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
	echo "$msg_program_stop!"
	my_ocs_exit 1
fi

# Prepare $dest in case it does not exist
mkdir -p ${dest}

# First we get the parameter "echo_ocs_preload" to see if
# the ocs_preload has to be echoed or not.
parse_cmdline_option -c $cmdl_file "echo_ocs_preload"

# Now start parsing the paramaters listed in $ocs_preload_list
RETVAL=0
for i in $ocs_preload_list; do
  parse_cmdline_option -c $cmdl_file "$i"
  eval iload=\$$i
  if [ -n "$iload" ]; then
    # Only when not force to run, we will check the tag file
    if [ "$force_to_run" = "no" ]; then
      # Checking if this program is already run
      if [ -e /var/lib/clonezilla/$i ]; then
        [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
	echo "This boot parameter has been successfully run before, so skip it: $i ($iload)"
        [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
        continue
      fi
    fi
    echo "**************************"
    # Process it
    if [ "$echo_ocs_preload" != "no" ]; then
      echo "Now process \"$i\": $iload"
    fi
    case "$iload" in
      mount*)  replicate_files_from_netfs $iload
	       rc=$?
	       RETVAL="$((RETVAL + $rc))" ;;
           *)  extract_tarball $iload
	       rc=$?
	       RETVAL="$((RETVAL + $rc))" ;;
    esac
    # Creating state file
    if [ "$rc" -eq 0 ]; then
      touch /var/lib/clonezilla/$i
    fi
  fi
done

exit $RETVAL
