#!/usr/bin/python

import os
import sys

PACKAGE="firmware-usb-autoupdate"
LOCALEDIR="/usr/share/locale"
PYTHONDIR="/usr/lib/python2.3/site-packages"

sys.path.insert(0,PYTHONDIR)
sys.path.insert(0,os.path.join(PYTHONDIR, "gtk-2.0"))

import gobject
import dbus
import dbus.service
import ConfigParser
import glob
import subprocess
import optparse
import gettext, locale

try:
    from dbus.mainloop.glib import DBusGMainLoop
    DBusGMainLoop(set_as_default=True)
except ImportError:
    # old python dbus bindings... not fatal
    import dbus.glib

from firmware_usb_autoupdate import halhelper, util

locale.setlocale(locale.LC_ALL,'')
gettext.bindtextdomain(PACKAGE, os.environ.get("LOCALEDIR", LOCALEDIR))
gettext.textdomain(PACKAGE)
gettext.install(PACKAGE, LOCALEDIR, unicode=1)
_ = gettext.lgettext

os.environ["PATH"] = ":".join(os.environ.get("PATH", "/bin:/usr/bin").split(":") + ["/sbin", "/usr/sbin"] )
CONFIG = os.environ.get("CONFIG_PATH", "/etc/bios_autoupdate.ini")
TRUE_OPTS = ["1", "yes", "true"]

DEFBUS=dbus.SystemBus
DBUS_INTERFACE='com.dell.libsmbios.firmware_usb_autoupdate'
DBUS_BUS='com.dell.libsmbios.firmware_usb_autoupdate'

