#!/usr/bin/env ruby.ruby3.4

# Copyright 2025 Open Text
#
# ------------------------------------------------------------------------------
# The only warranties for products and services of Open Text and its
# affiliates and licensors (“Open Text”) are as may be set forth in the express
# warranty statements accompanying such products and services.  Nothing herein
# should be construed as constituting an additional warranty. Open Text shall not
# be liable for technical or editorial errors or omissions contained herein. The
# information contained herein is subject to change without notice.
#
# Except as specifically indicated otherwise, this document contains
# confidential information and a valid license is required for possession, use or
# copying. If this work is provided to the U.S. Government, consistent with FAR
# 12.211 and 12.212, Commercial Computer Software, Computer Software
# Documentation, and Technical Data for Commercial Items are licensed to the U.S.
# Government under vendor's standard commercial license.
# ------------------------------------------------------------------------------

# (C) Copyright 1995 - 2022 Micro Focus or one of its affiliates.
 
# The only warranties for products and services of Micro Focus and its affiliates and licensors
# (“Micro Focus”) are as may be set forth in the express warranty statements accompanying such
# products and services. Nothing herein should be construed as constituting an additional
# warranty. Micro Focus shall not be liable for technical or editorial errors or omissions contained
# herein. The information contained herein is subject to change without notice.
 
# Except as specifically indicated otherwise, this document contains confidential information
# and a valid license is required for possession, use or copying. If this work is provided to the
# U.S. Government, consistent with FAR 12.211 and 12.212, Commercial Computer Software,
# Computer Software Documentation, and Technical Data for Commercial Items  are licensed
# to the U.S. Government under vendor's standard commercial license.
 
# Copyright (C) [2007-2009] Novell, Inc.  All Rights Reserved.

# THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
# IT MAY NOT BE USED, COPIED, DISTRIBUTED, DISCLOSED, ADAPTED, PERFORMED,
# DISPLAYED, COLLECTED, COMPILED, OR LINKED WITHOUT NOVELL'S PRIOR WRITTEN
# CONSENT.  USE OR EXPLOITATION OF THIS WORK WITHOUT AUTHORIZATION COULD
# SUBJECT THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.

# NOVELL PROVIDES THE WORK "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,
# INCLUDING WITHOUT THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE, AND NON-INFRINGEMENT. NOVELL, THE AUTHORS OF THE WORK,
# AND THE OWNERS OF COPYRIGHT IN THE WORK ARE NOT LIABLE FOR ANY CLAIM, DAMAGES,
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
# ARISING FROM, OUT OF, OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER
# DEALINGS IN THE WORK.

require 'rubygems'
require 'mig'
require 'logger'
require 'date'

APP="migration"
VERS="0.1.0"

GEM_PATH=Gem.path
GEM_BIN="#{GEM_PATH}/gems/#{APP}-#{VERS}/bin"

include Migration
NBACKUP = "/opt/novell/sms/bin/nbackup"
SOURCE_PATH_FILE = "/tmp/sourcePathListFile"
ROOT_USER = "root"

def writeSessionFile
  File.open(@opt['c'], File::CREAT|File::WRONLY|File::TRUNC, 0600) { |fd|
    fd.write($session + "  status: stopped\n  stopped-at: #{$lastChkPt}\n  Files Processed: #{@filesCopied}\n")
    fd.close
  }
end

def convertFullPathToColonPath(filename)
  rpath = filename.slice(@mntpt.size+1, filename.size)
  if rpath != nil and rpath != ""
    rpath.strip!
    if rpath[0,1] == "/" or rpath[0,1] == ":"
      rpath = rpath.slice(1, rpath.length)
    end
    if rpath[rpath.length-1, rpath.length] == "\""
      rpath = rpath.slice(0, rpath.length-1)
    end
    colonPath = @srcVol + ":" + rpath
    print_message(DEBUG, "colonpath = #{colonPath}")
    return colonPath
  else
    return filename
  end
end

