#!/usr/bin/env python

# Copyright (c) 2019 Arista Networks, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.

from __future__ import print_function

import os
import signal
import stat
import subprocess
import sys
import time

RC_EOS_PATH = '/mnt/flash/rc.eos'

def execRc( rcfile ):
   p = None

   try:
      # Make sure the file is executable. On ext4 there is no guarantee that it
      # will be.
      st = os.stat( rcfile )
      os.chmod( rcfile, st.st_mode | stat.S_IEXEC )

      # We use the subprocess module instead of ManagedSubprocess
      # because we don't want to kill backgrounded processes upon
      # rc.eos's exit. ManagedSubprocess terminates background
      # processes when rc.eos exits. We run rc.eos before setting the
      # controlling terminal. If we run rc.eos after the setsid()
      # call, the background processes still get killed when rc.eos
      # exits.
      p = subprocess.Popen( [ rcfile ] )

      # Install a signal handler for SIGINT, so that in case the script
      # hangs we can kill it.
      def handler( signum, frame ):
         print( 'Terminating %s with ^C' % rcfile, file=sys.stderr )
         sys.stderr.flush()
         # On some runs of EosInitHang, we may get the above output eaten by some
         # other prints on the console. As soon as we return, systemd starts other
         # services. Wait a little bit to allow time for the console to show the
         # message.
         time.sleep( 2 )
         sys.exit( 1 )

      signal.signal( signal.SIGINT, handler )

      tty = None
      # Find the console and make it the controlling terminal.
      with open( '/proc/cmdline' ) as f:
         cmdline = f.read()
      try:
         tty = ( ( cmdline[ cmdline.rindex( 'console=' ) + len( 'console=' ) : ] )
                    .partition( ' ' ) )[ 0 ].partition( ',' )[ 0 ]
      except ValueError as e:
         pass

      if tty:
         if '/dev/' in tty:
            tty_name = tty
         else:
            tty_name = '/dev/' + tty

         if 'uart8250' in tty_name:
            tty_name = '/dev/ttyS0'

         # Make ourselves session leader and open the tty. That should
         # make tty our controlling terminal.
         os.setsid()
         fd = os.open( tty_name, os.O_RDWR )

         # We want to listen to ^C. So dup stdinput to fd. We leave
         # stdoutput alone. Should we dup that too?
         os.dup2( fd, 0 )
         os.dup2( fd, 2 )

      p.wait()

   except OSError as e:
      print( 'Unexpected error when trying to access %s: %s' % ( rcfile, e ),
             file=sys.stderr )
      if p:
         p.kill()
         p.wait()

def maybeExecRc( rcFile=RC_EOS_PATH ):
   # This is redundant with the systemd unit ConditionalPathExists, but doesn't hurt.
   # Especially if someone wants to run this script manually
   if not os.path.exists( rcFile ):
      return

   print( rcFile, 'detected' )

   try:
      # We want to control the TTY to be able to CTRL+C rc.eos if it hangs. By
      # default systemd units cannot do that. Doing a fork() solves the problem.
      # That is not very elegant, but ...
      pid = os.fork()

      if pid == 0:
         execRc( rcFile )
      else:
         os.waitpid( pid, 0 )
   except OSError as e:
      print( 'Unexpected error while setting up rc.eos execution:', e,
             file=sys.stderr )

if __name__ == "__main__":
   maybeExecRc()
