#!/usr/bin/python -u
#
# Command line wrapper for Update Agent
# Copyright (c) 1999-2001 Red Hat, Inc.  Distributed under GPL.
#
# Author: Preston Brown <pbrown@redhat.com>

import sys, os
import rpm
import getopt

sys.path.append("/usr/share/rhn/up2date")

from translate import _, N_, cat
import up2date
try:
    import lilocfg
except ImportError:
    lilocfg = None

global isatty
isatty = None

def showHelp():
    print _("Usage: up2date [options] [package names]")
    print ""
    print _("Available command line options:")
    print _("    --configure        - graphically configure Update Agent options")
    print _("""-d, --download         - download packages only, even if configuration says
                         to install.""")
    print _("    --dbpath=<dir>     - Specify a path where an alternate rpm db is found")
    print _("""-f, --force            - Force package installation, overriding package
                         and file skip list""")
    print _("""-i, --install          - install packages, even if configuration says to
                         download only.""")
    print _("    --justdb           - Only add packages to database, do not install to the filesystem")
    print _("-l, --list             - list packages available for retrieval")
    print _("-h, --help             - this help")
    print _("""-k, --packagedir       - colon separated path of directories
                         to look in for packages""")

    print _("    --nosig            - do not use GPG to check package signatures")
    print _("    --nox              - do not attempt to use X. ")
    print _("""-p, --packages         - update packages associated with this System Profile
                         on Red Hat Network""")
    print _("""    --showall	       - List all packages available for download""")
    print _("""    --solvedeps=<deps>
                       - finds, downloads, and installs the 
                         packages needed to solve the list of deps""")
    print _("    --tmpdir=<dir>     - where to store temporary files / RPMs")
    print _("-u, --update           - update system with all relevant packages")
    print _("--version              - show program version information")
    print _("-v, --verbose          - Show additional output")
    print _("""    --whatprovides=<deps>
                       - shows the packages which solve the comma
                         separated list of deps""")
    print ""
    print _("""When operating in command line mode, specifying package names as arguments to
the program will attempt to retrieve (and possibly install, based on your
configuration) those packages.  Version, release, and architecture details will
be determined by the Update Agent automatically.""")


def selectPackages(pkgNames, availablePackages):
    selectedPackages = []

    for pkg in availablePackages:
        if pkg[0] in pkgNames:
            selectedPackages.append(pkg)

    return selectedPackages

def selectPackages2(pkgNames): 
    selPackages = []
    try:
        allPackages = up2date.getAvailablePackageList()
    except:
        print _("Error getting available package list")
        return selPackages

    for pkg in allPackages:
        if pkg[0] in pkgNames:
            selPackages.append(pkg)


    return selPackages


def printit(a):
    print "\n" + a + "..."

lastPercent = 0
def percent(amount, total):
    global lastPercent
    hashesTotal = 40
    
    if total:
        hashesNeeded = int(hashesTotal * (float(amount) / total))
    else:
        hashesNeeded = hashesTotal
    
    # dont print if were not running on a tty
    if isatty and (hashesNeeded > lastPercent or amount == total):
        for i in range(hashesNeeded):
            sys.stdout.write('#')

        sys.stdout.write('\r')
        
        if amount == total:
            print
            
    if amount == total:
        lastPercent = 0
    else:
        lastPercent = hashesNeeded


def printPkg(name, shortName = None):
    if shortName:
        print "%-27.27s " % (shortName + ":"),
    else:
        print "%-27.27s " % (name + ":")

def printRetrieveHash(amount, total, speed = 0, secs = 0):
    hashesTotal = 26
    
    if total:
        percent = int(100 * (float(amount) / total))
        hashesNeeded = int(hashesTotal * (float(amount) / total))
    else:
        percent = 100
        hashesNeeded = hashesTotal

    if isatty:
        for i in range(hashesNeeded):
            sys.stdout.write('#')

        for i in range(hashesNeeded, hashesTotal):
            sys.stdout.write(' ')

    if isatty:
        if amount == total:
            print "%-25s" % " Done."
        else:
            print "%4d k/sec, %02d:%02d:%02d rem." % \
                  (speed / 1024, secs / (60*60), (secs % 3600) / 60,
                   secs % 60),
            for i in range(hashesTotal + 25):
                sys.stdout.write("\b")
    elif amount == total:
        print "Retrieved."
                