def convertNameToFDN(name)
  return @myHash[name] if @myHash[name] != nil

  names = name.split(".")

  last = names.last
  if @myHash[last] != nil
    fdnLast = @myHash[last]
  else
    if (ldap_exist?(@opt['s'], @ldapDN, "o=#{last}", @useSecure_src, @opt['source-ldap-port']))
      @myHash[last] = fdnLast = "o=#{last}"
    elsif (ldap_exist?(@opt['s'], @ldapDN, "c=#{last}", @useSecure_src, @opt['source-ldap-port']))
      @myHash[last] = fdnLast = "c=#{last}"
    elsif (ldap_exist?(@opt['s'], @ldapDN, "st=#{last}", @useSecure_src, @opt['source-ldap-port']))
      @myHash[last] = fdnLast = "st=#{last}"
    elsif (ldap_exist?(@opt['s'], @ldapDN, "l=#{last}", @useSecure_src, @opt['source-ldap-port']))
      @myHash[last] = fdnLast = "l=#{last}"
    else
      print_message(WARN, "could not find mapping for #{last} for #{name}")
      return ""
    end
  end
  if names.length == 1
    return fdnLast
  else
    #remove the last node
    names.delete_at(names.length-1)
  end
  fdn = fdnLast
  names.reverse_each { |elem|
    if (ldap_exist?(@opt['s'], @ldapDN, "cn=#{elem},#{fdn}", @useSecure_src, @opt['source-ldap-port']))
      fdn = "cn=#{elem}," + fdn
    elsif (ldap_exist?(@opt['s'], @ldapDN, "ou=#{elem},#{fdn}", @useSecure_src, @opt['source-ldap-port']))
      fdn = "ou=#{elem}," + fdn
    elsif (ldap_exist?(@opt['s'], @ldapDN, "o=#{elem},#{fdn}", @useSecure_src, @opt['source-ldap-port']))
      fdn = "o=#{elem}," + fdn
    elsif (ldap_exist?(@opt['s'], @ldapDN, "st=#{elem},#{fdn}", @useSecure_src, @opt['source-ldap-port']))
      fdn = "st=#{elem}," + fdn
    elsif (ldap_exist?(@opt['s'], @ldapDN, "l=#{elem},#{fdn}", @useSecure_src, @opt['source-ldap-port']))
      fdn = "l=#{elem}," + fdn
    else
      print_message(WARN, "could not find mapping for #{elem} with fdn #{fdn} for #{name}")
      return ""
    end
  }
  @myHash[name] = fdn
  return fdn
end

def doCmd cmd

  # To support NCP as source, nbackup should execute with the root credentials. So setting the ENV variable to 
  # root user password if source is NCP. This is to provide fix for bug #831590
  tempRemotePwd = ENV["SMS_REMOTE_PASSWORD"]
  if $isSrcNCP
    ENV['SMS_REMOTE_PASSWORD'] = $remoteRootPwd
  end

