Example #1
0
    def loadManifest(self):

        installed = self.isInstalled()

        g.feedback.log(LogLevels.INFO, "Loading new manifest from %s" % options.manifest)
        self.new_manifest = Manifest(self.os, self.arch, self.osver, installed, options.manifest)

        if installed:
            self.manifest = Manifest(self.os, self.arch, self.osver, True, None)
        else:
            g.feedback.log(LogLevels.INFO, "Loading manifest from new manifest, as not installed")
            self.manifest = self.new_manifest
Example #2
0
 def loadManifest(self):
             
     g.feedback.log(LogLevels.INFO, "Loading new manifest from %s" % options.manifest)
     self.new_manifest = Manifest(self.os, self.arch, options.manifest, options.manifest_is_url)
     
     if self.isInstalled():
         manifest_file = ".jinn" + self.sep() + "current_manifest.json"
         g.feedback.log(LogLevels.INFO, "Loading manifest from %s" % manifest_file)
         self.manifest = Manifest(self.os, self.arch, manifest_file, False, True)
     else:
         g.feedback.log(LogLevels.INFO, "Loading manifest from new manifest, as not installed")
         self.manifest = self.new_manifest
Example #3
0
    def loadManifest(self):

        g.feedback.log(LogLevels.INFO,
                       "Loading new manifest from %s" % options.manifest)
        self.new_manifest = Manifest(self.os, self.arch, options.manifest,
                                     options.manifest_is_url)

        if self.isInstalled():
            manifest_file = ".jinn" + self.sep() + "current_manifest.json"
            g.feedback.log(LogLevels.INFO,
                           "Loading manifest from %s" % manifest_file)
            self.manifest = Manifest(self.os, self.arch, manifest_file, False,
                                     True)
        else:
            g.feedback.log(
                LogLevels.INFO,
                "Loading manifest from new manifest, as not installed")
            self.manifest = self.new_manifest