hashesPrinted = 0
progressCurrent = 0
progressTotal = 0
packagesTotal = 0
# ported from C code in RPM 2/28/01 -- PGB
def printRpmHash(amount, total):
    hashesTotal = 44

    if total:
        percent = int(100 * (float(amount) / total))
    else:
        percent = 100

    global lastPercent
    if percent <= lastPercent:
        return

    lastPercent = percent
    
    global hashesPrinted
    if (hashesPrinted != hashesTotal):
        if total:
            hashesNeeded = int(hashesTotal * (float(amount) / total))
        else:
            hashesNeeded = hashesTotal
            
        if isatty:
            for i in range(hashesNeeded):
                sys.stdout.write('#')
        
            for i in range(hashesNeeded,hashesTotal):
                sys.stdout.write(' ')
                
            print "(%3d%%)" % percent,
            for i in range(hashesTotal + 6):
                sys.stdout.write("\b")

        hashesPrinted = hashesNeeded
        if hashesPrinted == hashesTotal:
            if isatty:
                global progressCurrent, progressTotal
                progressCurrent = progressCurrent + 1
                for i in range(1,hashesTotal):
                    sys.stdout.write("#")
                if progressTotal:
                    print " [%3d%%]" % int(100 * (float(progressCurrent) /
                                                  progressTotal))
                else:
                    print " [%3d%%]" % 100

    sys.stdout.flush()

fd = 0
def rpmCallback(what, amount, total, hdr, path):
    if what == rpm.RPMCALLBACK_INST_OPEN_FILE:
        global fd
        fileName = "%s/%s-%s-%s.%s.rpm" % (path,
                                           hdr['name'],
                                           hdr['version'],
                                           hdr['release'],
                                           hdr['arch'])
        try:
            fd = os.open(fileName, os.O_RDONLY)
        except:
            raise up2date.RpmError(_("Error opening %s") % fileName)

        return fd
    elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
        os.close(fd)
        fd = 0
        
    elif what == rpm.RPMCALLBACK_INST_START:
        global hashesPrinted, lastPercent
        hashesPrinted = 0
        lastPercent = 0
        fileName = "%s/%s-%s-%s.%s.rpm" % (path,
                                           hdr['name'],
                                           hdr['version'],
                                           hdr['release'],
                                           hdr['arch'])
        if isatty:
            global progressCurrent
            print "%4d:%-23.23s" % (progressCurrent + 1, hdr['name']),
            sys.stdout.flush()
        else:
            printit(_("Installing %s") % fileName)
            
    elif (what == rpm.RPMCALLBACK_TRANS_PROGRESS or
          what == rpm.RPMCALLBACK_INST_PROGRESS):
        printRpmHash(amount, total)

    elif what == rpm.RPMCALLBACK_TRANS_START:
        global progressTotal, progressCurrent, hashesPrinted, lastPercent
        hashesPrinted = 0
        lastPercent = 0
        progressTotal = 1
        progressCurrent = 0
        print "%-28s" % _("Preparing..."),
        sys.stdout.flush()

    elif what == rpm.RPMCALLBACK_TRANS_STOP:
        printRpmHash(1, 1)
        global progressTotal, progressCurrent, packagesTotal
        progressTotal = packagesTotal
        progressCurrent = 0
        
    elif (what == rpm.RPMCALLBACK_UNINST_PROGRESS or
          what == rpm.RPMCALLBACK_UNINST_START or
          what == rpm.RPMCALLBACK_UNINST_STOP):
        pass

def showVersion(hasGui):
    if hasGui:
        try:
            import gui
            gui.showAbout()
        except:
            print _("Unable to open gui. Try specifying \"--nox\" as an option")
    else:
        print "Red Hat Update Agent v%s" % up2date.version()
        print "Copyright (c) 1999-2001 Red Hat, Inc."
        print _("Licensed under terms of the GPL.")