begin
  flag = false
  Open3.popen3(cmd) do | input, output, err |
  cnt = 0
  filenameSave = ""
  t = Thread.new {
    while 1
      line = err.gets
      if line.nil?
        break
      end
      if (line.index("Processed") == 0)
	if @opt['c']
	  if line =~ /(\d+) of (\d+)/
	    totalFilesCopied = @filesProcessed.to_i + $2.to_i if flag == false
	    @filesCopied = @filesProcessed.to_i + $1.to_i
	    flag = true
	    print_message(PROGRESS, "Processed #{@filesCopied} of #{totalFilesCopied} files")
	  end
	else
	  print_message(PROGRESS, line)
	end
      elsif line.include?("precomputing for displaying progress")
	print_message(INFO, line)
      elsif line.include?("Total dataSets = ")
        print_message(INFO, line)
      elsif line.include?("nbackup: source server codepage is missing use 'usecodeset' option")
        print_message(FATAL, "Source server is NetWare 6.0. Please load latest smsut.nlm and tsafs.nlm on source server")
        exit 1
      elsif line.include?("nbackup: Fail over / Fail back...")
        print_message(ERROR, "Resource Fail over / Fail back...")
      elsif line.include?("nbackup: reconnecting...")
        if not @opt['continue-after-failover']
          #reconnection is supported only if -c  and continue-after-failover is specified 
          #Cannot restart, so just exit
          print_message(ERROR, "Cluster resource appears to have migrated. Cannot continue as option continue-after-failover is not provided")
          $nbackup_command.gsub!("\"", "")
          lcAllCmd, $nbackup_command = $nbackup_command.split(' ', 2)
          killAllnbackups($nbackup_command, true)
          @isFailOver = false
        end
      elsif line.include?("Connection denied")
        print_message(FATAL, "nbackup command failed, #{line}")
        exit 1
      elsif line != "" and line != "\n"
        print_message(ERROR, line)
      end
    end #while
  }
  #Parent
  arr = []
  outcnt = 0
  while 1
    if not @isFailOver
      # This will be false only when user wants migration to be stopped in case of a failover
      return
    end
    line = output.gets
    if line.nil?
      break
    end
    #If standard output has SMDR debugging statements then dont process and write to file
    #This fix is provided for bug #777005
    if line.include? "SMDR Debug Log" or line.include? "SMDR:###SMSDEBUG###"
 	next
    end
    if line != ""
      if line.include?("- file:")
        # New file details are being printed.. make sure we do not print the file which has [Supervisor], [Public] as 
        # trustee/modifier/owner. To get this done we need to build a array which has all info of a single file
        if not arr.empty? 
          # We have some info to print.. print it
          # Here this condition will take care of the case where we file with owner [supervisor]
          # and some valid trustee or anything else
          if arr.length > 1
            puts "---" if outcnt == 0
            puts arr
            outcnt = outcnt + 1
            # Here note that one case is not handled where trustee is a supervisor/public. In this case
            # mls output may contain something like this: (This will be ignored by maprights and maptrustees)
            # -file: file1
            #  rights: RF
          end
          arr = []
        end
        if outcnt % NO_OF_OBJECTS_PER_RECORD == 0 and outcnt != 0
          puts "---"
        end
        #Force it to UTF-8, throwing out invalid bits in below code line
        line.encode!("UTF-8", undef: :replace, invalid: :replace, replace: "")
        filename = line.sub("- file: ","")
        # If it is a full path and source is LINUX, then change full path
        # to vol:rpath
        if not filename.include?(":") and $isSrcLinux
          filename = convertFullPathToColonPath(filename)
          filename.strip! if filename != nil and filename != ""
          line = "- file: '#{filename.gsub("'", "''")}'"
        end
        line = "- file: #{filename.gsub(/[^[:print:]]/i, '')}"
        if filename != filenameSave
          last = filenameSave
          filenameSave = filename
        end
        cnt = cnt + 1
      end
      if ($isSrcLinux and line.include?("trustee:") and not line.include?("="))
        splitLine = line.split(":")
        fdn = convertNameToFDN(splitLine[1].lstrip.strip)
        next if fdn == ""
        line = "#{splitLine[0]}: '#{fdn.gsub("'", "''")}'"
      end
      if line.include?"[Supervisor]" or line.include?"[Public]"
        # Do not include it in array. Just skip it.
        next
      end
      arr << line
      #$stdout.puts line
    end
    if @opt['c'] != nil and cnt % 10 == 0 and last != ""
      $lastChkPt = "#{last}"
      $lastChkPt.strip!
      print_message(DEBUG, "Checkpoined at #{last}")
      if cnt % 20 == 0
        writeSessionFile
      end
    end
  end #while
  # If arr has something print it and make sure its length is greater than 1 (This will make sure we not printing only file name)
  if not arr.empty? and arr.length > 1
    puts arr
  end
  t.join
  end #Open3

  # Setting back the SMS_REMOTE_PASSWORD to the original password it had. This is to provide fix for bug #831590
  ENV['SMS_REMOTE_PASSWORD'] = tempRemotePwd

  rescue Errno::EIO
    #apparently, it randomly gives this for no reason
    # Setting back the SMS_REMOTE_PASSWORD to the original password it had. This is to provide fix for bug #831590
    ENV['SMS_REMOTE_PASSWORD'] = tempRemotePwd
  end
  $complete = true