Example #4
0
class Jinn(FileSystemHelper):

    # Header for messages
    header = """
    .---.                           
    |   |                           
    '---'.--.   _..._      _..._    
    .---.|__| .'     '.  .'     '.  
    |   |.--..   .-.   ..   .-.   . 
    |   ||  ||  '   '  ||  '   '  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||__||  |   |  ||  |   |  | 
 __.'   '    |  |   |  ||  |   |  | 
|      '     |  |   |  ||  |   |  | 
|____.'      '--'   '--''--'   '--' 
A Java installer"""

    def __init__(self):
        self.new_manifest = None
        self.manifest = None
        self.args = ""

        # Setup feedback mechanism
        global feedback
        if (options.interface == FeedbackMechanisms.CMD):
            g.feedback = ConsoleFeedback()
        elif (options.interface == FeedbackMechanisms.UI):
            g.feedback = UIFeedback()
        else:
            # Not specified, need something to stop errors, so us this
            g.feedback = FeedbackBase()

        # Setup min log level
        g.feedback.minLogLevel = LogLevels.getLevelFromString(
            options.min_log_level)

        self.os = self.getOperatingSystem()
        self.arch = self.getArchitecture()

        g.feedback.log(
            LogLevels.INFO, "Started up. OS: %s; Architecture: %s" %
            (OperatingSystem().getOperatingSystem(
                self.os), Architecture().getArchitecture(self.arch)))

    """
    Gets the current OS
    """

    def getOperatingSystem(self):
        p = platform.system()
        if p == "Windows":
            return OperatingSystem.WIN
        elif p == "Darwin":
            return OperatingSystem.OSX
        elif p == "Linux":
            return OperatingSystem.LIN
        else:
            g.feedback.log(LogLevels.ERROR,
                           "Unable to work with operating system %s" % p)
            raise OperatingSystemNotFoundException(p)

    """
    Gets the current architecture
    """

    def getArchitecture(self):
        if self.os is OperatingSystem.WIN:
            g.feedback.log(
                LogLevels.DEBUG,
                "Getting architecture, we are windows, so asking the registry")
            return self.getArchitectureWindows()
        else:
            g.feedback.log(LogLevels.DEBUG,
                           "Working out if we are running 32 or 64 bit")
            if (sys.maxsize > 2**32):
                g.feedback.log(LogLevels.DEBUG, "Decided 64 bit")
                return Architecture.x64
            else:
                g.feedback.log(LogLevels.DEBUG, "Decided 32 bit")
                return Architecture.x32

    """
    Windows special case for getting architecture
    """

    def getArchitectureWindows(self):
        from _winreg import *
        softwarekey = OpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE")
        microsoftkey = OpenKey(softwarekey, "Microsoft")
        windowskey = OpenKey(microsoftkey, "Windows NT")
        versionkey = OpenKey(windowskey, "CurrentVersion")
        value = QueryValueEx(versionkey, "BuildLabEx")[0]
        CloseKey(versionkey)
        CloseKey(windowskey)
        CloseKey(microsoftkey)
        CloseKey(softwarekey)
        if "amd64" in value:
            g.feedback.log(LogLevels.DEBUG,
                           "From value %s, decided we are 64 bit" % value)
            return Architecture.x64
        else:
            g.feedback.log(LogLevels.DEBUG,
                           "From value %s, decided we are 32 bit" % value)
            return Architecture.x32

    """
    Loads the manifest file
    """

    def loadManifest(self):

        g.feedback.log(LogLevels.INFO,
                       "Loading new manifest from %s" % options.manifest)
        self.new_manifest = Manifest(self.os, self.arch, options.manifest,
                                     options.manifest_is_url)

        if self.isInstalled():
            manifest_file = ".jinn" + self.sep() + "current_manifest.json"
            g.feedback.log(LogLevels.INFO,
                           "Loading manifest from %s" % manifest_file)
            self.manifest = Manifest(self.os, self.arch, manifest_file, False,
                                     True)
        else:
            g.feedback.log(
                LogLevels.INFO,
                "Loading manifest from new manifest, as not installed")
            self.manifest = self.new_manifest

    """
    A helper which sets up the system before a run
    """

    def setupSystem(self):

        g.feedback.log(LogLevels.INFO, "Setting up the system")

        # Make sure we are installed
        if not self.isInstalled():
            g.feedback.log(LogLevels.INFO,
                           "We aren't installed, so installing")
            status = self.doInstall()
            if status != 0:
                g.feedback.log(LogLevels.ERROR,
                               "Tried installing but failed horribly")
                g.feedback.userMessage(
                    "Installation failed (1) - please contact distributor")
                return status
            g.feedback.log(LogLevels.INFO, "Installation succeeded")
        else:
            self.loadManifest()

        return self.doUpdate()

    """
    Runs the default action in the jinn
    """

    def runDefaultAction(self):
        g.feedback.log(LogLevels.DEBUG, "Running default action")

        self.setupSystem()

        g.feedback.log(LogLevels.DEBUG, "Default action execution commencing")
        try:
            self.manifest.runDefaultAction(self.args)
            g.feedback.log(LogLevels.DEBUG,
                           "Run default action executed successfully")
            return 0
        except Exception as e:
            g.feedback.log(LogLevels.ERROR,
                           "Running default action threw exception %s" % e)
            return 1

    """
    Runs a specific action within the jinn
    """

    def runAction(self, action):

        g.feedback.log(LogLevels.INFO, "Running %s action" % action)
        self.setupSystem()
        g.feedback.log(LogLevels.DEBUG, "Run action %s" % action)
        try:
            self.manifest.runAction(action, self.args)
            g.feedback.log(LogLevels.INFO,
                           "Running action %s successfully completed" % action)
            return 0
        except Exception as e:
            g.feedback.log(
                LogLevels.ERROR,
                "Running action %s threw exception %s" % (action, e))
            return 1

    """
    Are we in dev mode?
    """

    def isDevMode(self):
        isDev = options.version is "DEV"
        g.feedback.log(LogLevels.WARN, "Dev mode? %s" % str(isDev))
        return isDev

    """
    Checks the manifests for updates
    """

    def doUpdate(self):

        g.feedback.log(LogLevels.DEBUG, "Starting update procedure")

        # No update if the version number is the same
        if self.manifest.jinn.version == self.new_manifest.jinn.version:
            g.feedback.log(
                LogLevels.DEBUG,
                "New and current manifest versions are %s and %s, so skipping update as they are identical"
                % (self.manifest.jinn.version, self.new_manifest.jinn.version))
            return True

        g.feedback.log(LogLevels.DEBUG,
                       "Setting installation status across manifests")
        # Initially, set the resources that are installed on the new manifest already
        if not self.new_manifest.setInstallStatus(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Failed to set install status")
            return False

        g.feedback.log(LogLevels.INFO, "Installing new resources")
        # First, install resources that are new
        if not self.new_manifest.installNewResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Failed to install new resources")
            return False

        g.feedback.log(LogLevels.INFO, "Uninstalling removed resources")
        # Second, uninstall resources that are gone
        if not self.new_manifest.uninstallRemovedResources(
                self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Removing resources failed")
            return False

        g.feedback.log(LogLevels.INFO, "Updating changed resources")
        # Third, update resources that are new version
        if not self.new_manifest.updateResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR,
                           "Updating changed resources failed")
            return False

        g.feedback.log(LogLevels.DEBUG, "Saving manifest")
        # Finally, transition the manifest over to the new one
        self.manifest = self.new_manifest
        if not self.manifest.save():
            g.feedback.log(LogLevels.ERROR, "Unable to save updated manifest")
            return False

        # Done!
        g.feedback.log(LogLevels.DEBUG, "Update complete")
        return True

    """
    Copy this executable to the target directory, then run it
    """

    def doCopy(self):
        g.feedback.log(
            LogLevels.DEBUG,
            "We are not in the correct directory, so installing to the correct location"
        )

        if self.os is OperatingSystem.OSX:
            g.feedback.log(LogLevels.DEBUG, "Copying in OS X mode")

            targetDir = self.getInstallTargetDirectory()
            copyTargetDir = targetDir.rsplit("/", 2)[0]
            currentDir = self.getCurrentDirectory() + self.sep(
            ) + ".." + self.sep() + ".."

            executable = targetDir + self.sep() + self.getExecutableName()

            g.feedback.log(LogLevels.DEBUG,
                           "Target executable: %s" % executable)

            if not self.exists(executable):
                g.feedback.log(LogLevels.DEBUG,
                               "Target executable does not exist, copying")
                if not self.makeDirectory(copyTargetDir):
                    return False
                if not self.copyDir(currentDir, copyTargetDir):
                    return False
            else:
                g.feedback.log(LogLevels.DEBUG,
                               "Target executable exists, so running it")

            cmd = executable + " -install"
            g.feedback.log(LogLevels.DEBUG, "Executing command: %s" % cmd)
            res = os.system(cmd)
            if res < 1:
                g.feedback.log(LogLevels.DEBUG,
                               "Executed command successfully")
                return 0
            else:
                g.feedback.log(
                    LogLevels.ERROR,
                    "Executing command %s failed with code %s" %
                    (cmd, str(res)))
                return 1

        else:
            g.feedback.log(LogLevels.DEBUG, "Copying in Windows / Linux mode")
            targetFile = self.getInstallTargetFile()
            g.feedback.log(LogLevels.DEBUG, "Target file is %s" % targetFile)
            d = self.getInstallTargetDirectory()
            g.feedback.log(LogLevels.DEBUG, "Target directory is %s" % d)
            if not self.exists(targetFile):
                # Make the jinn install directory
                if not self.makeDirectory(d):
                    g.feedback.log(
                        LogLevels.ERROR,
                        "Unable to make directory %s to install to" % d)
                    return 1

                # Copy this binary into it
                frm = self.getCurrentFile()
                to = targetFile
                g.feedback.log(LogLevels.DEBUG,
                               "Copying from %s to %s" % (frm, to))
                if not self.copyFile(frm, to):
                    g.feedback.log(LogLevels.ERROR,
                                   "Unable to copy %s to %s" % (frm, to))
                    return 1

            # Change into that directory
            g.feedback.log(LogLevels.DEBUG, "Changing to %s" % d)
            if not self.changeDirectory(d):
                g.feedback.log(
                    LogLevels.ERROR,
                    "Unable to change to the InstallTargetDirectory %s" % d)
                return 1

            # Run the new executable
            g.feedback.log(LogLevels.DEBUG,
                           "Code copied to %s, executing" % targetFile)
            cmd = self.getExecutableName() + " -install"
            if self.os == OperatingSystem.LIN or self.os == OperatingSystem.OSX:
                cmd = "./" + cmd
            g.feedback.log(LogLevels.DEBUG, "Run command: %s" % cmd)
            os.system(cmd)
            return 0

    """
    Runs an installation of this jinn
    """

    def doInstall(self):

        g.feedback.log(LogLevels.INFO, "Beginning installation")

        # We need the manifest first of all
        self.loadManifest()

        # Hard code here to not happen for dev
        correctDir = self.isCorrectDirectory()
        g.feedback.log(LogLevels.DEBUG,
                       "Are we in correct directory? %s" % str(correctDir))
        if not correctDir and not self.isDevMode():
            return self.doCopy()

        # Make sure we are in the right directory
        installDir = self.getInstallTargetDirectory()
        g.feedback.log(LogLevels.DEBUG,
                       "Target install dir is %s" % installDir)
        if not self.changeDirectory(installDir) and not self.isDevMode():
            g.feedback.log(
                LogLevels.ERROR,
                "Unable to change to where we thought we were installed, %s" %
                installDir)
            return 1

        if self.isInstalled():
            g.feedback.log(LogLevels.ERROR, "This jinn is already installed")
            g.feedback.userMessage(
                "Installation failed (3) - please contact distributor")
            return 1

        g.feedback.log(LogLevels.DEBUG, "Installing")

        if not self.makeDirectory(".jinn"):
            g.feedback.log(LogLevels.ERROR, "Unable to make .jinn directory")
            return 1

        if not self.manifest.save():
            g.feedback.log(LogLevels.ERROR, "Unable to save the manifest")
            return 1

        try:
            if self.manifest.installResources():
                g.feedback.log(LogLevels.DEBUG, "Install resources succeeded")
                return 0
            else:
                g.feedback.log(LogLevels.ERROR, "Install resources failed")
                return 1
        except Exception as e:
            g.feedback.log(LogLevels.ERROR,
                           "Unable to install resources: %s" % e)
            return 1

    """
    Runs uninstallation
    """

    def doUninstall(self):

        g.feedback.log(LogLevels.INFO, "Doing uninstallation")

        if not self.isInstalled():
            g.feedback.log(
                LogLevels.ERROR,
                "This jinn is not installed, so cannot be uninstalled")
            g.feedback.userMessage(
                "Uninstallation failed (4) - please contact distributor")
            return 1

        g.feedback.log(LogLevels.DEBUG, "Uninstalling")

        self.loadManifest()

        try:
            if self.manifest.uninstallResources():
                if self.delete(".jinn"):
                    g.feedback.userMessage(
                        "Uninstallation finished. To completely erase this application, please delete the directory %s"
                        % self.getInstallTargetDirectory())
                    return 0
                else:
                    g.feedback.log(
                        LogLevels.ERROR,
                        "Failed to delete .jinn directory, will think its still installed"
                    )
                    return 1
            else:
                g.feedback.log(LogLevels.ERROR, "Uninstallation failed")
                return 1
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Unable to uninstall: %s" % e)
            return 1

    """
    Returns the install target directory
    """

    def getInstallTargetDirectory(self):
        if self.isDevMode():
            return self.getCurrentDirectory()
        elif self.os is OperatingSystem.OSX:
            return self.getHomeDirectory() + self.sep(
            ) + "Applications" + self.sep(
            ) + self.manifest.jinn.name + ".app" + self.sep(
            ) + "Contents" + self.sep() + "MacOS"
        else:
            return self.getHomeDirectory() + self.getDirectorySeparator(
            ) + self.manifest.jinn.name

    """
    Get the name of the executable
    """

    def getExecutableName(self):
        if self.os == OperatingSystem.WIN:
            return self.manifest.jinn.name + ".jinn.exe"
        elif self.os == OperatingSystem.LIN:
            return self.manifest.jinn.name + ".jinn"
        elif self.os == OperatingSystem.OSX:
            return "jinn"
        return None

    """
    Get the file we wish to install to
    """

    def getInstallTargetFile(self):
        return self.getInstallTargetDirectory() + self.getDirectorySeparator(
        ) + self.getExecutableName()

    """
    Check whether or not we are running from the right place
    """

    def isCorrectDirectory(self):
        currentdir = self.getCurrentDirectory()
        targetdir = self.getInstallTargetDirectory()

        g.feedback.log(LogLevels.DEBUG, "Current directory: %s" % currentdir)
        g.feedback.log(LogLevels.DEBUG, "Target dir: %s" % targetdir)

        return targetdir == currentdir

    """
    Take the sys args and turn them into a string stored on the object
    for later use. Offset is how many to skip off the front.
    Automatically skips the first one, the current script
    """

    def processArgs(self, offset=0):
        args = copy.copy(sys.argv)
        args.pop(0)
        i = 0
        while i < offset:
            args.pop(0)
            i += 1
        self.args = " ".join(map(str, args))
        g.feedback.log(LogLevels.DEBUG, "System args: %s" % self.args)

    """
    Check whether or not this jinn is currently installed
    """

    def isInstalled(self):
        return self.directoryExists(".jinn")

    """
    Output the version information
    """

    def doVersion(self):
        g.feedback.userMessage("""
%s
Version: %s
""" % (self.header, options.version))
        return 0

    """
    Output the help content
    """

    def doHelp(self):
        g.feedback.userMessage("""
%s
Created by import.io
        
Options:
    ./jinn
        Run the jinn with the default action
    ./jinn -install
        Run the jinn installation
    ./jinn -uninstall
        Uninstall the jinn
    ./jinn -help
        Display this helpful help dialog
    ./jinn -action (actionname)
        Run the jinn action specified by (actionname)
    ./jinn -version
        Print the version string
        """ % self.header)
        return 0

    """
    Runs the jinn feature we need to perform
    """

    def do(self):

        # Change to the current run directory
        self.changeDirectory(self.getPathFromFilePath(sys.argv[0]))

        # Analyse the sys args to figure out what to do
        if len(sys.argv) < 2:
            # No extra args, if we are installed we want to run the default action.
            # Otherwise this is the first run from download, so do install
            if self.isInstalled():
                return self.runDefaultAction()
            else:
                return self.doInstall()
        elif sys.argv[1] == "-install":
            return self.doInstall()
        elif sys.argv[1] == "-uninstall":
            return self.doUninstall()
        elif sys.argv[1] == "-help":
            return self.doHelp()
        elif sys.argv[1] == "-version":
            return self.doVersion()
        elif sys.argv[1] == "-action":
            if len(sys.argv) < 3:
                g.feedback.userMessage(
                    "For -action, you must specify an action - try -help")
                return 1
            self.processArgs(2)
            return self.runAction(sys.argv[2])
        else:
            # There are some args, we don't recognise the first, so must want to run default action with some args
            self.processArgs()
            return self.runDefaultAction()
Example #5
0
class Jinn(FileSystemHelper):

    # Header for messages
    header = """
    .---.                           
    |   |                           
    '---'.--.   _..._      _..._    
    .---.|__| .'     '.  .'     '.  
    |   |.--..   .-.   ..   .-.   . 
    |   ||  ||  '   '  ||  '   '  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||__||  |   |  ||  |   |  | 
 __.'   '    |  |   |  ||  |   |  | 
|      '     |  |   |  ||  |   |  | 
|____.'      '--'   '--''--'   '--' 
A multi-platform installer"""

    def __init__(self):
        self.new_manifest = None
        self.manifest = None
        self.args = ""
        self.onInstall = None
        self.runningAction = False
        self.detector = Detector()

        self.os = self.detector.getOperatingSystem()

        if self.isDevMode():
            directory = "install-debug"
            if not os.path.exists(directory):
                os.mkdir(directory)
            os.chdir(directory)

        # Setup feedback mechanism
        global feedback
        if options.interface == FeedbackMechanisms.CMD:
            g.feedback = ConsoleFeedback()
        elif options.interface == FeedbackMechanisms.UI:
            self.ui = True
            g.feedback = QTUIFeedback(self)
        else:
            # Not specified, need something to stop errors, so us this
            g.feedback = FeedbackBase()

        # Setup min log level
        if hasattr(options, "min_log_level"):
            g.feedback.minLogLevel = LogLevels.getLevelFromString(options.min_log_level)
        else:
            g.feedback.minLogLevel = LogLevels.getLevelFromString(LogLevels.INFO)

        # Create PID file
        self.createPidFile()

        self.logSystemInfo()

        g.os = self.os
        self.arch = self.detector.getArchitecture(self.os)
        self.osver = self.detector.getOperatingSystemVersion(self.os)

        g.feedback.log(
            LogLevels.INFO,
            "Started up. OS: %s; Architecture: %s; Version: %s"
            % (OperatingSystem().getOperatingSystem(self.os), Architecture().getArchitecture(self.arch), self.osver),
        )

    def logSystemInfo(self):

        # str() casting necessary here as sometimes returns None

        g.feedback.log(LogLevels.INFO, "Herein follows the system information")
        g.feedback.log(LogLevels.INFO, "sys.version\t\t\t\t%s" % str(sys.version.split("\n")))
        g.feedback.log(LogLevels.INFO, "platform.dist\t\t\t\t%s" % str(platform.dist()))
        g.feedback.log(LogLevels.INFO, "platform.system\t\t\t\t%s" % str(platform.system()))
        g.feedback.log(LogLevels.INFO, "platform.machine\t\t\t%s" % str(platform.machine()))
        g.feedback.log(LogLevels.INFO, "platform.processor\t\t\t%s" % str(platform.processor()))
        g.feedback.log(LogLevels.INFO, "platform.platform\t\t\t%s" % str(platform.platform()))
        g.feedback.log(LogLevels.INFO, "platform.platform\t\t\t%s" % str(platform.release()))
        g.feedback.log(LogLevels.INFO, "platform.uname\t\t\t\t%s" % str(platform.uname()))
        g.feedback.log(LogLevels.INFO, "platform.version\t\t\t%s" % str(platform.version()))
        g.feedback.log(LogLevels.INFO, "platform.win32_ver\t\t\t%s" % str(platform.win32_ver()))
        g.feedback.log(LogLevels.INFO, "platform.mac_ver\t\t\t%s" % str(platform.mac_ver()))
        try:
            g.feedback.log(LogLevels.INFO, "platform.linux_distribution\t\t%s" % str(platform.linux_distribution()))
        except:
            g.feedback.log(LogLevels.INFO, "platform.linux_distribution\t\tnot available")
        g.feedback.log(LogLevels.INFO, "locale.getdefaultlocale\t\t\t%s" % str(locale.getdefaultlocale()))
        g.feedback.log(LogLevels.INFO, "os.name\t\t\t\t\t%s" % str(os.name))
        g.feedback.log(LogLevels.INFO, "sys.argv\t\t\t\t%s" % str(sys.argv))
        g.feedback.log(LogLevels.INFO, "System information complete")

    """
    Called when the Jinn is finished
    Whether that is running, installing, updating, etc.
    This has to be the last thing before the program quits
    """

    def done(self):
        self.deletePidFile()

        if self.os is OperatingSystem.OSX:
            # Run the launch service command
            cmd = [
                "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister",
                "-f",
                ".." + os.sep + "..",
            ]
            g.feedback.log(LogLevels.DEBUG, "Updating launch services: %s" % cmd)
            StartSubprocess(cmd)

        return True

    def run(self):
        g.feedback.finished(self.do())

    """
    Loads the manifest file
    """

    def loadManifest(self):

        installed = self.isInstalled()

        g.feedback.log(LogLevels.INFO, "Loading new manifest from %s" % options.manifest)
        self.new_manifest = Manifest(self.os, self.arch, self.osver, installed, options.manifest)

        if installed:
            self.manifest = Manifest(self.os, self.arch, self.osver, True, None)
        else:
            g.feedback.log(LogLevels.INFO, "Loading manifest from new manifest, as not installed")
            self.manifest = self.new_manifest

    """
    A helper which sets up the system before a run
    """

    def setupSystem(self):

        g.feedback.log(LogLevels.INFO, "Setting up the system")

        self.loadManifest()

        # If there is an old .tmp binary, delete it
        self.delete(self.getInstallTargetFile() + ".tmp")
        if platform.system() == "Darwin":
            self.delete(self.getInstallTargetFile() + "tmp.pkg")

    """
    Create a PID file
    """

    def createPidFile(self):

        if not self.makeDirectory(self.pidsDir()):
            raise "Cannot create pids directory"

        # this also tidies up dead pid files
        self.countRunningInstances()

        print "Creating pid file"
        self.saveToFile(self.getPidFile(), "RUNNING")

    """
    Delete this program's PID file
    """

    def deletePidFile(self):
        if os.path.exists(self.getPidFile()):
            print "Deleting pid file"
            if self.exists(self.getPidFile()):
                self.delete(self.getPidFile())
        else:
            print "Pid file already gone"

    def getPidFile(self, pid=os.getpid()):

        f = os.sep.join([self.pidsDir(), str(pid)])
        print "PID file: %s" % os.path.abspath(f)
        return f

    def countRunningInstances(self):

        # if theres not pids directory then there will be no running instances
        if not os.path.exists(self.pidsDir()):
            return 0

        # GetExitCodeProcess uses a special exit code to indicate that the process is
        # still running.
        _STILL_ACTIVE = 259

        def is_pid_running(pid):
            try:
                return (
                    _is_pid_running_on_windows(pid) if platform.system() == "Windows" else _is_pid_running_on_unix(pid)
                )
            except Exception as e:
                print "Can't work out if a pid is running, presuming yes: %s" % e
                return False

        def _is_pid_running_on_unix(pid):
            try:
                os.kill(pid, 0)
            except OSError:
                return False
            return True

        def _is_pid_running_on_windows(pid):
            import ctypes.wintypes

            kernel32 = ctypes.windll.kernel32
            handle = kernel32.OpenProcess(1, 0, pid)
            if handle == 0:
                return False

            # If the process exited recently, a pid may still exist for the handle.
            # So, check if we can get the exit code.
            exit_code = ctypes.wintypes.DWORD()
            is_running = kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)) == 0
            kernel32.CloseHandle(handle)

            # See if we couldn't get the exit code or the exit code indicates that the
            # process is still running.
            return is_running or exit_code.value == _STILL_ACTIVE

        i = 0

        for pid in os.listdir(self.pidsDir()):
            try:
                pid = int(pid)
            except:
                pass

            g.feedback.log(LogLevels.DEBUG, "Checking PID %s" % pid)
            if is_pid_running(pid):
                i = i + 1
            else:
                f = self.getPidFile(pid)
                print "PID dead, removing %s" % f
                os.remove(f)

        return i

    def pidsDir(self):
        return "pids"

    """
    Runs action in UI mode
    """

    def runActionThread(self, action):

        ret = 1
        try:
            ret = self._runAction(action)
        except Exception as e:
            traceback.print_exc()
            g.feedback.userMessage("ERROR", "Unexpected error: %s" % e)
        finally:
            self.finished(ret)

    """
    Runs a specific action within the jinn
    """

    def runAction(self, action=None):

        self.runningAction = True

        if not self.isInstalled():
            g.feedback.log(LogLevels.INFO, "We aren't installed, so installing")

            def onInstall():
                cmd = copy.copy(sys.argv)
                g.feedback.log(LogLevels.DEBUG, "Run command: %s" % cmd)
                self.done()
                StartProcess(cmd)

            self.onInstall = lambda: onInstall()

            self.doInstall()
            return

        if self.ui:
            return g.feedback.startUI("RUN", "Running")
        else:
            return self._runAction(action)

    def _runAction(self, action=None):

        self.checkAndDoUpdate()

        g.feedback.log(LogLevels.INFO, "Running %s action" % action)
        g.feedback.log(LogLevels.DEBUG, "Run action %s" % action)

        # this will close the wizard UI
        g.feedback.hide(4)

        if platform.system() == "Darwin" and self.ui:

            # we need to still listen for url open events while we're running this action

            import argv_emulation

            def spawn(x):
                print "Got event %s" % x
                if self.manifest.runDefaultAction(x) != 0:
                    g.feedback.onUnexpectedExit()

            def onEvent(x):
                thread = Thread(target=spawn, args=[x])
                thread.start()

            def run():
                print "Hiding dock icon"
                setOSXProcessType(4)  # lsuielement
                print "Waiting for argv timeout"
                # wait the default amount of time that the argv timeout is
                time.sleep(10)
                print "Listening for events......"
                argv_emulation.listen(onEvent)

            thread = Thread(target=run)
            thread.setDaemon(True)
            thread.start()

        try:
            if action is None:
                res = self.manifest.runDefaultAction(self.args)
            else:
                res = self.manifest.runAction(action, self.args)
            if res != 0:
                g.feedback.log(LogLevels.INFO, "Running action %s successfully completed" % action)
                return 1
            return 0
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Running action %s threw exception %s" % (action, e))
            return 1

    """
    Are we in dev mode?
    """

    def isDevMode(self):
        isDev = options.version is "DEV"
        return isDev

    """
    Checks whether an update is available. If so and eligible, starts the update
    """

    def checkAndDoUpdate(self):

        g.feedback.log(LogLevels.DEBUG, "Starting update procedure")
        g.feedback.userMessage("MSG", "Checking for updates")

        # Check the PID files
        if self.countRunningInstances() != 1:
            g.feedback.log(LogLevels.DEBUG, "Won't update because multiple instances")
            return True

        # at this point if there is a critical message set for this OS we need to display it to the user
        criticalMessage = self.manifest.criticalMessage
        if criticalMessage != None:
            g.feedback.userMessage("INFO", "Please read carefully: %s" % criticalMessage)
            self.finished(0)
            return False

        return self.doUpdate()

    """
    Checks the manifests for updates
    """

    def doUpdate(self):

        # Check platforms
        if not self.new_manifest.platformSupported():
            g.feedback.userMessage("ERROR", "Your platform is not supported by this update.%s" % self.getOutputExtras())
            return False

        # Check to see if there is a change in the binary, but not in dev mode
        if (
            self.new_manifest.hasNewBinary(self.manifest.jinn.getBinary(self.os, self.arch, self.osver))
            and not self.isDevMode()
        ):
            return self.doBinaryUpdate()

        g.feedback.log(LogLevels.DEBUG, "Setting installation status across manifests")
        g.feedback.userMessage("MSG", "Setting installation status across manifests")

        g.feedback.log(LogLevels.INFO, "Installing new resources")
        g.feedback.userMessage("MSG", "Installing new resources")
        # First, install resources that are new
        if not self.new_manifest.installNewResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Failed to install new resources")
            return False

        g.feedback.log(LogLevels.INFO, "Uninstalling removed resources")
        g.feedback.userMessage("MSG", "Uninstalling removed resources")
        # Second, uninstall resources that are gone
        if not self.new_manifest.uninstallRemovedResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Removing resources failed")
            return False

        g.feedback.log(LogLevels.INFO, "Updating changed resources")
        g.feedback.userMessage("MSG", "Updating changed resources")
        # Third, update resources that are new version
        if not self.new_manifest.updateResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Updating changed resources failed")
            return False

        g.feedback.log(LogLevels.DEBUG, "Saving manifest")
        g.feedback.userMessage("MSG", "Saving manifest")
        # Finally, transition the manifest over to the new one
        self.manifest = self.new_manifest
        if not self.manifest.save():
            g.feedback.log(LogLevels.ERROR, "Unable to save updated manifest")
            return False

        # Done!
        g.feedback.log(LogLevels.DEBUG, "Update complete")
        return True

    """
    Does a binary update of jinn binary
    """

    def doBinaryUpdate(self):

        g.feedback.userMessage("MSG", "A binary update is available. Downloading...")

        fileUrl = self.new_manifest.jinn.getBinary(self.os, self.arch, self.osver)
        g.feedback.log(LogLevels.DEBUG, "Downloading file from %s" % fileUrl)
        downloader = UrlDownloader(fileUrl)
        downloadTarget = self.getInstallTargetFile() + ".tmp"
        if self.os == OperatingSystem().OSX:
            downloadTarget = self.getInstallTargetFile() + ".pkg.tmp"
        filename = downloader.download(downloadTarget)
        if self.os is OperatingSystem().OSX:
            filename = self.getInstallTargetFile()
        g.feedback.log(LogLevels.DEBUG, "File downloaded to %s" % filename)

        if platform.system() == "Darwin":
            # if on os x we also need the pkg file
            downloader = UrlDownloader(fileUrl + ".pkg")
            filename = downloader.download(self.getInstallTargetFile() + ".tmp.pkg")
            g.feedback.log(LogLevels.DEBUG, "File downloaded to %s" % filename)

        if self.os is not OperatingSystem().OSX:
            if not self.makeExecutable(filename):
                g.feedback.log(LogLevels.ERROR, "Unable to make filename %s executable" % filename)

        args = copy.copy(sys.argv)
        args.pop(0)
        new_args = [filename, "-binaryupdate", self.getCurrentFile()]
        if args is not None and len(args) > 0:
            new_args.extend(args)
        g.feedback.log(LogLevels.DEBUG, "Sys args: %s" % sys.argv)
        g.feedback.log(LogLevels.DEBUG, "New args: %s" % new_args)
        if not self.new_manifest.save():
            g.feedback.log(LogLevels.ERROR, "Unable to save new manifest file")
            self.done()
            self.finished(1)
            return

        g.feedback.userMessage("MSG", "Binary update complete.")

        if type(g.feedback) == QTUIFeedback:
            self.finished(-2)  # tell the UI to completely quit
        self.done()

        StartProcess(new_args)

        sys.exit()

    """
    Does a binary replace,calls the new binary, then exits
    """

    def doBinaryReplace(self):

        # wait for the old process to exit
        time.sleep(1)

        filename = copy.copy(sys.argv[2])

        oldFile = filename
        newFile = self.getCurrentFile()

        if self.os == OperatingSystem().OSX:
            newFile = newFile + ".tmp"
            filename = filename.replace(".pkg", "")

        if not self.delete(oldFile):
            g.feedback.log(LogLevels.ERROR, "Unable to delete old file, %s" % filename)

            self.done()
            self.finished(1)
            return

        if not self.copyFile(newFile, oldFile):
            g.feedback.log(LogLevels.ERROR, "Unable to copy to %s" % filename)
            self.done()
            self.finished(1)
            return

        if platform.system() == "Darwin":
            if not self.copyFile(self.getCurrentFile() + ".pkg", filename + ".pkg"):
                g.feedback.log(LogLevels.ERROR, "Unable to copy to %s" % filename)
                self.done()
                self.finished(1)
                return

        args = copy.copy(sys.argv)
        args.pop(0)
        args.pop(0)
        args.pop(0)
        new_args = [filename]
        if args is not None and len(args) > 0:
            new_args.extend(args)
        g.feedback.log(LogLevels.DEBUG, "Sys args: %s" % sys.argv)
        g.feedback.log(LogLevels.DEBUG, "New args: %s" % new_args)

        self.done()

        time.sleep(1)
        StartProcess(new_args)

        time.sleep(1)
        sys.exit()

    """
    Copy this executable to the target directory, then run it
    """

    def doCopy(self, debug=False):
        g.feedback.log(LogLevels.DEBUG, "We are not in the correct directory, so installing to the correct location")
        g.feedback.userMessage("MSG", "Copying install files")

        if self.os is OperatingSystem.OSX:
            g.feedback.log(LogLevels.DEBUG, "Copying in OS X mode")

            targetDir = self.getInstallTargetDirectory()
            copyTargetDir = targetDir.rsplit("/", 2)[0]
            currentDir = self.getCurrentDirectory() + os.sep + ".." + os.sep + ".."

            executable = targetDir + os.sep + self.getExecutableName()

            g.feedback.log(LogLevels.DEBUG, "Target executable: %s" % executable)

            if not self.exists(executable):
                g.feedback.log(LogLevels.DEBUG, "Target executable does not exist, copying")
                if not self.delete(copyTargetDir):
                    return 1
                if not self.makeDirectory(copyTargetDir):
                    return 1
                if not self.copyDir(currentDir, copyTargetDir):
                    return 1
            else:
                g.feedback.log(LogLevels.DEBUG, "Target executable exists, so running it")

            cmd = copy.copy(sys.argv)
            cmd[0] = "--args"

            # make quiet if not already
            if len(cmd) > 1 and cmd[1] != "-quiet":
                cmd.insert(1, "-quiet")

            cmd.insert(0, self.getInstallTargetDirectory() + os.sep + ".." + os.sep + "..")
            cmd.insert(0, "open")

            g.feedback.log(LogLevels.DEBUG, "Executing command: %s" % cmd)

            if type(g.feedback) == QTUIFeedback:
                self.finished(-1)  # tell the UI to disappear silently

            res = StartSubprocessWithDir(cmd, targetDir)

            if res < 1:
                g.feedback.log(LogLevels.DEBUG, "Executed command successfully")
                return 0
            else:
                g.feedback.log(LogLevels.ERROR, "Executing command %s failed with code %s" % (cmd, res))
                return 1

        else:
            g.feedback.log(LogLevels.DEBUG, "Copying in Windows / Linux mode")
            targetFile = self.getInstallTargetFile(debug)
            g.feedback.log(LogLevels.DEBUG, "Target file is %s" % targetFile)
            d = self.getInstallTargetDirectory(debug)
            g.feedback.log(LogLevels.DEBUG, "Target directory is %s" % d)
            if not self.exists(targetFile):
                # Make the jinn install directory
                if not self.makeDirectory(d):
                    g.feedback.log(LogLevels.ERROR, "Unable to make directory %s to install to" % d)
                    return 1

                # Copy this binary into it
                frm = self.getCurrentFile()
                to = targetFile
                g.feedback.log(LogLevels.DEBUG, "Copying from %s to %s" % (frm, to))
                if not self.copyFile(frm, to):
                    g.feedback.log(LogLevels.ERROR, "Unable to copy %s to %s" % (frm, to))
                    return 1

            if not self.makeExecutable(targetFile):
                g.feedback.log(LogLevels.ERROR, "Unable to make %s executable" % targetFile)
                return 1

            # Run the new executable
            g.feedback.log(LogLevels.DEBUG, "Code copied to %s, executing" % targetFile)

            cmd = copy.copy(sys.argv)
            cmd[0] = self.getInstallTargetDirectory() + os.sep + self.getExecutableName()

            # make quiet if not already
            if len(cmd) > 1 and cmd[1] == "-quiet":
                pass
            else:
                cmd.insert(1, "-quiet")

            g.feedback.log(LogLevels.DEBUG, "Run command: %s" % cmd)

            if type(g.feedback) == QTUIFeedback:
                self.finished(-1)  # tell the UI to disappear silently

            print "FORK in %s: %s" % (d, cmd)

            if debug:
                return 0

            # can't check for success
            StartProcess(cmd)

            return 0

    def doMove(self, debug=False):

        if self.isDevMode() and not debug:
            return True

        ret = self.doCopy(debug)
        if ret != 0:
            return ret
        return self._doUninstall()

    """
    Runs installtion in UI mode
    """

    def doInstallThread(self):
        ret = 1
        try:
            ret = self._doInstall()
            if self.runningAction:
                self.finished(-1)
        except Exception as e:
            traceback.print_exc()
            g.feedback.userMessage("ERROR", "Unexpected error: %s" % e)
        finally:
            self.finished(ret)

    """
    Runs an installation of this jinn
    """

    def doInstall(self):
        if self.ui:
            return g.feedback.startUI("INSTALL", "Beginning installation")
        else:
            return self._doInstall()

    def _doInstall(self):

        if self.countRunningInstances() > 1:
            g.feedback.userMessage("ERROR", "Installer already running")
            return 1

        g.feedback.log(LogLevels.INFO, "Beginning installation")
        g.feedback.userMessage("MSG", "Beginning installation")

        # We need the manifest first of all
        self.loadManifest()

        # Check we are a supported platform
        if not self.manifest.platformSupported():
            g.feedback.userMessage(
                "ERROR", "Your platform is not supported by this application.%s" % self.getOutputExtras()
            )
            return 1

        # Hard code here to not happen for dev
        correctDir = self.isCorrectDirectory()
        g.feedback.log(LogLevels.DEBUG, "Are we in correct directory? %s" % correctDir)
        if not correctDir and not self.isDevMode():
            ret = self.doCopy()
            if ret != 0:
                g.feedback.log(LogLevels.ERROR, "Failed to copy")
                g.feedback.userMessage(
                    "ERROR", "Installation failed - failed to copy resources.%s" % self.getOutputExtras()
                )
            return ret

        # Make sure we are in the right directory
        installDir = self.getInstallTargetDirectory()
        g.feedback.log(LogLevels.DEBUG, "Target install dir is %s" % installDir)
        if not self.changeDirectory(installDir) and not self.isDevMode():
            g.feedback.log(LogLevels.ERROR, "Unable to change to where we thought we were installed, %s" % installDir)
            return 1

        if self.isInstalled():
            g.feedback.log(LogLevels.ERROR, "This jinn is already installed")
            g.feedback.userMessage(
                "ERROR", "Installation failed with error code (3) - already installed!%s" % self.getOutputExtras()
            )
            return 1

        g.feedback.log(LogLevels.DEBUG, "Installing")
        g.feedback.userMessage("MSG", "Installing. Please wait...")
        if not self.makeDirectory(".jinn"):
            g.feedback.log(LogLevels.ERROR, "Unable to make .jinn directory")
            return 1

        try:
            if self.manifest.installResources():
                g.feedback.log(LogLevels.DEBUG, "Install resources succeeded")
                if not self.manifest.save():
                    g.feedback.log(LogLevels.ERROR, "Unable to save the manifest")
                    return 1

                if self.onInstall != None:
                    self.onInstall()

                return 0
            else:
                g.feedback.log(LogLevels.ERROR, "Install resources failed")
                return 1
        except Exception as e:
            traceback.print_exc()
            g.feedback.log(LogLevels.ERROR, "Unable to install resources: %s" % e)
            g.feedback.userMessage("ERROR", "Unable to install resources: %s" % e)
            return 1

    """
    Runs uninstalltion in UI mode
    """

    def doUninstallThread(self):

        ret = 1
        try:
            ret = self._doUninstall()
        except Exception as e:
            traceback.print_exc()
            g.feedback.userMessage("ERROR", "Unexpected error: %s" % e)
        finally:
            self.finished(ret)

    def finished(self, ret):
        g.feedback.log(LogLevels.DEBUG, "Exiting UI Thread with status: %s" % ret)
        g.feedback.finished(ret)

    """
    Runs uninstallation
    """

    def doUninstall(self):

        if self.ui:
            return g.feedback.startUI("UNINSTALL", "Beginning uninstallation")
        else:
            return self._doUninstall()

    def _doUninstall(self):

        g.feedback.log(LogLevels.INFO, "Doing uninstallation")

        if self.countRunningInstances() > 1:
            g.feedback.userMessage("ERROR", "Please shut all instances before trying to uninstall.")
            return 1

        if not self.isInstalled():
            msg = "This is not installed, so cannot be uninstalled."
            g.feedback.log(LogLevels.ERROR, msg)
            g.feedback.userMessage("ERROR", msg)
            return 1

        g.feedback.log(LogLevels.DEBUG, "Uninstalling")

        self.loadManifest()

        try:
            if self.manifest.uninstallResources():
                # remove cache directory
                self.delete(".cache")
                if self.delete(".jinn"):
                    if (self.os == OperatingSystem().LIN or self.os == OperatingSystem().OSX) and not self.isDevMode():
                        self.delete(self.getCurrentDirectory())

                    g.feedback.userMessage("MSG", "Uninstallation finished.")
                    return 0
                else:
                    g.feedback.log(LogLevels.ERROR, "Failed to delete .jinn directory, will think its still installed")
                    return 1
            else:
                g.feedback.log(LogLevels.ERROR, "Uninstallation failed")
                return 1
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Unable to uninstall: %s" % e)
            return 1

    """
    Returns the install target directory
    """

    def getInstallTargetDirectory(self, debug=False):
        if self.isDevMode() and not debug:
            return self.getCurrentDirectory()
        elif self.os is OperatingSystem.OSX:
            return os.sep.join(
                [
                    self.getHomeDirectory(),
                    "Library",
                    "Application Support",
                    self.manifest.jinn.name + ".app",
                    "Contents",
                    "Resources",
                ]
            )
        elif self.os is OperatingSystem.LIN:
            return os.sep.join([self.getHomeDirectory(), ".%s" % self.manifest.jinn.name])
        else:
            return os.sep.join([os.environ["APPDATA"], self.manifest.jinn.name])

    """
    Returns the install target directory
    """

    def getLogFile(self):
        if self.os is OperatingSystem.OSX:
            return os.sep.join([self.getHomeDirectory(), "Library", "Application Support", "jinn", "jinn.log"])
        elif self.os is OperatingSystem.LIN:
            return os.sep.join([self.getHomeDirectory(), ".jinn", "jinn.log"])
        else:
            return os.sep.join([os.environ["APPDATA"], "jinn", "jinn.log"])

    """
    Get the name of the executable
    """

    def getExecutableName(self):
        if self.os == OperatingSystem.WIN:
            return self.manifest.jinn.name + ".exe"
        elif self.os == OperatingSystem.LIN:
            return self.manifest.jinn.name + ".jinn"
        elif self.os == OperatingSystem.OSX:
            return "jinn"
        return None

    """
    Get the file we wish to install to
    """

    def getInstallTargetFile(self, debug=False):
        return os.sep.join([self.getInstallTargetDirectory(debug), self.getExecutableName()])

    """
    Check whether or not we are running from the right place
    """

    def isCorrectDirectory(self, debug=False):
        currentdir = self.getCurrentDirectory()
        targetdir = self.getInstallTargetDirectory(debug)

        g.feedback.log(LogLevels.DEBUG, "Current directory: %s" % currentdir)
        g.feedback.log(LogLevels.DEBUG, "Target dir: %s" % targetdir)

        return targetdir == currentdir

    """
    Take the sys args and turn them into a string stored on the object
    for later use. Offset is how many to skip off the front.
    Automatically skips the first one, the current script
    """

    def processArgs(self, offset=0):
        args = copy.copy(sys.argv)

        # remove unwanteds
        i = 0
        while i < offset:
            args.pop(0)
            i += 1

        self.args = " ".join(map(str, args))
        g.feedback.log(LogLevels.DEBUG, "System args: %s" % self.args)

    """
    Check whether or not this jinn is currently installed
    """

    def isInstalled(self):
        return self.directoryExists(".jinn") and self.exists(manifest.Manifest.getManifestFile())

    """
    Provide some useful output for help, version calls etc
    """

    def getOutputExtras(self):
        extras = ""

        if self.isInstalled():
            self.loadManifest()
            developer = self.manifest.jinn.getDeveloperMessage("You can contact the developer")
            version = self.manifest.jinn.version
            name = self.manifest.jinn.name
            extras = """
This Jinn is installed:
    %s version %s

%s          """ % (
                name,
                version,
                developer,
            )

        return extras

    """
    Output the version information
    """

    def doVersion(self):
        def cb():
            g.feedback.userMessage(
                "INFO",
                """
%s
Version: %s
%s"""
                % (self.header, options.version, self.getOutputExtras()),
            )
            g.feedback.finished(0)

        g.feedback.startUI("INFO", "Info", cb)
        return 0

    """
    List the actions available to the installed application
    """

    def listActions(self):

        if not self.isInstalled():

            def cbt():
                g.feedback.userMessage("ERROR", "This jinn is not installed, so can't list actions")
                g.feedback.finished(0)

            g.feedback.startUI("INFO", "Info", cbt)
            return False

        self.loadManifest()

        actionListing = "Actions: "
        for action in self.manifest.actions:
            actionListing = (
                actionListing
                + """
%s:
    %s: %s
            """
                % (action.id, action.name, action.description)
            )

        def cb():
            g.feedback.userMessage(
                "INFO",
                """
%s
Created by import.io
        
%s
%s        """
                % (self.header, actionListing, self.getOutputExtras()),
            )
            g.feedback.finished(0)

        g.feedback.startUI("INFO", "Info", cb)
        return 0

    """
    Output the help content
    """

    def doHelp(self):
        def cb():
            g.feedback.userMessage(
                "INFO",
                """
%s
Created by import.io

Startup arguments: %s

Options:
    ./jinn
        Run the jinn with the default action
    ./jinn -install
        Run the jinn installation
    ./jinn -uninstall
        Uninstall the jinn
    ./jinn -help
        Display this helpful help dialog
    ./jinn -action (actionname)
        Run the jinn action specified by (actionname)
    ./jinn -version
        Print the version string
    ./jinn -actions
        List all of the available actions and their descriptions
%s      """
                % (self.header, sys.argv, self.getOutputExtras()),
            )
            g.feedback.finished(0)

        g.feedback.startUI("INFO", "Info", cb)
        return 0

    """
    Runs the jinn feature we need to perform
    """

    def do(self):

        self.setupSystem()

        debug = False
        installed = self.isInstalled()

        idx = 1
        if (hasattr(options, "quiet") and options.quiet) or (len(sys.argv) > 1 and sys.argv[1] == "-quiet"):
            g.feedback.quiet()
            idx = 2

        # We want to skip over the move if we are part way through a binary update
        if (
            installed
            and not self.isCorrectDirectory(debug)
            and not (len(sys.argv) >= idx + 1 and sys.argv[idx] == "-binaryupdate")
        ):
            ret = self.doMove(debug)
            if ret != 0:
                g.feedback.log(LogLevels.ERROR, "Failed to move executable to correct location")
                g.feedback.userMessage(
                    "ERROR", "Installation failed - failed to move to new location.%s" % self.getOutputExtras()
                )
            return ret

        # Analyse the sys args to figure out what to do
        if len(sys.argv) < idx + 1:
            # No extra args, if we are installed we want to run the default action.
            # Otherwise this is the first run from download, so do install
            if self.isInstalled():
                return self.runAction()
            else:
                return self.doInstall()
        elif sys.argv[idx] == "-binaryupdate":
            return self.doBinaryReplace()
        elif sys.argv[idx] == "-install":
            idx = idx + 1
            if len(sys.argv) > idx and sys.argv[idx] == "-quiet":
                g.feedback.quiet()
            return self.doInstall()
        elif sys.argv[idx] == "-uninstall":
            return self.doUninstall()
        elif sys.argv[idx] == "-help":
            return self.doHelp()
        elif sys.argv[idx] == "-version":
            return self.doVersion()
        elif sys.argv[idx] == "-actions":
            return self.listActions()
        elif sys.argv[idx] == "-action":
            idx = idx + 1
            if not len(sys.argv) > idx:
                g.feedback.userMessage("ERROR", "For -action, you must specify an action - try -help")
                return 1
            self.processArgs(idx + 1)
            return self.runAction(sys.argv[idx])
        else:
            # There are some args, we don't recognise the first, so must want to run default action with some args
            self.processArgs(idx)
            return self.runAction()