def rootWarning(hasGui):
    errMsg =  _("You must run the Update Agent as root.")
    if hasGui:
        try:
            import gui
            gui.rootWarning()
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
    else:
        print errMsg

def gpgWarning1(hasGui):
    errMsg =  _("""GPG does not appear to be installed.  Without it, you will be unable to
verify that packages Update Agent downloads are securely signed by a
trusted source.

Your Update Agent options specify that you want to use GPG.  Aborting.""")

    if hasGui:
        try:
            import gui
            gui.errorWindow(errMsg)
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
    else:
        print errMsg

def gpgWarning2(hasGui):
    def cb(arg):
        return arg

    errMsg = _("""Your GPG keyring does not contain the Red Hat, Inc. public key.
Without it, you will be unable to verify that packages Update Agent downloads
are securely signed by Red Hat.

Your Update Agent options specify that you want to use GPG.

To install the key, run the following as root:

    /usr/bin/gpg --import /usr/share/rhn/RPM-GPG-KEY""")

    if hasGui:
        try:
            import gui, gnome, gnome.ui
            dlg = gnome.ui.GnomeQuestionDialog(errMsg+_("\n\nInstall key?"),
                                               cb)
            if not dlg.run_and_close():
                if up2date.addGPGKey():
                    dlg = gnome.ui.GnomeErrorDialog(_("Some sort of error occurred adding the Red Hat\nGPG key to your keyring."))
                    dlg.run_and_close()
                    return 1
                else:
                    return 0
                
            return 1
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
            print
            return 1
    else:
        print errMsg
        print
        return 1

def sslWarning(hasGui):
    def cb(arg):
        return arg

    errMsg = _("""Your system libraries do not support SSL (secure) connections.  Any data
that you send or receive from redhat.com will be transmitted in the clear.

""")

    if hasGui:
        try:
            import gui, gnome, gnome.ui
            dlg = gnome.ui.GnomeQuestionDialog(errMsg + _("Continue anyway?"),
                                               cb)
            if dlg.run_and_close():
                return 1
            else:
                return 0
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
            return 1
    else:
        print errMsg
        return 1

def registeredWarning(hasGui):
    if hasGui:
        try:
            import gui
            gui.errorWindow(_("""You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

To register, run \"rhn_register\"."""))
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print _("""You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

To register, run \"rhn_register\".""")
            return 1
    else:
        print _("""You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

To register, run \"rhn_register\".""")
    return 1


def whatprovides(depslist,returnlist=None):
    import string
    tmp_list = string.split(depslist, ",")
    deps_list=[]
    # a little text munging to make this commandline friendlier
    for i in tmp_list:
        i=string.strip(i)
        deps_list.append(i)
    try:
        provides = up2date.solveDep(deps_list)
    except up2date.RpmError:
        print "Unable to solve deps for  %s" %         deps_list       
        return None
    if returnlist:
        pkgnames = []
        for provide in provides:
            pkgnames.append(provide[0])
        return pkgnames
    for provide in provides:
        print "%s-%s-%s" % (provide[0], provide[1],provide[2])


def comps_packages(compslist):
    import string
    tmp_list = string.split(compslist, ",")
    comps_list=[]
    # a little text munging to make this commandline friendlier
    for i in tmp_list:
        i=string.strip(i)
        print ":%s:" % i
        comps_list.append(i)
    #try:
    packages = up2date.comps_packages(comps_list)
    #except:
        #FIXME handle whatever faults I add here
    #    return []
    return packages
        

def printList(packageList):
	packageList.sort()
	for pkg in packageList:
            print "%s-%s-%s" % (pkg[0],pkg[1],pkg[2])
    