end
def validateArgs()
  if not @opt['V'] and not @opt['X']
    print_message(FATAL, "Need to specify either of --source-path(-V) or --source-full-path(-X) option")
    exit 1
  end
  if @opt['V'] and @opt['X']
     print_message(FATAL, "You cannot specify --source-path(-V) and --source-full-path(-X) options together")
    exit 1
  end
  if not @opt['progress'] and @opt['progress-interval']
    print_message(FATAL, "--progress-interval needs --progress switch")
    printUsage($argdef)
    exit 1
  end
end

#Boolean variable to hold whether source is NCP or not. This is introduced to provide fix for bug #831590
$isSrcNCP = false
def main 
  $argdef = [
    ['s', 'source-server', "indicates the source server's IP address", ParseArgText, true],
    ['V', 'source-path', "indicates the volume or directory path to use on the source server", ParseArgText],
    ['X', 'source-full-path', "specifies the full path of the volume to be used on source server", ParseArgText],
    [nil, 'continue-after-failover', "specifies that mls should continue after a cluster resource failover", ParseArgBool],
    [nil, 'use-casa', "use CASA to store/retrieve username and password information", ParseArgBool],
    [nil, 'source-unsecure-ldap', "use unsecure LDAP for all LDAP commands", ParseArgBool],
    [nil, 'source-ldap-port', "port where LDAP server is listening", ParseArgText],
    [nil, 'usecodeset', "codepage of NetWare 5.1 source server", ParseArgText],
    ['c', "session-file", "stores the command's progress, this file can be used to continue the command", ParseArgText],
    ['e', 'exclude', "exclude filter on files to be scanned", ParseArgMult],
    [nil, 'progress', "display progress in terms of percentage completed", ParseArgBool],
    [nil, 'progress-interval', "time interval for displaying progress", ParseArgText],
    [nil, 'modified-after', "scan files which are modified after this date", ParseArgText],
    [nil, 'modified-before', "scan files which are modified before this date", ParseArgText],
    [nil, 'accessed-after', "scan files which are accessed after this date", ParseArgText],
    [nil, 'accessed-before', "scan files which are accessed before this date", ParseArgText],
    [nil, 'no-dirquotas', "exclude directory quotas while scanning", ParseArgBool],
    [nil, 'no-userquotas', "exclude user quotas while scannig", ParseArgBool],
    [nil, 'precheck', "checks whether system meets all pre-requisite to start mls", ParseArgBool],
    [nil, 'debug', "generate debug log", ParseArgBool],
   #['e', 'effective-rights', "list effective rights on all source server files", ParseArgBool],
    ParseArgHelp,
    ParseArgUsage
  ]

  mlsCommand = "#{$0}"
  debugOpt = false
  $*.each do |arg|
    if arg.strip == "--debug"
      debugOpt = true
    end
      mlsCommand += " #{arg}"
  end
  initLog(debugOpt, "mls")
  print_message(DEBUG, "mls command started at #{`date +%d-%m-%y\\ %H:%M:%S`}")
  print_message(DEBUG, "mls command executed as: #{mlsCommand}")

  @opt = {}
  (@opt, extraargs) = parseargs(ARGV, $argdef)
 
  validateArgs()
  @opt['use-casa'] = true if ENV["MIG_USE_CASA"] != nil

  if not checkSMDRStatus()
    print_message(FATAL, "Ensure smdrd is running and tsafs is loaded")
    exit 1
  end
  
  # Do not append any space at the end while adding options to srcFlags
  srcFlags = "-lF"

  srcFlags += " --progress" if @opt['progress']
  srcFlags += " --progress-interval #{@opt['progress-interval']}" if @opt['progress-interval']
  if @opt['e']
    if @opt['e'].class == Array
      @opt['e'].each { |i| srcFlags += " --exclude-file=#{i}" }
    else	
      srcFlags += " --exclude-file=#{@opt['e']}"
    end
  end

  localIp = getLocalIPAddress()
  if localIp == nil
    print_message(FATAL, "Failed to get local server's IP address details")
    exit 1
  end

  # To support NCP as source, nbackup should execute with the root credentials. So to execute nbackup command with root credentials when source is NCP, 
  # we are receiving root password (from miggui) of source server through env variable. This is to provide fix for bug #831590
  $remoteRootPwd = ENV["SOURCE_USER_PWD"]

  credentialDetails = {}
  print_message(DEBUG, "Calling function getCredentialDetails")
  credentialDetails = getCredentialDetails(@opt['P'], @opt['s'], nil)
  @ldapDN = "\"#{credentialDetails['ldapUserDN']}\""
  ENV['OES_DATA'] = credentialDetails['source-password']
  ENV['SMS_REMOTE_PASSWORD'] = ENV['OES_DATA']

  source = @opt['s']
  credentials = nil
  $isSrcLinux = is_src_linux?(@opt['s'], "\"#{credentialDetails['user-name']}\"")
  if not $isSrcLinux and @opt['X']
    print_message(FATAL, "you cannot specify --source-full-path(-X) option if the source server is NetWare")
    exit 1
  end
  passCreatedDateFilters = true
  if isSource51? @opt['s'], "\"#{credentialDetails['source-username']}\""
    passCreatedDateFilters = false
    # We need to check the codepage in which tsafs is loaded in destination is same as source code page
    if @opt['usecodeset']
      same = chktsafsCodeSet(@opt['usecodeset'])
      if (same[0] == false)
        print_message(FATAL, "Source server is NetWare 5.1 and tsafs on the destination is not loaded with right codeset page. For migration to work correctly, use the --usecodeset option of tsafs. Refer to man page of tsafs for more details")
        print_message(DEBUG, "chktsafsCodeSet returned #{same[1]}")
        exit 1
      else
        print_message(DEBUG, "Destination server's tsafs is loaded with code page #{@opt['usecodeset']}")
      end
    else
        print_message(FATAL, "Source server is NetWare 5.1. Need to specify --usecodeset option. Also ensure tsafs is loaded with \"--usecodeset=<source codeset>\" if code set is non-English. See man page of tsafs for more details")
	exit 1
    end
  elsif isSource60? @opt['s'], "\"#{credentialDetails['source-username']}\""
	print_message(INFO, "Source server is NetWare 6.0. Make sure you load latest tsafs.nlm on the source server.")
  else
    # If it not NW5.1 then we should check remote TSAFS version. If its minor version is not 
    # greater then 0 we will display warning and continue
    # Form a nbackup command which should bring us the info about remote TSAFS version
    if @opt['usecodeset']
      print_message(FATAL, "--usecodeset option can be specified only for NetWare 5.1 source server")
      exit 1
    end
    # If it not NW5.1 then we should check remote TSAFS version. If its minor version is not
    # greater then 0 we will display warning and continue
    # Form a nbackup command which should bring us the info about remote TSAFS version
    nbackupCmdForVersion = "#{NBACKUP} -U \"#{credentialDetails['source-username']}\" -R #{@opt['s']} --list-tsa-version"
    print_message(DEBUG, "nbackup command executed as #{nbackupCmdForVersion}")
    majorVersion, minorVersion = checkTSAFSVersion(nbackupCmdForVersion)
    if majorVersion == -1 or minorVersion == -1
      print_message(FATAL, "Failed to get version information of source TSAFS")
    elsif $isSrcLinux and (majorVersion < 4 or minorVersion < 1)
      print_message(WARN, "tsafs on source #{@opt['s']} is not latest. Please update it to latest")
    elsif majorVersion < 3 or minorVersion < 1
      print_message(WARN, "tsafs on source #{@opt['s']} is not latest. Please update it to latest")
    end
    print_message(DEBUG, "Major version of source tsafs is #{majorVersion}")
    print_message(DEBUG, "Minor version of source tsafs is #{minorVersion}")
  end

  @useSecure_src = true
  @useSecure_src = false if @opt['source-unsecure-ldap']

  createLdapConfFile()

  excludePath = ""
  @srcVol = nil
  if $isSrcLinux
    passCreatedDateFilters = false
    volMap = getVolumesAndMountPoints(credentialDetails, @opt)
    print_message(DEBUG, "Calling function verifyAndFixSrcPath for source path")
    @srcVol, @mntpt, srcPath = verifyAndFixSrcPath(volMap, @opt)
    # To support NCP as source, nbackup should execute with the root credentials. So to 
    # identify the source volume type, the below method call has been added. This is to provide fix for bug #831590
    returnValue = verifyAndReturnSrcType(volMap, @opt, false)
    if returnValue != false
      $isSrcNCP = returnValue
    end
    if srcPath == nil and @srcVol == nil and @mntpt == nil
      print_message(FATAL, "Source path validation failed for path #{@opt['V']}") if @opt['V']
      print_message(FATAL, "Source path validation failed for path #{@opt['X']}") if @opt['X']
      exit 1
    end
    excludePath << @mntpt.chomp('/') + '/' + "._NETWARE" if srcPath == @mntpt
  else
    # That means source is NetWare and we have @opt['V'] to deal with
    @srcVol, extraPath = @opt['V'].split(":")
    srcPath = @opt['V']
    srcPath = srcPath + ":" if not srcPath.include?(":")
    if not checkNFSLoadedOnSource(@opt['s'], @srcVol, "\"#{credentialDetails['source-username']}\"")
      print_message(FATAL, "NFS namespace is not loaded for the volume #{@srcVol} on source server #{@opt['s']}")
      exit 1
    end
  end
  @srcVol = @srcVol.chomp(':') if @srcVol.include?(":")
  print_message(DEBUG, "Source path is #{srcPath}")
  if $isSrcLinux and excludePath != ""
    srcFlags +=  " --exclude-path #{excludePath}"
  end

  deleteLdapConfFile()

  srcFlags += " --modified-after \"#{@opt['modified-after']}\"" if @opt['modified-after']

  srcFlags += " --created-after \"#{@opt['modified-after']}\"" if @opt['modified-after'] and passCreatedDateFilters

  srcFlags += " --modified-before \"#{@opt['modified-before']}\"" if @opt['modified-before']

  srcFlags += " --accessed-after \"#{@opt['accessed-after']}\"" if @opt['accessed-after']

  srcFlags += " --accessed-before \"#{@opt['accessed-before']}\"" if @opt['accessed-before']

  srcFlags += " --exclude-dirquota" if @opt['no-dirquotas']

  srcFlags += " --exclude-userquota" if @opt['no-userquotas']

  if @opt['usecodeset']
    srcFlags += " --usecodeset \"#{@opt['usecodeset']}\" " 
  end

  if $isSrcLinux 
    # To support NCP as source, nbackup should execute with the root credentials. So setting the user to 
    # root if source is NCP. This is to provide fix for bug #831590
    if $isSrcNCP
      srcFlags += " -U \"#{ROOT_USER}\""
    else
      srcFlags += " -U \"#{credentialDetails['source-username']}\""
    end
  else
    srcFlags += " -U \".#{credentialDetails['source-username']}\""
  end
  @filesProcessed = 0
  if @opt['c']
    #session file is specified
    if File.size?(@opt['c']) != nil
      #file exists and is not zero size
      input = File.open(@opt['c'], "r")
      r = YAML.load(input)
      #compare src and dest info to make sure its the right session file
      if r['src-server'] != source
        print_message(FATAL, "Session file's source server #{r['src-server']} does not match with the given source #{source}. Ensure the sessions file is correct")
        exit 1
      end 
      if r['src-path'] != srcPath
        print_message(FATAL, "Session file's source path #{r['src-path']} does not match with given source path #{srcPath}. Ensure the sessions file is correct")
        exit 1
      end
      if r['stopped-at'] != nil
        if r['stopped-at'] != ""
          if r['stopped-at'].length < 512
            srcFlags += " --checkpoint \"#{r['stopped-at']}\""
          else
            File.open("/tmp/checkpoint", "w") { |fp|
              fp.puts(r['stopped-at'])
            }
            srcFlags += " --checkpoint @/tmp/checkpoint"
          end
        end
      else
        if r['status'] == "completed"
          print_message(INFO, "Session status is complete")
          exit 0
        else
          print_message(ERROR, "Session not complete but no \"stopped-at\" tag")
          exit 1
        end
      end
      @filesProcessed = r['Files Processed']
      $session << "---\n"
      $session << "  src-server: #{r['src-server']}\n"
      $session << "  src-path: \"#{r['src-path']}\"\n"
      $session << "  started-on: \"#{r['started-on']}\"\n"
    else
      #its the first time
      $session << "---\n"
      $session << "  src-server: #{source}\n"
      $session << "  src-path: \"#{srcPath}\"\n"
      date = DateTime.now
      dateStr = "#{date.mday}-#{date.mon}-#{date.year} #{date.hour}:#{date.min}:#{date.sec}"
      $session << "  started-on: \"#{dateStr}\"\n"
      #this is the constant info, we need to add "stopped-at" and "status" on ^C or completion
    end
  end

  if @opt['precheck']
    print_message(INFO, "Validation for mls is done successfully")
    credentials.clear if credentials != nil
    exit 0
  end

  cmd = "LC_ALL=en_US.UTF-8 #{NBACKUP} #{srcFlags} -R #{source} \"#{srcPath}\""

  print_message(DEBUG, "nbackup cmd = #{cmd}")
  print_message(DEBUG, "srcPath = #{srcPath}")

  if $isSrcLinux
    @myHash = Hash.new
    @passwd = ENV['OES_DATA']
  end

  $complete = false
  $nbackup_command = cmd
  @isFailOver = true
  doCmd(cmd)

  if File.size?("nbackup.warn") != nil
    File.readlines("nbackup.warn").each { |line|
      print_message(ERROR, line)
    }
    File.delete("nbackup.warn")
  end

  if $complete
    #update the status
    if @opt['c']
      File.open(@opt['c'], File::CREAT|File::WRONLY|File::TRUNC, 0600) { |fd|
        fd.write($session + "  status: completed")
      }
    end
  end
  print_message(DEBUG, "mls completed successfully at #{`date +%d-%m-%y\\ %H:%M:%S`}")
