#! /usr/local/bin/python

# Copyright C 2002-2003 by Isilon Systems, Inc. All rights not expressly
# granted herein are reserved.
# Created in the United States of America.
# 
# This digital media is protected by U.S. and international copyright and
# intellectual property laws. Unless otherwise specified, all information
# and screens appearing as part of this digital medium, including
# software, services, documents, text, images, icons, and logos design;
# the selection, assembly, arrangement, and design thereof; and the code
# that enables its presentation, are the sole property of Isilon Systems,
# Inc.
# 
# Except as otherwise required by applicable law, no part of any
# copyrighted material may be reproduced, modified, translated,
# distributed, published, or transmitted by any means, electronic or
# mechanical, for any purpose, without the prior written consent of Isilon
# Systems. 
#  
# Isilon and Isilon Systems are registered trademarks of Isilon Systems,
# Inc.  Legion is a trademark of Isilon Systems, Inc.  All other
# trademarks and registered trademarks are the property of their
# respective holders.


import os
import sys
import time
import logging
import errno
import string
import signal
import isi.ui.lcd.screens as screens
import isi.ui.lcd.library as lcd
import isi.hw.hal as hal
import isi.sys.bootdisk as bootdisk

# Setup logging
import logging
log = logging.getLogger('isi_initial_newfs')

newfs_started = False
got_sighup = False

def newfs_efs():
    global newfs_started

    assert(not newfs_started)
    newfs_started = True
    try:
        screen = screens.StaticScreen('Please wait, formatting...')
        status = lcd.LCDClient(screen)
        status.start()
    except:
        # Ignore LCD errors
        pass

    pid = os.fork()

    if pid == 0:
        # We cannot ignore the SIGPIPE signal, otherwise yes never dies.
        old = signal.signal(signal.SIGPIPE, signal.SIG_DFL)
        os.system("/sbin/newfs_efs -f")
        signal.signal(signal.SIGPIPE, old)
        os._exit(0)
    else:
        try:
            while True:
                try:
                    status.service()
                except:
                    # Ignore LCD Errors
                    pass
                res = os.waitpid(pid, os.WNOHANG)
                if res[0]:
                    break
                time.sleep(5)
        finally:
            try:
                status.cancel()
            except:
                # Ignore LCD errors
                pass

def kill_other(pid):
    try:
        os.kill(pid, signal.SIGINT)
    except os.error:
        pass

def newfs_prompt():

    def handle_sighup(signal, stack_frame):
        global got_sighup
        got_sighup = True

    pid = os.fork()

    if pid == 0:
        # Don't use stdin. Keep stdout and stderr so newfs_efs will
        # output to the console.
        os.close(sys.stdin.fileno())
        sys.stdin = open('/dev/null', 'r')

        handle_lcd(os.getppid())
        os._exit(0)
    else:
        signal.signal(signal.SIGHUP, handle_sighup)
        signal.signal(signal.SIGINT, signal.SIG_IGN)
        handle_console(pid)
        while True:
            try:
                res = os.waitpid(pid, 0)
                if res[0]:
                    break
            except os.error, e:
                if e[0] == errno.EINTR:
                    pass
                elif e[0] == errno.ECHILD:
                    break
                else:
                    break

def signal_other(sig, pid):
    try:
        os.kill(pid, sig)
    except Exception, e:
        # Ignore any errors.
        pass

def handle_lcd(console_pid):
    # first initialize the lcd for client calls
    lcd.init_client(logging.ERROR)

    try:
        screen = screens.ListInputScreen('Format ifs partition?', ['no', 'yes'])
        result = lcd.show(screen)

        # Tell handle_console() the LCD has taken over.
        signal_other(signal.SIGHUP, console_pid)
        # Ensure SIGHUP is processed before SIGINT.
        time.sleep(0.01)
        # Interrupt handle_console()'s wait for input
        signal_other(signal.SIGINT, console_pid)

        if result == 'yes':
            screen = screens.ListInputScreen( \
                'This can cause data loss; are you sure?', ['no', 'yes'])
            result = lcd.show(screen)
            if result == 'yes':
                newfs_efs()

            # On "no", just exit

    except Exception, e:
        # This means we've received a signal. Done with any LCD
        # interaction.
        pass
    

def handle_console(lcd_pid):

    prompt = ''
    while prompt != "yes" and prompt != 'no':
        print """
About to format ifs partition for all drives.
WARNING: Proceeding will destroy *all data* on this node!
Are you sure you want to proceed?
"""
        try:
            prompt=raw_input('(yes or no)> ')
        except EOFError:
            # bogus characters generated by an incorrect baudrate or ctrl-d
            continue
        except Exception, e:
            # SIGHUP is from handle_lcd() taking over. Return and wait
            # for LCD to exit.
            if got_sighup:
                print "\n[Front panel has received input and is now in control of formatting ifs]"
                return
                    
            # On any other exception, we'll just go on to exit the
            # program cleanly.
            break

    # No need for LCD at this point. But for some reason LCD
    # occasionally doesn't die if a SIGINT is sent and the waitpid()
    # in newfs_prompt() hangs forever. So use a SIGKILL and do it
    # three times. This is excessive, but it solves the problem. One
    # SIGKILL is probably enough, but it doesn't hurt to attempt more
    # than once.

    # kill #1
    signal_other(signal.SIGKILL, lcd_pid)
    time.sleep(0.01)
    # kill #2
    signal_other(signal.SIGKILL, lcd_pid)
    if prompt.lower() == 'yes':
        newfs_efs()
    # kill #3
    signal_other(signal.SIGKILL, lcd_pid)


if __name__ == '__main__':
    # If we're running off a manufacturing flash, then imdd
    # will be disabled, and we'll want to skip the newfs
    if hal.isAcceleratorClass():
        print "diskless node, skipping newfs_efs"
    elif not bootdisk.current_system_is_flash():
        newfs_prompt()
    else:
        print "imdd is disabled, skipping initial newfs"
    sys.exit(0)