def batchRun(onlyList, pkgNames, fullUpdate = 0, fromDaemon = 0,actionPkgs=None):
    try:
        up2date.maybeUpdateVersion()
    except up2date.RpmError, e:
        print _("There was a fatal RPM error.  The message was:\n") + e.errmsg
        return 1
    except up2date.CommunicationError, e:
        print _("There was a fatal error communicating with the server.  The message was:\n") + e.errmsg
        return 1

    # quiet mode for rhn_check
    if fromDaemon:
        printCallback = None
        percentCallback = None
    else:
        printCallback = printit
        percentCallback = percent
        
    availUpdates = []
    if not fromDaemon:
        try:
            availUpdates, skippedUpdates = up2date.getUpdatedPackageList(pkgNames,
                                                                         printCallback, percentCallback)
        except up2date.RpmError, e:
            print _("There was a fatal RPM error.  The message was:\n") + e.errmsg
            return 1
        except up2date.CommunicationError, e:
            print _("There was a fatal error communicating with the server.  The message was:\n") + e.errmsg
            return 1

        if not len(availUpdates) + len(skippedUpdates) and not len(pkgNames):
            print _("There are no packages available for update.")
            return 1

    
        if onlyList:
            if len(availUpdates):
                print _("""
                
Name                                    Version        Rel
--------------------------------------------------------------""")

                for pkg in availUpdates:
                    print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
                    if up2date.cfg.readEntry("verbose"):
                        advisories = up2date.getAdvisoryInfo(pkg)
                        if advisories:
                            for a in advisories:
                                print "[%s] %s" % (a['advisory'],
                                                   a['topic'])
                        else:
                            print "No advisory information available"
                        
                print

        if (len(skippedUpdates)):
             print _("The following Packages were marked to be skipped by your configuration:")
             print _("""
Name                                    Version        Rel  Reason
-------------------------------------------------------------------------------""")
             for pkg,reason in skippedUpdates:
                    print "%-40s%-15s%-5s%s" % (pkg[0], pkg[1], pkg[2], reason)
                    if up2date.cfg.readEntry("verbose"):
                        advisories = up2date.getAdvisoryInfo(pkg)
                        if advisories:
                            for a in advisories:
                                print "[%s] %s" % (a['advisory'],
                                                   a['topic'])
                        else:
                            print "No advisory information available"
             print
            
	if onlyList:
		return 0

    # running from the daemon, no need to do a listall, etc
    else:
        (availUpdates,skippedPackages) = up2date.getUpdatedPackageList([],None,None,availList=actionPkgs)


    # if we are in full update mode, append all available relevant packages
    if fullUpdate:
        pkgNames = []
        for pkg in availUpdates:
            pkgNames.append(pkg[0])

    
    if len(pkgNames):
        if not fromDaemon:
            selPkgList = selectPackages(pkgNames, availUpdates)
        else:
            selPkgList = selectPackages2(pkgNames)

        if not len(selPkgList):
            print _("None of the packages you requested were found, or they are already updated.")
            return 1
            
        totalSize = 0
        for pkg in selPkgList:
            totalSize = totalSize + pkg[4]

        if totalSize > up2date.freeDiskSpace():
            print _("The total size of the selected packages (%d kB) exceeds your free disk space (%d kB).") % (totalSize / 1024, up2date.freeDiskSpace() / 1024)
            return 1

        try:
            (depPackages,conflicts) = up2date.dryRun(selPkgList, printCallback, percentCallback)
        except up2date.CommunicationError, e:
            print _("There was a fatal error communicating with the server.  The message was:\n") + e.errmsg
            return 1
        except up2date.RpmError, e:
            print _("There was a fatal RPM error.  The message was:\n") + e.errmsg
            return 1
        except up2date.DependencyError, e:
            print _("There was a package dependency problem.  The message was:\n") + e.errmsg
            return 1
        except up2date.ConflictError, e:
            print e
            return 1

        if len(conflicts):
            print _("The following conflicts were found:")
            print _("""
New package               conflicts with      installed package
-------------------------------------------------------------""")
            for conf in conflicts:
                print "%s-%s-%s%40s" % (conf[0], conf[1],conf[2], conf[3])
            return 0        

        if len(depPackages):
            print _("The following packages were added to your selection to satify dependencies:")
            print _("""
Name                                    Version        Release
--------------------------------------------------------------""")
            for pkg in depPackages:
                print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
            print

        print
        # damn translations, let's not break them.
        if not fromDaemon:
            print _("Retrieving selected packages."),
            print "\b.."
        for pkg in selPkgList:
            try:
                up2date.getPackage(pkg, printPkg, printRetrieveHash)
            except up2date.CommunicationError, e:
                print e
                sys.exit(1)

            # do signature checking only if GPG installation checks out,
            # and GPG checking is enabled.
            if up2date.cfg.readEntry("useGPG") and not up2date.checkGPGInstallation():
                try:
                    res = up2date.hasBadSignature(pkg)
                except up2date.RpmError, e:
                    print e
                    sys.exit(1)
                if res == 1:
                    print _("The package %s is not signed with a GPG signature.  Aborting...") % up2date.pkgToString(pkg)
                    return 1
                if res == 2:
                    _("The package %s does not have a valid GPG signature.\nIt has been tampered with or corrupted.  Aborting...") % up2date.pkgToString(pkg)
                    return 1

	kernelsToInstall = []
        if not up2date.cfg.readEntry("retrieveOnly"):
            try:
                global packagesTotal
                packagesTotal = len(selPkgList)
                kernelsToInstall = up2date.installPackages(selPkgList, rpmCallback)
            except up2date.RpmError, e:
                print e
                return 1

            if not up2date.cfg.readEntry("keepAfterInstall"):
                for pkg in selPkgList:
                    try:
                        up2date.removePackage(pkg)
                    except up2date.FileError, e:
                        print e
                        return 1

        if len(kernelsToInstall) and lilocfg:
            try:
                up2date.installBootLoader(kernelsToInstall)
            except lilocfg.LiloConfError, info:
                print "%s" % info
            except lilocfg.LiloInstallError, info:
                print "%s" % info                

        return 0
    
            