end

begin
  $session = ""
  $lastChkPt = ""
  trap("INT"){
    print_message(INFO, "Interrupted by user")
    $interrupted = true
    if @opt['c']
      #session file was specified!
      writeSessionFile
    end
    $nbackup_command.gsub!("\"", "")
    lcAllCmd, $nbackup_command = $nbackup_command.split(' ', 2)
    killAllnbackups($nbackup_command, true)
    exit 1
  }
  $interrupted = false
  main #if __FILE__ == $0
rescue SystemCallError => failed	
  print_message(FATAL, "SystemCallError, #{failed.message}")
  exit failed.errno
rescue MigrationException => e 
  print_message(FATAL, e.to_s)
  if @opt['continue-after-failover']
    print_message(FATAL, "Cluster resouce might have failed over before start of listing. Try restarting")
  end
  exit e.code 
rescue MigSyntaxError => e
  print_message(FATAL, "MigSyntaxError," + e.to_s)
  printUsage($argdef)
  exit e.code
rescue LdapAuthError => e
  print_message(FATAL,"LdapAuthError, #{e.message}")
  exit e.code
rescue Interrupt
  deleteLdapConfFile()
  print_message(DEBUG,"Interrupted")
  exit 1
rescue
  print_message(FATAL, "Caught exception: #{$!.message}\n Backtrace:\n #{$!.backtrace.join("\n")}") if !$interrupted
  exit 1
ensure
  # Here we need to sleep for sometime to fix deadlock error, which will be thrown
  # whenever we interrupt this script.
  sleep(1) if @opt['debug']
  deleteLdapConfFile()
  $log.close if @opt['debug']
end
