#!/usr/bin/python
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0

  #############################################################################
  #
  # Copyright (c) 2005 Dell Computer Corporation
  # Dual Licenced under GNU GPL and OSL
  #
  #############################################################################
"""smbios-rbu-bios-update"""

from __future__ import generators, division

# import arranged alphabetically
import ctypes
import gettext
import locale
import os
import struct
import sys
import time
import traceback

# the following vars are all substituted on install
# this bin isnt byte-compiled, so this is ok
__VERSION__="uninstalled-version"
pythondir=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "..", "python")
clidir=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "..", "py-cli")
# end vars

# import all local modules after this.
sys.path.insert(0,pythondir)
sys.path.insert(0,clidir)

import cli
from libsmbios_c import system_info as sysinfo, smbios, token, localedir, GETTEXT_PACKAGE
from libsmbios_c.trace_decorator import decorate, traceLog, getLog

locale.setlocale(locale.LC_ALL, '')
gettext.install(GETTEXT_PACKAGE, localedir, unicode=1)

from libsmbios_c.rbu_hdr import *
from libsmbios_c.rbu_update import *

# use python-decoratortools if it is installed, otherwise use our own local
# copy. Imported this locally because it doesnt appear to be available on SUSE
# and the fedora RPM doesnt appear to compile cleanly on SUSE
try:
    from peak.util.decorators import decorate_class
except ImportError:
    from libsmbios_c._peak_util_decorators import decorate_class

moduleLog = getLog()
verboseLog = getLog(prefix="verbose.")

def command_parse():
    parser = cli.OptionParser(usage=__doc__, version=__VERSION__)
    parser.add_option('--hdr', '-f', metavar="HDR_FILE", action="store", default=None, help= _("The BIOS update file (.HDR file)"))
    parser.add_option('--hdr-info', metavar="HDR_FILE", action="append", default=[], help= _("Show information about a given BIOS HDR file"))
    parser.add_option('--system-info', action="store_const", const="system_info", dest="action", default=None, help= _("Show system information and RBU completion codes"))
    parser.add_option('--cancel', action="store_const", const="cancel", dest="action", help= _("Cancel pending BIOS update"))
    parser.add_option("-u", '--update', action="store_const", const="update", dest="action", help= _("Stage BIOS for update at next reboot"))
    parser.add_option('--test', action="store_const", const="test", dest="action", help= _("Test HDR file to see if it is appropriate for this system"))

    parser.add_option('--auto-detect-mode', action="store_const", const="auto", dest="update_mode", default="auto", help= _("Automatically select the optimal update mode (default, recommended)"))
    parser.add_option('--force-packet-mode', action="store_const", const="packet", dest="update_mode", help= _("Force update to use packet-mode"))
    parser.add_option('--force-mono-mode', action="store_const", const="mono", dest="update_mode", help= _("Force update to use monolithic-mode"))
    parser.add_option('--force-compat-mode', action="store_const", const="compat", dest="update_mode", help= _("Force update to use old dellBiosUpdate binary"))

    parser.add_option('--override-version-check', action="store_false", dest="check_bios_version", default=True, help= _("Allow BIOS downgrades and re-flash"))
    parser.add_option('--override-sysid-check', action="store_false", dest="check_sysid", default=True, help= _("Disable check for system-id (Dangerous)"))

    parser.add_option('--reboot', action="store_true", dest="reboot", default=None, help= _("Reboot the system after successfully staging update."))
    parser.add_option('--no-reboot', action="store_false", dest="reboot", help= _("Do not reboot the system after staging update."))
    cli.addStdOptions(parser)
    options, args = parser.parse_args()

    if len(args) == 1 and options.hdr is None:
        options.hdr = args[0]

    return options,args

decorate(traceLog())
def _getNum(s, off, len):
    retval = 0
    try:
        t = list(struct.unpack( 'B' * len, s.getData(off, len) ))
        t.reverse()
        for i in t:
            retval = (retval << 8) | i
    except IndexError, e:
        pass
    return retval



decorate(traceLog())
def system_info():
    table = smbios.SmbiosTable()
    s = table[RBU_SMBIOS_STRUCT]

    print _("RBU Update information:")
    print _("\tMinimum size: %d") % _getNum(s, 0x04, 2)
    print _("\tCompletion Code: %d") %  getCompletion(s)[0]
    print _("\tCompletion Message: %s") %  getCompletion(s)[1]
    print _("\tLast Update Date - Year  : %02x") % getRbuLastUpdate(s)["year"]
    print _("\tLast Update Date - Month : %02x") % getRbuLastUpdate(s)["month"]
    print _("\tLast Update Date - Day   : %02x") % getRbuLastUpdate(s)["day"]
    print _("\tLast Update Date - time  : %02x:%02x") % (getRbuLastUpdate(s)["hour"], getRbuLastUpdate(s)["minute"])
    print _("\tBoot Feature Flags: 0x%08x") % _getNum(s, 0x0d, 2)
    print _("\tCharacteristics: 0x%04x") % _getNum(s, 0x0f, 1)
    print _("\tUpdate modes supported: %s") % ", ".join(getUpdateModes(s))


def ensureRbuDriver():
    os.system("/sbin/modprobe dell_rbu")

def conditionalReboot(options):
    if options.reboot is None:
        print _("No reboot option specified. The --reboot option is highly recommended.")
    elif options.reboot:
        print _("Rebooting system to update the BIOS.")
        while 1:
            os.system("/sbin/shutdown -r now")
            time.sleep(10)
            os.system("/usr/bin/reboot")
            time.sleep(10)

def main():
    exit_code = 0
    (options, args) = command_parse()
    cli.setup_std_options(options)

    try:
        if options.hdr_info:
            for filename in options.hdr_info:
                h = HdrFile(filename)
                dumpHdrFileInfo(h)
        elif options.action == "system_info":
            system_info()

        elif options.action == "cancel":
            ensureRbuDriver()
            #smbios.SmbiosTable() # check permissions
            table = token.TokenTable()
            print _("Cancelling BIOS update...")
            cancelUpdate()
            print _("BIOS update cancelled.")
        elif options.action == "update":
            ensureRbuDriver()
            smbios.SmbiosTable() # check permissions
            print( _("Performing BIOS update...") )
            exit_code = updateBios(HdrFile(options.hdr), options)
            print _("Update successfully staged. Reboot the system to begin BIOS update.")
            if exit_code == 0:
                conditionalReboot(options)
            else:
                print _("The BIOS update did NOT complete successfully.")
        elif options.action == "test":
            ensureRbuDriver()
            smbios.SmbiosTable() # check permissions
            exit_code = updateBios(HdrFile(options.hdr), options, testMode=1)

    except IndexError, e:
        moduleLog.info( _("RBU Bios Update not supported on this system.") )
        exit_code=2
    except (smbios.TableParseError, token.TokenTableParseError), e:
        exit_code=3
        moduleLog.info( _("ERROR: Could not parse system SMBIOS table.") )
        verboseLog.info( _("The smbios library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )
    except (token.TokenManipulationFailure,), e:
        exit_code=4
        moduleLog.info( _("ERROR: Could not manipulate system token.") )
        verboseLog.info( _("The token library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )

    return exit_code



if __name__ == "__main__":
    sys.exit( main() )