def main(arglist = [], fromDaemon = 0):
    onlyList = 0
    hasGui = 0
    nox = 0
    nogui = 0
    showversion = 0
    pkgNames = []
    depslist = []
    solvedepslist = []
    comps_list = []
    test = 0
    fullUpdate = 0
    configure = 0

    if not len(arglist):
        arglist = sys.argv[1:]
    try:
        optlist, args = getopt.getopt(arglist,
                                      'adihlpuvtk:sf',
                                      ['allpackages', 'configure',
                                       'download', 'install', 'help',
                                       'list', 'nosig', 'packages',
                                       'tmpdir=',
                                       'update','packagedir=',
                                       'whatprovides=', "solvedeps=",
                                       'showall', "nox", 'dbpath=',
                                       'verbose', 'justdb',
#                                      'comps=','listcomps',
                                       'version', 'test', 'force'])
    except getopt.error, e:
        print _("Error parsing command line arguments: %s") % e
        showHelp()
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if args:
        pkgNames = args

    if os.path.basename(sys.argv[0]) == "up2date-nox":
	nox = 1

    if os.path.basename(sys.argv[0]) == "up2date-config":
        configure = 1

    # detect if were running on a tty or not
    global isatty
    if sys.stdout.isatty():
	    isatty = 1
    else:
	    isatty = None

    for opt in optlist:
	if opt[0] == "-s" or opt[0] == "--showall":
	    printList(up2date.getAvailablePackageList())
	    return 0 
        if opt[0] == "--configure":
            configure = 1
        if opt[0] == "-d" or opt[0] == "--download":
            up2date.cfg.writeEntry("retrieveOnly", 1)
	if opt[0] == "-f" or opt[0] == "--force":
	    up2date.cfg.writeEntry("forceInstall", 1)
        if opt[0] == "-k" or opt[0] == "--packagedir":
            up2date.cfg.writeEntry("packageDir",opt[1])
        if opt[0] == "-i" or opt[0] == "--install":
            up2date.cfg.writeEntry("retrieveOnly", 0)
        if opt[0] == "-h" or opt[0] == "--help":
            showHelp()
            if fromDaemon:
                return 0
            else:
                sys.exit(0)
        if opt[0] == "-l" or opt[0] == "--list":
            onlyList = 1
        if opt[0] == "--nosig":
            up2date.cfg.writeEntry("useGPG", 0)
        if opt[0] == "-p" or opt[0] == "--packages":
            print _("Updating package profile...")
            up2date.updatePackageProfile()
            return 0
        if opt[0] == "--tmpdir":
            up2date.cfg.writeEntry("storageDir", opt[1])
        if opt[0] == "-u" or opt[0] == "--update":
            fullUpdate = 1
	if opt[0] == "--nox":
	    nox = 1
        if opt[0] == opt[0] == "--version":
	    showversion = 1
        if opt[0] == "--whatprovides":
            depslist = opt[1]
            up2date.cfg.writeEntry("depslist", opt[1])
        if opt[0] == "-v" or opt[0] == "--verbose":
            up2date.cfg.writeEntry("verbose", 1)
        if opt[0] == "--solvedeps":
            solvedepslist = opt[1]
        if opt[0] == "--dbpath":
            up2date.cfg.writeEntry("dbpath", opt[1])
            print up2date.cfg.readEntry("dbpath")
        if opt[0] == "--justdb":
            up2date.cfg.writeEntry("justdb",1)

            # the raw list gets passed to whatprovides() for better parsing