Example #6
0
class Jinn(FileSystemHelper):
    
    # Header for messages
    header = """
    .---.                           
    |   |                           
    '---'.--.   _..._      _..._    
    .---.|__| .'     '.  .'     '.  
    |   |.--..   .-.   ..   .-.   . 
    |   ||  ||  '   '  ||  '   '  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||  ||  |   |  ||  |   |  | 
    |   ||__||  |   |  ||  |   |  | 
 __.'   '    |  |   |  ||  |   |  | 
|      '     |  |   |  ||  |   |  | 
|____.'      '--'   '--''--'   '--' 
A Java installer"""

    def __init__(self):
        self.new_manifest = None
        self.manifest = None
        self.args = ""
        
        # Setup feedback mechanism
        global feedback
        if (options.interface == FeedbackMechanisms.CMD):
            g.feedback = ConsoleFeedback()
        elif (options.interface == FeedbackMechanisms.UI):
            g.feedback = UIFeedback()
        else:
            # Not specified, need something to stop errors, so us this
            g.feedback = FeedbackBase()
            
        # Setup min log level
        g.feedback.minLogLevel = LogLevels.getLevelFromString(options.min_log_level)
            
        self.os = self.getOperatingSystem()
        self.arch = self.getArchitecture()
            
        g.feedback.log(LogLevels.INFO, "Started up. OS: %s; Architecture: %s" % (OperatingSystem().getOperatingSystem(self.os), Architecture().getArchitecture(self.arch)))
    
    """
    Gets the current OS
    """
    def getOperatingSystem(self):
        p = platform.system()
        if p == "Windows":
            return OperatingSystem.WIN
        elif p == "Darwin":
            return OperatingSystem.OSX
        elif p == "Linux":
            return OperatingSystem.LIN
        else:
            g.feedback.log(LogLevels.ERROR, "Unable to work with operating system %s" % p)
            raise OperatingSystemNotFoundException(p)
    
    """
    Gets the current architecture
    """
    def getArchitecture(self):
        if self.os is OperatingSystem.WIN:
            g.feedback.log(LogLevels.DEBUG, "Getting architecture, we are windows, so asking the registry")
            return self.getArchitectureWindows()
        else:
            g.feedback.log(LogLevels.DEBUG, "Working out if we are running 32 or 64 bit")
            if(sys.maxsize > 2**32):
                g.feedback.log(LogLevels.DEBUG, "Decided 64 bit")
                return Architecture.x64
            else:
                g.feedback.log(LogLevels.DEBUG, "Decided 32 bit")
                return Architecture.x32
    
    """
    Windows special case for getting architecture
    """
    def getArchitectureWindows(self):
        from _winreg import *
        softwarekey = OpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE")
        microsoftkey = OpenKey(softwarekey, "Microsoft")        
        windowskey = OpenKey(microsoftkey, "Windows NT")
        versionkey = OpenKey(windowskey, "CurrentVersion")
        value = QueryValueEx(versionkey, "BuildLabEx")[0]
        CloseKey(versionkey)
        CloseKey(windowskey)
        CloseKey(microsoftkey)
        CloseKey(softwarekey)
        if "amd64" in value:
            g.feedback.log(LogLevels.DEBUG, "From value %s, decided we are 64 bit" % value)
            return Architecture.x64
        else:
            g.feedback.log(LogLevels.DEBUG, "From value %s, decided we are 32 bit" % value)
            return Architecture.x32
    
    """
    Loads the manifest file
    """
    def loadManifest(self):
                
        g.feedback.log(LogLevels.INFO, "Loading new manifest from %s" % options.manifest)
        self.new_manifest = Manifest(self.os, self.arch, options.manifest, options.manifest_is_url)
        
        if self.isInstalled():
            manifest_file = ".jinn" + self.sep() + "current_manifest.json"
            g.feedback.log(LogLevels.INFO, "Loading manifest from %s" % manifest_file)
            self.manifest = Manifest(self.os, self.arch, manifest_file, False, True)
        else:
            g.feedback.log(LogLevels.INFO, "Loading manifest from new manifest, as not installed")
            self.manifest = self.new_manifest
    
    """
    A helper which sets up the system before a run
    """
    def setupSystem(self):
        
        g.feedback.log(LogLevels.INFO, "Setting up the system")
        
        # Make sure we are installed
        if not self.isInstalled():
            g.feedback.log(LogLevels.INFO, "We aren't installed, so installing")
            status = self.doInstall()
            if status != 0:
                g.feedback.log(LogLevels.ERROR, "Tried installing but failed horribly")
                g.feedback.userMessage("Installation failed (1) - please contact distributor")
                return status
            g.feedback.log(LogLevels.INFO, "Installation succeeded")
        else:
            self.loadManifest()
            
        return self.doUpdate()
    
    """
    Runs the default action in the jinn
    """
    def runDefaultAction(self):
        g.feedback.log(LogLevels.DEBUG, "Running default action")
        
        self.setupSystem()
        
        g.feedback.log(LogLevels.DEBUG, "Default action execution commencing")
        try:
            self.manifest.runDefaultAction(self.args)
            g.feedback.log(LogLevels.DEBUG, "Run default action executed successfully")
            return 0
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Running default action threw exception %s" % e)
            return 1
    
    """
    Runs a specific action within the jinn
    """
    def runAction(self, action):
        
        g.feedback.log(LogLevels.INFO, "Running %s action" % action)
        self.setupSystem()
        g.feedback.log(LogLevels.DEBUG, "Run action %s" % action)
        try:
            self.manifest.runAction(action, self.args)
            g.feedback.log(LogLevels.INFO, "Running action %s successfully completed" % action)
            return 0
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Running action %s threw exception %s" % (action, e))
            return 1
    
    """
    Are we in dev mode?
    """
    def isDevMode(self):
        isDev = options.version is "DEV"
        g.feedback.log(LogLevels.WARN, "Dev mode? %s" % str(isDev))
        return isDev

    """
    Checks the manifests for updates
    """
    def doUpdate(self):
        
        g.feedback.log(LogLevels.DEBUG, "Starting update procedure")
        
        # No update if the version number is the same
        if self.manifest.jinn.version == self.new_manifest.jinn.version:
            g.feedback.log(LogLevels.DEBUG, "New and current manifest versions are %s and %s, so skipping update as they are identical" % (self.manifest.jinn.version, self.new_manifest.jinn.version))
            return True
        
        g.feedback.log(LogLevels.DEBUG, "Setting installation status across manifests")
        # Initially, set the resources that are installed on the new manifest already
        if not self.new_manifest.setInstallStatus(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Failed to set install status")
            return False
        
        g.feedback.log(LogLevels.INFO, "Installing new resources")
        # First, install resources that are new
        if not self.new_manifest.installNewResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Failed to install new resources")
            return False
        
        g.feedback.log(LogLevels.INFO, "Uninstalling removed resources")
        # Second, uninstall resources that are gone
        if not self.new_manifest.uninstallRemovedResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Removing resources failed")
            return False
        
        g.feedback.log(LogLevels.INFO, "Updating changed resources")
        # Third, update resources that are new version
        if not self.new_manifest.updateResources(self.manifest.resources):
            g.feedback.log(LogLevels.ERROR, "Updating changed resources failed")
            return False
        
        g.feedback.log(LogLevels.DEBUG, "Saving manifest")
        # Finally, transition the manifest over to the new one
        self.manifest = self.new_manifest
        if not self.manifest.save():
            g.feedback.log(LogLevels.ERROR, "Unable to save updated manifest")
            return False
        
        # Done!
        g.feedback.log(LogLevels.DEBUG, "Update complete")
        return True
            

    """
    Copy this executable to the target directory, then run it
    """
    def doCopy(self):
        g.feedback.log(LogLevels.DEBUG, "We are not in the correct directory, so installing to the correct location")
        
        if self.os is OperatingSystem.OSX:
            g.feedback.log(LogLevels.DEBUG, "Copying in OS X mode")
            
            targetDir = self.getInstallTargetDirectory()
            copyTargetDir = targetDir.rsplit("/", 2)[0]
            currentDir = self.getCurrentDirectory() + self.sep() + ".." + self.sep() + ".."
            
            executable = targetDir + self.sep() + self.getExecutableName()
            
            g.feedback.log(LogLevels.DEBUG, "Target executable: %s" % executable)
            
            if not self.exists(executable):
                g.feedback.log(LogLevels.DEBUG, "Target executable does not exist, copying")
                if not self.makeDirectory(copyTargetDir):
                    return False
                if not self.copyDir(currentDir, copyTargetDir):
                    return False
            else:
                g.feedback.log(LogLevels.DEBUG, "Target executable exists, so running it")
            
            cmd = executable + " -install"
            g.feedback.log(LogLevels.DEBUG, "Executing command: %s" % cmd)
            res = os.system(cmd)
            if res < 1:
                g.feedback.log(LogLevels.DEBUG, "Executed command successfully")
                return 0
            else:
                g.feedback.log(LogLevels.ERROR, "Executing command %s failed with code %s" % (cmd, str(res)))
                return 1
            
        else:
            g.feedback.log(LogLevels.DEBUG, "Copying in Windows / Linux mode")
            targetFile = self.getInstallTargetFile()
            g.feedback.log(LogLevels.DEBUG, "Target file is %s" % targetFile)
            d = self.getInstallTargetDirectory()
            g.feedback.log(LogLevels.DEBUG, "Target directory is %s" % d)
            if not self.exists(targetFile):
                # Make the jinn install directory
                if not self.makeDirectory(d):
                    g.feedback.log(LogLevels.ERROR, "Unable to make directory %s to install to" % d)
                    return 1
                
                # Copy this binary into it
                frm = self.getCurrentFile()
                to = targetFile
                g.feedback.log(LogLevels.DEBUG, "Copying from %s to %s" % (frm, to))
                if not self.copyFile(frm, to):
                    g.feedback.log(LogLevels.ERROR, "Unable to copy %s to %s" % (frm,to))
                    return 1
            
            # Change into that directory
            g.feedback.log(LogLevels.DEBUG, "Changing to %s" % d)
            if not self.changeDirectory(d):
                g.feedback.log(LogLevels.ERROR, "Unable to change to the InstallTargetDirectory %s" % d)
                return 1
            
            # Run the new executable
            g.feedback.log(LogLevels.DEBUG, "Code copied to %s, executing" % targetFile)
            cmd = self.getExecutableName() + " -install"
            if self.os == OperatingSystem.LIN or self.os == OperatingSystem.OSX:
                cmd = "./" + cmd
            g.feedback.log(LogLevels.DEBUG, "Run command: %s" % cmd)
            os.system(cmd)
            return 0

    """
    Runs an installation of this jinn
    """
    def doInstall(self):
        
        g.feedback.log(LogLevels.INFO, "Beginning installation")
        
        # We need the manifest first of all
        self.loadManifest()
        
        # Hard code here to not happen for dev
        correctDir = self.isCorrectDirectory()
        g.feedback.log(LogLevels.DEBUG, "Are we in correct directory? %s" % str(correctDir))
        if not correctDir and not self.isDevMode():
            return self.doCopy()
    
        # Make sure we are in the right directory
        installDir = self.getInstallTargetDirectory()
        g.feedback.log(LogLevels.DEBUG, "Target install dir is %s" % installDir)
        if not self.changeDirectory(installDir) and not self.isDevMode():
            g.feedback.log(LogLevels.ERROR, "Unable to change to where we thought we were installed, %s" % installDir)
            return 1
        
        if self.isInstalled():
            g.feedback.log(LogLevels.ERROR, "This jinn is already installed")
            g.feedback.userMessage("Installation failed (3) - please contact distributor")
            return 1
        
        g.feedback.log(LogLevels.DEBUG, "Installing")
        
        if not self.makeDirectory(".jinn"):
            g.feedback.log(LogLevels.ERROR, "Unable to make .jinn directory")
            return 1
        
        if not self.manifest.save():
            g.feedback.log(LogLevels.ERROR, "Unable to save the manifest")
            return 1
        
        try:
            if self.manifest.installResources():
                g.feedback.log(LogLevels.DEBUG, "Install resources succeeded")
                return 0
            else:
                g.feedback.log(LogLevels.ERROR, "Install resources failed")
                return 1
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Unable to install resources: %s" % e)
            return 1
    
    """
    Runs uninstallation
    """
    def doUninstall(self):
        
        g.feedback.log(LogLevels.INFO, "Doing uninstallation")
        
        if not self.isInstalled():
            g.feedback.log(LogLevels.ERROR, "This jinn is not installed, so cannot be uninstalled")
            g.feedback.userMessage("Uninstallation failed (4) - please contact distributor")
            return 1

        g.feedback.log(LogLevels.DEBUG, "Uninstalling")
        
        self.loadManifest()
        
        try:
            if self.manifest.uninstallResources():
                if self.delete(".jinn"):
                    g.feedback.userMessage("Uninstallation finished. To completely erase this application, please delete the directory %s" % self.getInstallTargetDirectory())
                    return 0
                else:
                    g.feedback.log(LogLevels.ERROR, "Failed to delete .jinn directory, will think its still installed")
                    return 1
            else:
                g.feedback.log(LogLevels.ERROR, "Uninstallation failed")
                return 1
        except Exception as e:
            g.feedback.log(LogLevels.ERROR, "Unable to uninstall: %s" % e)
            return 1
    
    """
    Returns the install target directory
    """
    def getInstallTargetDirectory(self):
        if self.isDevMode():
            return self.getCurrentDirectory()
        elif self.os is OperatingSystem.OSX:
            return self.getHomeDirectory() + self.sep() + "Applications" + self.sep() + self.manifest.jinn.name + ".app" + self.sep() + "Contents" + self.sep() + "MacOS"
        else:
            return self.getHomeDirectory() + self.getDirectorySeparator() + self.manifest.jinn.name

    """
    Get the name of the executable
    """
    def getExecutableName(self):
        if self.os == OperatingSystem.WIN:
            return self.manifest.jinn.name + ".jinn.exe"
        elif self.os == OperatingSystem.LIN:
            return self.manifest.jinn.name + ".jinn"
        elif self.os == OperatingSystem.OSX:
            return "jinn"
        return None

    """
    Get the file we wish to install to
    """
    def getInstallTargetFile(self):
        return self.getInstallTargetDirectory() + self.getDirectorySeparator() + self.getExecutableName()

    """
    Check whether or not we are running from the right place
    """
    def isCorrectDirectory(self):
        currentdir = self.getCurrentDirectory()
        targetdir = self.getInstallTargetDirectory()
        
        g.feedback.log(LogLevels.DEBUG, "Current directory: %s" % currentdir)
        g.feedback.log(LogLevels.DEBUG, "Target dir: %s" % targetdir)
        
        return targetdir == currentdir
    
    """
    Take the sys args and turn them into a string stored on the object
    for later use. Offset is how many to skip off the front.
    Automatically skips the first one, the current script
    """
    def processArgs(self, offset = 0):
        args = copy.copy(sys.argv)
        args.pop(0)
        i = 0
        while i < offset:
            args.pop(0)
            i += 1
        self.args = " ".join(map(str, args))
        g.feedback.log(LogLevels.DEBUG, "System args: %s" % self.args)
    
    """
    Check whether or not this jinn is currently installed
    """
    def isInstalled(self):
        return self.directoryExists(".jinn")
    
    """
    Output the version information
    """
    def doVersion(self):
        g.feedback.userMessage("""
%s
Version: %s
""" % (self.header, options.version))
        return 0
    
    """
    Output the help content
    """
    def doHelp(self):
        g.feedback.userMessage("""
%s
Created by import.io
        
Options:
    ./jinn
        Run the jinn with the default action
    ./jinn -install
        Run the jinn installation
    ./jinn -uninstall
        Uninstall the jinn
    ./jinn -help
        Display this helpful help dialog
    ./jinn -action (actionname)
        Run the jinn action specified by (actionname)
    ./jinn -version
        Print the version string
        """ % self.header)
        return 0
    
    """
    Runs the jinn feature we need to perform
    """
    def do(self):
        
        # Change to the current run directory
        self.changeDirectory(self.getPathFromFilePath(sys.argv[0]))
        
        # Analyse the sys args to figure out what to do
        if len(sys.argv) < 2:
            # No extra args, if we are installed we want to run the default action.
            # Otherwise this is the first run from download, so do install
            if self.isInstalled():
                return self.runDefaultAction()
            else:
                return self.doInstall()
        elif sys.argv[1] == "-install":
            return self.doInstall()
        elif sys.argv[1] == "-uninstall":
            return self.doUninstall()
        elif sys.argv[1] == "-help":
            return self.doHelp()
        elif sys.argv[1] == "-version":
            return self.doVersion()
        elif sys.argv[1] == "-action":
            if len(sys.argv) < 3:
                g.feedback.userMessage("For -action, you must specify an action - try -help")
                return 1
            self.processArgs(2)
            return self.runAction(sys.argv[2])
        else:
            # There are some args, we don't recognise the first, so must want to run default action with some args
            self.processArgs()
            return self.runDefaultAction()