class HalMonitor(dbus.service.Object):
    def __init__(self, object_path, configFile, mainloop):
        dbus.service.Object.__init__(self,
            dbus.service.BusName(DBUS_BUS, bus=DEFBUS()), object_path)

        self.mainloop = mainloop
        self.bus = dbus.SystemBus ()
        self.hal_obj = self.bus.get_object ('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
        self.hal = dbus.Interface (self.hal_obj, 'org.freedesktop.Hal.Manager')

        udi = "/org/freedesktop/Hal/devices/computer"
        computerObj = self.bus.get_object ('org.freedesktop.Hal', udi)
        self.computerInt = dbus.Interface (computerObj, 'org.freedesktop.Hal.Device')
        self.computerInt.SetPropertyInteger("bios.usb_autoupdate_complete", 0)

        self.initConfig(configFile)

        self.usb_autoupdate_udi = None
        self.signalListeners = {}
        self.hal.connect_to_signal("DeviceAdded",
                 lambda *args: self.gdlAdded("DeviceAdded", *args))
        self.hal.connect_to_signal("DeviceRemoved",
                 lambda *args: self.gdlRemoved("DeviceRemoved", *args))


    @dbus.service.method(dbus_interface=DBUS_INTERFACE, in_signature='', out_signature='i')
    def UpdateBiosFromUSBDevice(self):
        if self.hdr:
            subprocess.call(["modprobe", "dell_rbu"])
            subprocess.call(["dellBiosUpdate", "-u", "-f",  self.hdr])

            self.computerInt.SetPropertyInteger("bios.usb_autoupdate_complete", 1)

            self.condReboot()
            return 1
        return 0

    @dbus.service.method(dbus_interface=DBUS_INTERFACE, in_signature='', out_signature='i')
    def CancelBiosUpdate(self):
        if self.computerInt.GetPropertyInteger("bios.usb_autoupdate_complete"):
            subprocess.call(["modprobe", "dell_rbu"])
            subprocess.call(["dellBiosUpdate", "-c"])
            self.computerInt.SetPropertyInteger("bios.usb_autoupdate_complete", 0)
            return 1
        else:
            return 0

    @dbus.service.method(dbus_interface=DBUS_INTERFACE, in_signature='', out_signature='')
    def shutdown(self):
        print "Bye"
        self.mainloop.quit()

    @dbus.service.method(dbus_interface=DBUS_INTERFACE, in_signature='v', out_signature='v')
    def ping(self, value):
        print "pinged :)"
        return value

    def initConfig(self, configFile):
        # set config defaults (overridden by bios_autoupdate.ini)
        self.cfg = util.initDefaultConfig()
        self.configFile=configFile
        self.rereadConfig()

    def rereadConfig(self):
        self.cfg.read( self.configFile )

    def propertyModified(self, udi, deviceInt, num, arr):
        print "propertyModified: %s" % (udi)
        for propName, added, removed in arr:
            try:
                if propName in ["volume.is_mounted"]:
                    if  halhelper.isMountedUsbVolume(self.bus, deviceInt):
                        self.handleMount(deviceInt)
            except dbus.DBusException:
                # sometimes properties/devices go away
                pass

    def gdlAdded(self, event, udi, *args):
        print "%s: %s" % (event, repr(udi))
        print "\t%s signal listeners" % len(self.signalListeners.keys())

        deviceObj = self.bus.get_object ('org.freedesktop.Hal', udi)
        deviceInt = dbus.Interface (deviceObj, 'org.freedesktop.Hal.Device')

        if halhelper.isMountedUsbVolume(self.bus, deviceInt):
            self.handleMount(deviceInt)

        elif halhelper.isUsbVolume(self.bus, deviceInt):
            if not self.signalListeners.get(udi):
                print "\tadding propertyModfied signal handler for udi"
                self.signalListeners[udi] = deviceInt.connect_to_signal("PropertyModified",
                    lambda *args: self.propertyModified(udi, deviceInt, *args))

    def gdlRemoved(self, event, udi, *args):
        print "%s: %s" % (event, repr(udi))
        print "\t%s signal listeners" % len(self.signalListeners.keys())
        if self.signalListeners.get(udi):
            print "\tremoving signal handler for device"
            self.signalListeners[udi].remove()
            del(self.signalListeners[udi])

        if self.usb_autoupdate_udi == udi:
            print "\tDevice contained our update, removing."
            self.hdr = None
            self.usb_autoupdate_udi = None
            try:
                self.computerInt.RemoveProperty("bios.usb_autoupdate_udi")
            except dbus.DBusException:
                pass

    def handleMount(self, deviceInt):
        self.rereadConfig()
        if self.cfg.get("usb_auto_update", "update_type").lower() == "update-disable":
            return

        mount_point = deviceInt.GetProperty("volume.mount_point")
        print "tryBiosUpdateOnMount: %s" % mount_point
        for hdr in glob.glob("%s/*.[Hh][Dd][Rr]" % mount_point):
            print "File: %s" % hdr
            ret = subprocess.call(["dellBiosUpdate", "-t", "-f",  hdr])
            if ret == 0:
                print "found good bios hdr: %s" % hdr
                self.setUpdateStats(deviceInt, hdr)
                if self.cfg.get("usb_auto_update", "update_type").lower() == "update-auto":
                    self.UpdateBiosFromUSBDevice()
                break
            else:
                print "bios hdr does not apply: %s" % hdr

    def condReboot(self):
        if self.cfg.get("usb_auto_update", "reboot_after_update").lower() in TRUE_OPTS:
            subprocess.call(["/sbin/reboot",])

    def setUpdateStats(self, deviceInt, hdr):
        self.usb_autoupdate_udi = deviceInt.GetProperty("info.udi")
        self.hdr = hdr

        hdrbiosver = "unknown"
        biosver = "unknown"
        productName = "unknown"

        try:
            p = subprocess.Popen(["dellBiosUpdate", "-i", "-f",  hdr],
                stdin = subprocess.PIPE,
                stdout = subprocess.PIPE,
                close_fds = True)
            out, err = p.communicate()
            for line in out.split("\n"):
                if line.startswith("Version:"):
                    hdrbiosver = line.split(":")[1].strip()
        except Exception, e:
            import traceback
            traceback.print_exc()
            pass

        try:
            p = subprocess.Popen(["getSystemId"],
                stdin = subprocess.PIPE,
                stdout = subprocess.PIPE,
                close_fds = True)
            out, err = p.communicate()
            for line in out.split("\n"):
                if line.startswith("Product Name:"):
                    productName = line.split(":")[1].strip()
                if line.startswith("BIOS Version:"):
                    biosver = line.split(":")[1].strip()
        except Exception, e:
            import traceback
            traceback.print_exc()
            pass

        deviceInt.SetPropertyString("bios.usb_autoupdate_hdr", self.hdr)
        deviceInt.SetPropertyString("bios.usb_autoupdate_hdrver", hdrbiosver)
        deviceInt.SetPropertyString("bios.usb_autoupdate_curver", biosver)
        deviceInt.SetPropertyString("bios.usb_autoupdate_prodname", productName)
        self.computerInt.SetPropertyString("bios.usb_autoupdate_udi", self.usb_autoupdate_udi)

def shutdown():
    bus = dbus.SystemBus ()
    checkObj = bus.get_object (DBUS_BUS, '/singleton')
    checkInt = dbus.Interface (checkObj, DBUS_INTERFACE)
    checkInt.shutdown()

def main():
    parser = optparse.OptionParser("usage: %prog [options]")
    parser.add_option("-f", "--foreground", dest="daemonize",
                       default=True, action="store_false",
                       help=_("Do *not* put program into the background."))
    parser.add_option("-b", "--background", dest="daemonize",
                       action="store_true",
                       help=_("Put program into the background."))
    parser.add_option("-c", "--config", dest="config_path",
                       type="string", action="store", default=CONFIG,
                       help=_("Put program into the background."))
    parser.add_option("-s", "--shutdown", dest="shutdown",
                       action="store_true", default=False,
                       help=_("Shutdown background process."))
    parser.add_option("-l", "--lock-session", dest="locksession",
                       action="store_true", default=True,
                       help=_("Lock session to ensure only one copy runs."))
    parser.add_option("-L", "--dont-lock-session", dest="locksession",
                       action="store_false",
                       help=_("Lock session to ensure only one copy runs."))

    (options, args) = parser.parse_args()
    if len(args) != 0:
         parser.error(_("Error, program does not take non-option arguments."))

    if options.shutdown:
        shutdown()
        sys.exit(1)

    if options.locksession:
        sessionLock = util.lockSession(os.path.expanduser("/var/lock/bios-update-monitor.lock"))
        if not sessionLock: # somebody else already running
            print _("Monitor is already running.")
            sys.exit(1)

    loop = gobject.MainLoop()
    f = HalMonitor("/singleton", options.config_path, loop)

    if options.daemonize:
        util.daemonize("/var/log/bios-output.txt", "/var/log/bios-errout.txt")

    loop.run()

if __name__ == "__main__":
    main()