#        if opt[0] == "--comps":
#            print opt[1]
#            comps_list = opt[1]

        if opt[0] == "-t" or opt[0] == "--test":
            test = 1

    try:
        if os.access("/usr/share/rhn/up2date/gui.py", os.R_OK):
            if os.environ["DISPLAY"] != "":
		if not nox:
                	hasGui = 1
    except:        
        pass

    if showversion:
	showVersion(hasGui)
        if fromDaemon:
            return 0
        else:
            sys.exit(0)

    if os.geteuid() != 0 and not test:
        rootWarning(hasGui)
        if fromDaemon:
            return 1
        else:
            sys.exit(1)


    if configure:
        if hasGui:
            try:
                import configdlg
            except:
                hasGui = 0
            if hasGui:
                configdlg.main()
                if fromDaemon:
                    return 0
                else:
                    sys.exit(0)
        if not hasGui:
            import text_config
            text_config.main()
            if fromDaemon:
                return 1
            else:
                sys.exit(1)
            
    if up2date.getSystemId() == None:
        registeredWarning(hasGui)
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 1:
        gpgWarning1(hasGui)
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 2:
        if gpgWarning2(hasGui):
            if fromDaemon:
                return 1
            else:
                sys.exit(1)

    if not up2date.hasSSL():
        if sslWarning(hasGui):
            if fromDaemon:
                return 1
            else:
                sys.exit(1)
                
    if len(depslist):
       return whatprovides(depslist)
       
    if len(solvedepslist):
        if not pkgNames:
            pkgNames = []
        # maybe this will handle comboa cases of packages and deps specified?? NEEDSTEST
        extraPackages = whatprovides(solvedepslist, 1)
        if extraPackages:
            pkgNames = pkgNames + extraPackages

    if (len(comps_list) and (version >= 3.0)):
        if not pkgNames:
            pkgNames = []
        # maybe this will handle comboa cases of packages and deps specified?? NEEDSTEST
        pkgNames = pkgNames + comps_packages(comps_list)

    if onlyList or len(pkgNames) or fullUpdate:
        if fromDaemon:
            return batchRun(onlyList, pkgNames, fullUpdate, fromDaemon)
        else:
            sys.exit(batchRun(onlyList, pkgNames, fullUpdate))
    else:
        if hasGui:
	    try:
            	import gui
                nogui = 0
	    except RuntimeError:
                # if we fail to initialize the interface, fall back to tui
                nogui = 1
                
	    if not nogui:
            	gui.main()	
            	sys.exit(0)
                
        print _("No interactive mode available")
        print _("Please specify either -l, -u, --nox, or package names as command line arguments.")
        showHelp()
        if fromDaemon:
            return 1
        else:
            sys.exit(1)
        
        
if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print "\nAborted."
        sys.exit(1)
    except OSError, e:
        print _("An unexpected OS error occurred: %s") % e
        sys.exit(1)
    except IOError, e:
        print _("There was some sort of I/O error: %s") % e
        sys.exit(1)
