#!/bin/bash

function LockStartup()
{
  InitDirs			|| return 0 # only try locking
  cmdExists "flock" || return 0 # if dir and cmd exist

  exec 300>> "$TV_STARTLOG"
  flock -n 300
}

function UnlockStartup()
{
  flock -u -n 300
  exec 300<&-
}

function RequireNetwork()
{
  IsDaemonRunning && return

  echo "Starting network process (no daemon)"

  RunNetworkProcess
}
  
function RequireWineServer()
{
  # Automatic start of wineserver fails sometimes when spawning from daemon, thus make sure it is running beforehand.
  # For QS, wineserver must run before profile creation (or be patched)

  "$TV_BIN_DIR/wine/bin/wineserver"
}


function IsDaemonRunning()
{
  # Check if daemon is running - ignore for non-installed (TAR / TAR_QS)
  isInstalledTV || return 1

  exec &> /dev/null
  ps --no-heading -p $(cat "$TV_PIDFILE") | grep teamviewerd
}

function RunNetworkProcess()
{
  local subs
  local subPID
  local repeat=20

  # Start a network process
  trap Network_Signal SIGUSR1

  "$TV_BIN_DIR/teamviewerd" -n -f &
  subPID=$!

  # wait works, but could be entered too late
  until [ $repeat = 0 ]; do
    subs=$(jobs -r | wc -l)		# or: while subPID running

    if [ $subs = 0 ]; then		# network process quit (error or already running)
      echo "Network process already started (or error)"; break
    fi
    if [ -n "$TV_NET_STATE" ]; then	# signalled
      echo "Network process started ($subPID)"; break
    fi

    sleep 0.5
    let repeat-=1
  done
}

function Network_Signal()
{
  TV_NET_STATE='is_up'
}

function PatchPatchELF()
{
 ( # subshell for cd
  cd "$TV_RTLIB_DIR"

  if ! [ -e patchelf.org ]; then
    cp 'patchelf' 'patchelf.org' || die 'Copy patchelf failed'
  fi

  local rpath=$(LD_LIBRARY_PATH="$TV_RTLIB_DIR" "$TV_BINLOADER" "./patchelf.org" --print-rpath 'patchelf')
  local intrp=$(LD_LIBRARY_PATH="$TV_RTLIB_DIR" "$TV_BINLOADER" "./patchelf.org" --print-interpreter 'patchelf')

  if [ "$rpath" = "$TV_RTLIB_DIR" ] && [ "$intrp" = "$TV_BINLOADER" ]; then
    echo 'PatchElf: ok'
  else
    LD_LIBRARY_PATH="$TV_RTLIB_DIR" "$TV_BINLOADER" "./patchelf.org" --set-rpath "$TV_RTLIB_DIR" 'patchelf'       || die 'PatchPatchElf: failed'
    LD_LIBRARY_PATH="$TV_RTLIB_DIR" "$TV_BINLOADER" "./patchelf.org" --set-interpreter "$TV_BINLOADER" 'patchelf' || die 'PatchPatchElf: failed'
    echo 'PatchElf: patched'
  fi
 ) || return

  PATCHELF="$TV_RTLIB_DIR/patchelf"
}

# patch binaries to use shipped libraries/loader (patchelf)
function UpdateBinaries()
{
  isQuickSupport || return 0

  echo 'Updating binaries...'

  local bmark="$TV_CFG_DIR/rtldir"
  local bpath=$(cat "$bmark" 2> /dev/null)
  local ublog=$(UpdateBinaryLogfile)

  local binaries=(
		"$TV_BIN_DIR/teamviewerd"
		"$TV_BIN_DIR/TeamViewer_Desktop"
		"$TV_BIN_DIR/TVGuiSlave.32"
		"$TV_BIN_DIR/wine/bin/wine"
		"$TV_BIN_DIR/wine/bin/wineserver"
		"$TV_BIN_DIR/wine/drive_c/TeamViewer/tvwine.dll.so"
		"$TV_BIN_DIR/wine/bin/wine"
		"$TV_BIN_DIR/wine/lib/libwine.so.1.0"
		"$TV_BIN_DIR"/wine/lib/wine/*.*.so
		"$TV_RTLIB_DIR"/lib*.so*
		)

  if [ "$bpath" = "$TV_RTLIB_DIR" ]; then
    echo "Already up to date"
    return 0
  fi

  ( # subshell for redirecting output
    exec >> "$ublog" 2>&1

    #prepare patcher
    PatchPatchELF

    # update each binary
    for bin in "${binaries[@]}"; do
      [ -h "$bin" ] && continue
      UpdateBinary "$bin"
    done

    PrintBinaryPaths "${binaries[@]}"
  ) || return

  echo "$TV_RTLIB_DIR" > "$bmark" || return 1
  echo "Binaries patched"
}

function UpdateBinaryLogfile()
{
  local count=1
  local patrn="$TV_LOG_DIR/BinaryPatch%03i.log"
  local lfile
  
  while : ; do
    lfile=$(printf "$patrn" $count)
    [ -e "$lfile" ] || break;
    (( count++ ))
  done
  
  echo "$lfile"
}

function UpdateBinary()
{
  local bin="$1"
  local name=${bin##/*/}

  echo "Patching $name ..."
  PatchIntrp "$bin"
  PatchRPath "$bin"
}

function PatchRPath()
{
  local file="$1"
  local path="$TV_RTLIB_DIR"
  local name=${file##/*/}
  local orig=$("$PATCHELF" --print-rpath "$file" 2> /dev/null)

  if [[ "$orig" = *ORIGIN* ]] ; then
    path="$path:\${ORIGIN}/../lib"
  fi

  [ "$name" = tvwine.dll.so ] && path="$path:$TV_EXTENSION"

  [ "$orig" = "$path" ] && return

  printf "  orig: %s\n  path: %s\n" "$orig" "$path"

  "$PATCHELF" --set-rpath "$path" "$file" || die "PatchRPath $name failed"
}

function PatchIntrp()
{
  local file="$1"
  local path="$TV_BINLOADER"
  local name=${file##/*/}
  local orig=$("$PATCHELF" --print-interpreter "$file" 2> /dev/null)

  [ -z "$orig" ]        && return	# interpreter n/a
  [ "$orig" = "$path" ] && return	# interpreter ok

  "$PATCHELF" --set-interpreter "$path" "$file" || die "PatchInterp $name failed"
}

function PrintBinaryPaths()
{
  local binaries=("$@")

  printf '\nBinary paths:\n'

  for bin in "${binaries[@]}"; do
    [ -h "$bin" ] && continue
    PrintBinaryPath "$bin" || die 'PrintBinaryPath fail'
  done

  echo
}

function PrintBinaryPath()
{
  local bin="$1"
  local name=${bin##/*/}
  local rpath=$("$PATCHELF" --print-rpath "$bin")
  local intrp=$("$PATCHELF" --print-interpreter "$bin" 2> /dev/null)

  printf "%22s - %20s - %s\n" "$name" "$rpath" "$intrp"
}
