summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/python-lib/mozrunner
diff options
context:
space:
mode:
Diffstat (limited to 'addon-sdk/source/python-lib/mozrunner')
-rw-r--r--addon-sdk/source/python-lib/mozrunner/__init__.py694
-rw-r--r--addon-sdk/source/python-lib/mozrunner/killableprocess.py329
-rw-r--r--addon-sdk/source/python-lib/mozrunner/qijo.py166
-rw-r--r--addon-sdk/source/python-lib/mozrunner/winprocess.py379
-rw-r--r--addon-sdk/source/python-lib/mozrunner/wpk.py80
5 files changed, 0 insertions, 1648 deletions
diff --git a/addon-sdk/source/python-lib/mozrunner/__init__.py b/addon-sdk/source/python-lib/mozrunner/__init__.py
deleted file mode 100644
index 87c2c320f..000000000
--- a/addon-sdk/source/python-lib/mozrunner/__init__.py
+++ /dev/null
@@ -1,694 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-import os
-import sys
-import copy
-import tempfile
-import signal
-import commands
-import zipfile
-import optparse
-import killableprocess
-import subprocess
-import platform
-import shutil
-from StringIO import StringIO
-from xml.dom import minidom
-
-from distutils import dir_util
-from time import sleep
-
-# conditional (version-dependent) imports
-try:
- import simplejson
-except ImportError:
- import json as simplejson
-
-import logging
-logger = logging.getLogger(__name__)
-
-# Use dir_util for copy/rm operations because shutil is all kinds of broken
-copytree = dir_util.copy_tree
-rmtree = dir_util.remove_tree
-
-def findInPath(fileName, path=os.environ['PATH']):
- dirs = path.split(os.pathsep)
- for dir in dirs:
- if os.path.isfile(os.path.join(dir, fileName)):
- return os.path.join(dir, fileName)
- if os.name == 'nt' or sys.platform == 'cygwin':
- if os.path.isfile(os.path.join(dir, fileName + ".exe")):
- return os.path.join(dir, fileName + ".exe")
- return None
-
-stdout = sys.stdout
-stderr = sys.stderr
-stdin = sys.stdin
-
-def run_command(cmd, env=None, **kwargs):
- """Run the given command in killable process."""
- killable_kwargs = {'stdout':stdout ,'stderr':stderr, 'stdin':stdin}
- killable_kwargs.update(kwargs)
-
- if sys.platform != "win32":
- return killableprocess.Popen(cmd, preexec_fn=lambda : os.setpgid(0, 0),
- env=env, **killable_kwargs)
- else:
- return killableprocess.Popen(cmd, env=env, **killable_kwargs)
-
-def getoutput(l):
- tmp = tempfile.mktemp()
- x = open(tmp, 'w')
- subprocess.call(l, stdout=x, stderr=x)
- x.close(); x = open(tmp, 'r')
- r = x.read() ; x.close()
- os.remove(tmp)
- return r
-
-def get_pids(name, minimun_pid=0):
- """Get all the pids matching name, exclude any pids below minimum_pid."""
- if os.name == 'nt' or sys.platform == 'cygwin':
- import wpk
-
- pids = wpk.get_pids(name)
-
- else:
- data = getoutput(['ps', 'ax']).splitlines()
- pids = [int(line.split()[0]) for line in data if line.find(name) is not -1]
-
- matching_pids = [m for m in pids if m > minimun_pid]
- return matching_pids
-
-def makedirs(name):
-
- head, tail = os.path.split(name)
- if not tail:
- head, tail = os.path.split(head)
- if head and tail and not os.path.exists(head):
- try:
- makedirs(head)
- except OSError, e:
- pass
- if tail == os.curdir: # xxx/newdir/. exists if xxx/newdir exists
- return
- try:
- os.mkdir(name)
- except:
- pass
-
-# addon_details() copied from mozprofile
-def addon_details(install_rdf_fh):
- """
- returns a dictionary of details about the addon
- - addon_path : path to the addon directory
- Returns:
- {'id': u'rainbow@colors.org', # id of the addon
- 'version': u'1.4', # version of the addon
- 'name': u'Rainbow', # name of the addon
- 'unpack': # whether to unpack the addon
- """
-
- details = {
- 'id': None,
- 'unpack': False,
- 'name': None,
- 'version': None
- }
-
- def get_namespace_id(doc, url):
- attributes = doc.documentElement.attributes
- namespace = ""
- for i in range(attributes.length):
- if attributes.item(i).value == url:
- if ":" in attributes.item(i).name:
- # If the namespace is not the default one remove 'xlmns:'
- namespace = attributes.item(i).name.split(':')[1] + ":"
- break
- return namespace
-
- def get_text(element):
- """Retrieve the text value of a given node"""
- rc = []
- for node in element.childNodes:
- if node.nodeType == node.TEXT_NODE:
- rc.append(node.data)
- return ''.join(rc).strip()
-
- doc = minidom.parse(install_rdf_fh)
-
- # Get the namespaces abbreviations
- em = get_namespace_id(doc, "http://www.mozilla.org/2004/em-rdf#")
- rdf = get_namespace_id(doc, "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
-
- description = doc.getElementsByTagName(rdf + "Description").item(0)
- for node in description.childNodes:
- # Remove the namespace prefix from the tag for comparison
- entry = node.nodeName.replace(em, "")
- if entry in details.keys():
- details.update({ entry: get_text(node) })
-
- # turn unpack into a true/false value
- if isinstance(details['unpack'], basestring):
- details['unpack'] = details['unpack'].lower() == 'true'
-
- return details
-
-class Profile(object):
- """Handles all operations regarding profile. Created new profiles, installs extensions,
- sets preferences and handles cleanup."""
-
- def __init__(self, binary=None, profile=None, addons=None,
- preferences=None):
-
- self.binary = binary
-
- self.create_new = not(bool(profile))
- if profile:
- self.profile = profile
- else:
- self.profile = self.create_new_profile(self.binary)
-
- self.addons_installed = []
- self.addons = addons or []
-
- ### set preferences from class preferences
- preferences = preferences or {}
- if hasattr(self.__class__, 'preferences'):
- self.preferences = self.__class__.preferences.copy()
- else:
- self.preferences = {}
- self.preferences.update(preferences)
-
- for addon in self.addons:
- self.install_addon(addon)
-
- self.set_preferences(self.preferences)
-
- def create_new_profile(self, binary):
- """Create a new clean profile in tmp which is a simple empty folder"""
- profile = tempfile.mkdtemp(suffix='.mozrunner')
- return profile
-
- def unpack_addon(self, xpi_zipfile, addon_path):
- for name in xpi_zipfile.namelist():
- if name.endswith('/'):
- makedirs(os.path.join(addon_path, name))
- else:
- if not os.path.isdir(os.path.dirname(os.path.join(addon_path, name))):
- makedirs(os.path.dirname(os.path.join(addon_path, name)))
- data = xpi_zipfile.read(name)
- f = open(os.path.join(addon_path, name), 'wb')
- f.write(data) ; f.close()
- zi = xpi_zipfile.getinfo(name)
- os.chmod(os.path.join(addon_path,name), (zi.external_attr>>16))
-
- def install_addon(self, path):
- """Installs the given addon or directory of addons in the profile."""
-
- extensions_path = os.path.join(self.profile, 'extensions')
- if not os.path.exists(extensions_path):
- os.makedirs(extensions_path)
-
- addons = [path]
- if not path.endswith('.xpi') and not os.path.exists(os.path.join(path, 'install.rdf')):
- addons = [os.path.join(path, x) for x in os.listdir(path)]
-
- for addon in addons:
- if addon.endswith('.xpi'):
- xpi_zipfile = zipfile.ZipFile(addon, "r")
- details = addon_details(StringIO(xpi_zipfile.read('install.rdf')))
- addon_path = os.path.join(extensions_path, details["id"])
- if details.get("unpack", True):
- self.unpack_addon(xpi_zipfile, addon_path)
- self.addons_installed.append(addon_path)
- else:
- shutil.copy(addon, addon_path + '.xpi')
- else:
- # it's already unpacked, but we need to extract the id so we
- # can copy it
- details = addon_details(open(os.path.join(addon, "install.rdf"), "rb"))
- addon_path = os.path.join(extensions_path, details["id"])
- shutil.copytree(addon, addon_path, symlinks=True)
-
- def set_preferences(self, preferences):
- """Adds preferences dict to profile preferences"""
- prefs_file = os.path.join(self.profile, 'user.js')
- # Ensure that the file exists first otherwise create an empty file
- if os.path.isfile(prefs_file):
- f = open(prefs_file, 'a+')
- else:
- f = open(prefs_file, 'w')
-
- f.write('\n#MozRunner Prefs Start\n')
-
- pref_lines = ['user_pref(%s, %s);' %
- (simplejson.dumps(k), simplejson.dumps(v) ) for k, v in
- preferences.items()]
- for line in pref_lines:
- f.write(line+'\n')
- f.write('#MozRunner Prefs End\n')
- f.flush() ; f.close()
-
- def pop_preferences(self):
- """
- pop the last set of preferences added
- returns True if popped
- """
-
- # our magic markers
- delimeters = ('#MozRunner Prefs Start', '#MozRunner Prefs End')
-
- lines = file(os.path.join(self.profile, 'user.js')).read().splitlines()
- def last_index(_list, value):
- """
- returns the last index of an item;
- this should actually be part of python code but it isn't
- """
- for index in reversed(range(len(_list))):
- if _list[index] == value:
- return index
- s = last_index(lines, delimeters[0])
- e = last_index(lines, delimeters[1])
-
- # ensure both markers are found
- if s is None:
- assert e is None, '%s found without %s' % (delimeters[1], delimeters[0])
- return False # no preferences found
- elif e is None:
- assert e is None, '%s found without %s' % (delimeters[0], delimeters[1])
-
- # ensure the markers are in the proper order
- assert e > s, '%s found at %s, while %s found at %s' (delimeter[1], e, delimeter[0], s)
-
- # write the prefs
- cleaned_prefs = '\n'.join(lines[:s] + lines[e+1:])
- f = file(os.path.join(self.profile, 'user.js'), 'w')
- f.write(cleaned_prefs)
- f.close()
- return True
-
- def clean_preferences(self):
- """Removed preferences added by mozrunner."""
- while True:
- if not self.pop_preferences():
- break
-
- def clean_addons(self):
- """Cleans up addons in the profile."""
- for addon in self.addons_installed:
- if os.path.isdir(addon):
- rmtree(addon)
-
- def cleanup(self):
- """Cleanup operations on the profile."""
- def oncleanup_error(function, path, excinfo):
- #TODO: How should we handle this?
- print "Error Cleaning up: " + str(excinfo[1])
- if self.create_new:
- shutil.rmtree(self.profile, False, oncleanup_error)
- else:
- self.clean_preferences()
- self.clean_addons()
-
-class FirefoxProfile(Profile):
- """Specialized Profile subclass for Firefox"""
- preferences = {# Don't automatically update the application
- 'app.update.enabled' : False,
- # Don't restore the last open set of tabs if the browser has crashed
- 'browser.sessionstore.resume_from_crash': False,
- # Don't check for the default web browser
- 'browser.shell.checkDefaultBrowser' : False,
- # Don't warn on exit when multiple tabs are open
- 'browser.tabs.warnOnClose' : False,
- # Don't warn when exiting the browser
- 'browser.warnOnQuit': False,
- # Only install add-ons from the profile and the app folder
- 'extensions.enabledScopes' : 5,
- # Don't automatically update add-ons
- 'extensions.update.enabled' : False,
- # Don't open a dialog to show available add-on updates
- 'extensions.update.notifyUser' : False,
- }
-
- # The possible names of application bundles on Mac OS X, in order of
- # preference from most to least preferred.
- # Note: Nightly is obsolete, as it has been renamed to FirefoxNightly,
- # but it will still be present if users update an older nightly build
- # via the app update service.
- bundle_names = ['Firefox', 'FirefoxNightly', 'Nightly']
-
- # The possible names of binaries, in order of preference from most to least
- # preferred.
- @property
- def names(self):
- if sys.platform == 'darwin':
- return ['firefox', 'nightly', 'shiretoko']
- if (sys.platform == 'linux2') or (sys.platform in ('sunos5', 'solaris')):
- return ['firefox', 'mozilla-firefox', 'iceweasel']
- if os.name == 'nt' or sys.platform == 'cygwin':
- return ['firefox']
-
-class ThunderbirdProfile(Profile):
- preferences = {'extensions.update.enabled' : False,
- 'extensions.update.notifyUser' : False,
- 'browser.shell.checkDefaultBrowser' : False,
- 'browser.tabs.warnOnClose' : False,
- 'browser.warnOnQuit': False,
- 'browser.sessionstore.resume_from_crash': False,
- }
-
- # The possible names of application bundles on Mac OS X, in order of
- # preference from most to least preferred.
- bundle_names = ["Thunderbird", "Shredder"]
-
- # The possible names of binaries, in order of preference from most to least
- # preferred.
- names = ["thunderbird", "shredder"]
-
-
-class Runner(object):
- """Handles all running operations. Finds bins, runs and kills the process."""
-
- def __init__(self, binary=None, profile=None, cmdargs=[], env=None,
- kp_kwargs={}):
- if binary is None:
- self.binary = self.find_binary()
- elif sys.platform == 'darwin' and binary.find('Contents/MacOS/') == -1:
- self.binary = os.path.join(binary, 'Contents/MacOS/%s-bin' % self.names[0])
- else:
- self.binary = binary
-
- if not os.path.exists(self.binary):
- raise Exception("Binary path does not exist "+self.binary)
-
- if sys.platform == 'linux2' and self.binary.endswith('-bin'):
- dirname = os.path.dirname(self.binary)
- if os.environ.get('LD_LIBRARY_PATH', None):
- os.environ['LD_LIBRARY_PATH'] = '%s:%s' % (os.environ['LD_LIBRARY_PATH'], dirname)
- else:
- os.environ['LD_LIBRARY_PATH'] = dirname
-
- # Disable the crash reporter by default
- os.environ['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
-
- self.profile = profile
-
- self.cmdargs = cmdargs
- if env is None:
- self.env = copy.copy(os.environ)
- self.env.update({'MOZ_NO_REMOTE':"1",})
- else:
- self.env = env
- self.kp_kwargs = kp_kwargs or {}
-
- def find_binary(self):
- """Finds the binary for self.names if one was not provided."""
- binary = None
- if sys.platform in ('linux2', 'sunos5', 'solaris') \
- or sys.platform.startswith('freebsd'):
- for name in reversed(self.names):
- binary = findInPath(name)
- elif os.name == 'nt' or sys.platform == 'cygwin':
-
- # find the default executable from the windows registry
- try:
- import _winreg
- except ImportError:
- pass
- else:
- sam_flags = [0]
- # KEY_WOW64_32KEY etc only appeared in 2.6+, but that's OK as
- # only 2.6+ has functioning 64bit builds.
- if hasattr(_winreg, "KEY_WOW64_32KEY"):
- if "64 bit" in sys.version:
- # a 64bit Python should also look in the 32bit registry
- sam_flags.append(_winreg.KEY_WOW64_32KEY)
- else:
- # possibly a 32bit Python on 64bit Windows, so look in
- # the 64bit registry incase there is a 64bit app.
- sam_flags.append(_winreg.KEY_WOW64_64KEY)
- for sam_flag in sam_flags:
- try:
- # assumes self.app_name is defined, as it should be for
- # implementors
- keyname = r"Software\Mozilla\Mozilla %s" % self.app_name
- sam = _winreg.KEY_READ | sam_flag
- app_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname, 0, sam)
- version, _type = _winreg.QueryValueEx(app_key, "CurrentVersion")
- version_key = _winreg.OpenKey(app_key, version + r"\Main")
- path, _ = _winreg.QueryValueEx(version_key, "PathToExe")
- return path
- except _winreg.error:
- pass
-
- # search for the binary in the path
- for name in reversed(self.names):
- binary = findInPath(name)
- if sys.platform == 'cygwin':
- program_files = os.environ['PROGRAMFILES']
- else:
- program_files = os.environ['ProgramFiles']
-
- if binary is None:
- for bin in [(program_files, 'Mozilla Firefox', 'firefox.exe'),
- (os.environ.get("ProgramFiles(x86)"),'Mozilla Firefox', 'firefox.exe'),
- (program_files, 'Nightly', 'firefox.exe'),
- (os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe'),
- (program_files, 'Aurora', 'firefox.exe'),
- (os.environ.get("ProgramFiles(x86)"),'Aurora', 'firefox.exe')
- ]:
- path = os.path.join(*bin)
- if os.path.isfile(path):
- binary = path
- break
- elif sys.platform == 'darwin':
- for bundle_name in self.bundle_names:
- # Look for the application bundle in the user's home directory
- # or the system-wide /Applications directory. If we don't find
- # it in one of those locations, we move on to the next possible
- # bundle name.
- appdir = os.path.join("~/Applications/%s.app" % bundle_name)
- if not os.path.isdir(appdir):
- appdir = "/Applications/%s.app" % bundle_name
- if not os.path.isdir(appdir):
- continue
-
- # Look for a binary with any of the possible binary names
- # inside the application bundle.
- for binname in self.names:
- binpath = os.path.join(appdir,
- "Contents/MacOS/%s-bin" % binname)
- if (os.path.isfile(binpath)):
- binary = binpath
- break
-
- if binary:
- break
-
- if binary is None:
- raise Exception('Mozrunner could not locate your binary, you will need to set it.')
- return binary
-
- @property
- def command(self):
- """Returns the command list to run."""
- cmd = [self.binary, '-profile', self.profile.profile]
- # On i386 OS X machines, i386+x86_64 universal binaries need to be told
- # to run as i386 binaries. If we're not running a i386+x86_64 universal
- # binary, then this command modification is harmless.
- if sys.platform == 'darwin':
- if hasattr(platform, 'architecture') and platform.architecture()[0] == '32bit':
- cmd = ['arch', '-i386'] + cmd
- return cmd
-
- def get_repositoryInfo(self):
- """Read repository information from application.ini and platform.ini."""
- import ConfigParser
-
- config = ConfigParser.RawConfigParser()
- dirname = os.path.dirname(self.binary)
- repository = { }
-
- for entry in [['application', 'App'], ['platform', 'Build']]:
- (file, section) = entry
- config.read(os.path.join(dirname, '%s.ini' % file))
-
- for entry in [['SourceRepository', 'repository'], ['SourceStamp', 'changeset']]:
- (key, id) = entry
-
- try:
- repository['%s_%s' % (file, id)] = config.get(section, key);
- except:
- repository['%s_%s' % (file, id)] = None
-
- return repository
-
- def start(self):
- """Run self.command in the proper environment."""
- if self.profile is None:
- self.profile = self.profile_class()
- self.process_handler = run_command(self.command+self.cmdargs, self.env, **self.kp_kwargs)
-
- def wait(self, timeout=None):
- """Wait for the browser to exit."""
- self.process_handler.wait(timeout=timeout)
-
- if sys.platform != 'win32':
- for name in self.names:
- for pid in get_pids(name, self.process_handler.pid):
- self.process_handler.pid = pid
- self.process_handler.wait(timeout=timeout)
-
- def kill(self, kill_signal=signal.SIGTERM):
- """Kill the browser"""
- if sys.platform != 'win32':
- self.process_handler.kill()
- for name in self.names:
- for pid in get_pids(name, self.process_handler.pid):
- self.process_handler.pid = pid
- self.process_handler.kill()
- else:
- try:
- self.process_handler.kill(group=True)
- # On windows, it sometimes behooves one to wait for dust to settle
- # after killing processes. Let's try that.
- # TODO: Bug 640047 is invesitgating the correct way to handle this case
- self.process_handler.wait(timeout=10)
- except Exception, e:
- logger.error('Cannot kill process, '+type(e).__name__+' '+e.message)
-
- def stop(self):
- self.kill()
-
-class FirefoxRunner(Runner):
- """Specialized Runner subclass for running Firefox."""
-
- app_name = 'Firefox'
- profile_class = FirefoxProfile
-
- # The possible names of application bundles on Mac OS X, in order of
- # preference from most to least preferred.
- # Note: Nightly is obsolete, as it has been renamed to FirefoxNightly,
- # but it will still be present if users update an older nightly build
- # only via the app update service.
- bundle_names = ['Firefox', 'FirefoxNightly', 'Nightly']
-
- @property
- def names(self):
- if sys.platform == 'darwin':
- return ['firefox', 'nightly', 'shiretoko']
- if sys.platform in ('linux2', 'sunos5', 'solaris') \
- or sys.platform.startswith('freebsd'):
- return ['firefox', 'mozilla-firefox', 'iceweasel']
- if os.name == 'nt' or sys.platform == 'cygwin':
- return ['firefox']
-
-class ThunderbirdRunner(Runner):
- """Specialized Runner subclass for running Thunderbird"""
-
- app_name = 'Thunderbird'
- profile_class = ThunderbirdProfile
-
- # The possible names of application bundles on Mac OS X, in order of
- # preference from most to least preferred.
- bundle_names = ["Thunderbird", "Shredder"]
-
- # The possible names of binaries, in order of preference from most to least
- # preferred.
- names = ["thunderbird", "shredder"]
-
-class CLI(object):
- """Command line interface."""
-
- runner_class = FirefoxRunner
- profile_class = FirefoxProfile
- module = "mozrunner"
-
- parser_options = {("-b", "--binary",): dict(dest="binary", help="Binary path.",
- metavar=None, default=None),
- ('-p', "--profile",): dict(dest="profile", help="Profile path.",
- metavar=None, default=None),
- ('-a', "--addons",): dict(dest="addons",
- help="Addons paths to install.",
- metavar=None, default=None),
- ("--info",): dict(dest="info", default=False,
- action="store_true",
- help="Print module information")
- }
-
- def __init__(self):
- """ Setup command line parser and parse arguments """
- self.metadata = self.get_metadata_from_egg()
- self.parser = optparse.OptionParser(version="%prog " + self.metadata["Version"])
- for names, opts in self.parser_options.items():
- self.parser.add_option(*names, **opts)
- (self.options, self.args) = self.parser.parse_args()
-
- if self.options.info:
- self.print_metadata()
- sys.exit(0)
-
- # XXX should use action='append' instead of rolling our own
- try:
- self.addons = self.options.addons.split(',')
- except:
- self.addons = []
-
- def get_metadata_from_egg(self):
- import pkg_resources
- ret = {}
- dist = pkg_resources.get_distribution(self.module)
- if dist.has_metadata("PKG-INFO"):
- for line in dist.get_metadata_lines("PKG-INFO"):
- key, value = line.split(':', 1)
- ret[key] = value
- if dist.has_metadata("requires.txt"):
- ret["Dependencies"] = "\n" + dist.get_metadata("requires.txt")
- return ret
-
- def print_metadata(self, data=("Name", "Version", "Summary", "Home-page",
- "Author", "Author-email", "License", "Platform", "Dependencies")):
- for key in data:
- if key in self.metadata:
- print key + ": " + self.metadata[key]
-
- def create_runner(self):
- """ Get the runner object """
- runner = self.get_runner(binary=self.options.binary)
- profile = self.get_profile(binary=runner.binary,
- profile=self.options.profile,
- addons=self.addons)
- runner.profile = profile
- return runner
-
- def get_runner(self, binary=None, profile=None):
- """Returns the runner instance for the given command line binary argument
- the profile instance returned from self.get_profile()."""
- return self.runner_class(binary, profile)
-
- def get_profile(self, binary=None, profile=None, addons=None, preferences=None):
- """Returns the profile instance for the given command line arguments."""
- addons = addons or []
- preferences = preferences or {}
- return self.profile_class(binary, profile, addons, preferences)
-
- def run(self):
- runner = self.create_runner()
- self.start(runner)
- runner.profile.cleanup()
-
- def start(self, runner):
- """Starts the runner and waits for Firefox to exitor Keyboard Interrupt.
- Shoule be overwritten to provide custom running of the runner instance."""
- runner.start()
- print 'Started:', ' '.join(runner.command)
- try:
- runner.wait()
- except KeyboardInterrupt:
- runner.stop()
-
-
-def cli():
- CLI().run()
diff --git a/addon-sdk/source/python-lib/mozrunner/killableprocess.py b/addon-sdk/source/python-lib/mozrunner/killableprocess.py
deleted file mode 100644
index 21eac6965..000000000
--- a/addon-sdk/source/python-lib/mozrunner/killableprocess.py
+++ /dev/null
@@ -1,329 +0,0 @@
-# killableprocess - subprocesses which can be reliably killed
-#
-# Parts of this module are copied from the subprocess.py file contained
-# in the Python distribution.
-#
-# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
-#
-# Additions and modifications written by Benjamin Smedberg
-# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
-# <http://www.mozilla.org/>
-#
-# More Modifications
-# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
-# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
-#
-# By obtaining, using, and/or copying this software and/or its
-# associated documentation, you agree that you have read, understood,
-# and will comply with the following terms and conditions:
-#
-# Permission to use, copy, modify, and distribute this software and
-# its associated documentation for any purpose and without fee is
-# hereby granted, provided that the above copyright notice appears in
-# all copies, and that both that copyright notice and this permission
-# notice appear in supporting documentation, and that the name of the
-# author not be used in advertising or publicity pertaining to
-# distribution of the software without specific, written prior
-# permission.
-#
-# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
-# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
-# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
-# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""killableprocess - Subprocesses which can be reliably killed
-
-This module is a subclass of the builtin "subprocess" module. It allows
-processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
-
-It also adds a timeout argument to Wait() for a limited period of time before
-forcefully killing the process.
-
-Note: On Windows, this module requires Windows 2000 or higher (no support for
-Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
-Python 2.5+ or available from http://python.net/crew/theller/ctypes/
-"""
-
-import subprocess
-import sys
-import os
-import time
-import datetime
-import types
-import exceptions
-
-try:
- from subprocess import CalledProcessError
-except ImportError:
- # Python 2.4 doesn't implement CalledProcessError
- class CalledProcessError(Exception):
- """This exception is raised when a process run by check_call() returns
- a non-zero exit status. The exit status will be stored in the
- returncode attribute."""
- def __init__(self, returncode, cmd):
- self.returncode = returncode
- self.cmd = cmd
- def __str__(self):
- return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
-
-mswindows = (sys.platform == "win32")
-
-if mswindows:
- import winprocess
-else:
- import signal
-
-# This is normally defined in win32con, but we don't want
-# to incur the huge tree of dependencies (pywin32 and friends)
-# just to get one constant. So here's our hack
-STILL_ACTIVE = 259
-
-def call(*args, **kwargs):
- waitargs = {}
- if "timeout" in kwargs:
- waitargs["timeout"] = kwargs.pop("timeout")
-
- return Popen(*args, **kwargs).wait(**waitargs)
-
-def check_call(*args, **kwargs):
- """Call a program with an optional timeout. If the program has a non-zero
- exit status, raises a CalledProcessError."""
-
- retcode = call(*args, **kwargs)
- if retcode:
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = args[0]
- raise CalledProcessError(retcode, cmd)
-
-if not mswindows:
- def DoNothing(*args):
- pass
-
-class Popen(subprocess.Popen):
- kill_called = False
- if mswindows:
- def _execute_child(self, *args_tuple):
- # workaround for bug 958609
- if sys.hexversion < 0x02070600: # prior to 2.7.6
- (args, executable, preexec_fn, close_fds,
- cwd, env, universal_newlines, startupinfo,
- creationflags, shell,
- p2cread, p2cwrite,
- c2pread, c2pwrite,
- errread, errwrite) = args_tuple
- to_close = set()
- else: # 2.7.6 and later
- (args, executable, preexec_fn, close_fds,
- cwd, env, universal_newlines, startupinfo,
- creationflags, shell, to_close,
- p2cread, p2cwrite,
- c2pread, c2pwrite,
- errread, errwrite) = args_tuple
-
- if not isinstance(args, types.StringTypes):
- args = subprocess.list2cmdline(args)
-
- # Always or in the create new process group
- creationflags |= winprocess.CREATE_NEW_PROCESS_GROUP
-
- if startupinfo is None:
- startupinfo = winprocess.STARTUPINFO()
-
- if None not in (p2cread, c2pwrite, errwrite):
- startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
-
- startupinfo.hStdInput = int(p2cread)
- startupinfo.hStdOutput = int(c2pwrite)
- startupinfo.hStdError = int(errwrite)
- if shell:
- startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
- startupinfo.wShowWindow = winprocess.SW_HIDE
- comspec = os.environ.get("COMSPEC", "cmd.exe")
- args = comspec + " /c " + args
-
- # determine if we can create create a job
- canCreateJob = winprocess.CanCreateJobObject()
-
- # set process creation flags
- creationflags |= winprocess.CREATE_SUSPENDED
- creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
- if canCreateJob:
- # Uncomment this line below to discover very useful things about your environment
- #print "++++ killableprocess: releng twistd patch not applied, we can create job objects"
- creationflags |= winprocess.CREATE_BREAKAWAY_FROM_JOB
-
- # create the process
- hp, ht, pid, tid = winprocess.CreateProcess(
- executable, args,
- None, None, # No special security
- 1, # Must inherit handles!
- creationflags,
- winprocess.EnvironmentBlock(env),
- cwd, startupinfo)
- self._child_created = True
- self._handle = hp
- self._thread = ht
- self.pid = pid
- self.tid = tid
-
- if canCreateJob:
- # We create a new job for this process, so that we can kill
- # the process and any sub-processes
- self._job = winprocess.CreateJobObject()
- winprocess.AssignProcessToJobObject(self._job, int(hp))
- else:
- self._job = None
-
- winprocess.ResumeThread(int(ht))
- ht.Close()
-
- if p2cread is not None:
- p2cread.Close()
- if c2pwrite is not None:
- c2pwrite.Close()
- if errwrite is not None:
- errwrite.Close()
- time.sleep(.1)
-
- def kill(self, group=True):
- """Kill the process. If group=True, all sub-processes will also be killed."""
- self.kill_called = True
-
- if mswindows:
- if group and self._job:
- winprocess.TerminateJobObject(self._job, 127)
- else:
- winprocess.TerminateProcess(self._handle, 127)
- self.returncode = 127
- else:
- if group:
- try:
- os.killpg(self.pid, signal.SIGKILL)
- except: pass
- else:
- os.kill(self.pid, signal.SIGKILL)
- self.returncode = -9
-
- def wait(self, timeout=None, group=True):
- """Wait for the process to terminate. Returns returncode attribute.
- If timeout seconds are reached and the process has not terminated,
- it will be forcefully killed. If timeout is -1, wait will not
- time out."""
- if timeout is not None:
- # timeout is now in milliseconds
- timeout = timeout * 1000
-
- starttime = datetime.datetime.utcnow()
-
- if mswindows:
- if timeout is None:
- timeout = -1
- rc = winprocess.WaitForSingleObject(self._handle, timeout)
-
- if (rc == winprocess.WAIT_OBJECT_0 or
- rc == winprocess.WAIT_ABANDONED or
- rc == winprocess.WAIT_FAILED):
- # Object has either signaled, or the API call has failed. In
- # both cases we want to give the OS the benefit of the doubt
- # and supply a little time before we start shooting processes
- # with an M-16.
-
- # Returns 1 if running, 0 if not, -1 if timed out
- def check():
- now = datetime.datetime.utcnow()
- diff = now - starttime
- if (diff.seconds * 1000000 + diff.microseconds) < (timeout * 1000): # (1000*1000)
- if self._job:
- if (winprocess.QueryInformationJobObject(self._job, 8)['BasicInfo']['ActiveProcesses'] > 0):
- # Job Object is still containing active processes
- return 1
- else:
- # No job, we use GetExitCodeProcess, which will tell us if the process is still active
- self.returncode = winprocess.GetExitCodeProcess(self._handle)
- if (self.returncode == STILL_ACTIVE):
- # Process still active, continue waiting
- return 1
- # Process not active, return 0
- return 0
- else:
- # Timed out, return -1
- return -1
-
- notdone = check()
- while notdone == 1:
- time.sleep(.5)
- notdone = check()
-
- if notdone == -1:
- # Then check timed out, we have a hung process, attempt
- # last ditch kill with explosives
- self.kill(group)
-
- else:
- # In this case waitforsingleobject timed out. We have to
- # take the process behind the woodshed and shoot it.
- self.kill(group)
-
- else:
- if sys.platform in ('linux2', 'sunos5', 'solaris') \
- or sys.platform.startswith('freebsd'):
- def group_wait(timeout):
- try:
- os.waitpid(self.pid, 0)
- except OSError, e:
- pass # If wait has already been called on this pid, bad things happen
- return self.returncode
- elif sys.platform == 'darwin':
- def group_wait(timeout):
- try:
- count = 0
- if timeout is None and self.kill_called:
- timeout = 10 # Have to set some kind of timeout or else this could go on forever
- if timeout is None:
- while 1:
- os.killpg(self.pid, signal.SIG_DFL)
- while ((count * 2) <= timeout):
- os.killpg(self.pid, signal.SIG_DFL)
- # count is increased by 500ms for every 0.5s of sleep
- time.sleep(.5); count += 500
- except exceptions.OSError:
- return self.returncode
-
- if timeout is None:
- if group is True:
- return group_wait(timeout)
- else:
- subprocess.Popen.wait(self)
- return self.returncode
-
- returncode = False
-
- now = datetime.datetime.utcnow()
- diff = now - starttime
- while (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000) and ( returncode is False ):
- if group is True:
- return group_wait(timeout)
- else:
- if subprocess.poll() is not None:
- returncode = self.returncode
- time.sleep(.5)
- now = datetime.datetime.utcnow()
- diff = now - starttime
- return self.returncode
-
- return self.returncode
- # We get random maxint errors from subprocesses __del__
- __del__ = lambda self: None
-
-def setpgid_preexec_fn():
- os.setpgid(0, 0)
-
-def runCommand(cmd, **kwargs):
- if sys.platform != "win32":
- return Popen(cmd, preexec_fn=setpgid_preexec_fn, **kwargs)
- else:
- return Popen(cmd, **kwargs)
diff --git a/addon-sdk/source/python-lib/mozrunner/qijo.py b/addon-sdk/source/python-lib/mozrunner/qijo.py
deleted file mode 100644
index 058055731..000000000
--- a/addon-sdk/source/python-lib/mozrunner/qijo.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong
-from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER
-
-LPVOID = c_void_p
-LPDWORD = POINTER(DWORD)
-SIZE_T = c_size_t
-ULONG_PTR = POINTER(c_ulong)
-
-# A ULONGLONG is a 64-bit unsigned integer.
-# Thus there are 8 bytes in a ULONGLONG.
-# XXX why not import c_ulonglong ?
-ULONGLONG = BYTE * 8
-
-class IO_COUNTERS(Structure):
- # The IO_COUNTERS struct is 6 ULONGLONGs.
- # TODO: Replace with non-dummy fields.
- _fields_ = [('dummy', ULONGLONG * 6)]
-
-class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(Structure):
- _fields_ = [('TotalUserTime', LARGE_INTEGER),
- ('TotalKernelTime', LARGE_INTEGER),
- ('ThisPeriodTotalUserTime', LARGE_INTEGER),
- ('ThisPeriodTotalKernelTime', LARGE_INTEGER),
- ('TotalPageFaultCount', DWORD),
- ('TotalProcesses', DWORD),
- ('ActiveProcesses', DWORD),
- ('TotalTerminatedProcesses', DWORD)]
-
-class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION(Structure):
- _fields_ = [('BasicInfo', JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
- ('IoInfo', IO_COUNTERS)]
-
-# see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx
-class JOBOBJECT_BASIC_LIMIT_INFORMATION(Structure):
- _fields_ = [('PerProcessUserTimeLimit', LARGE_INTEGER),
- ('PerJobUserTimeLimit', LARGE_INTEGER),
- ('LimitFlags', DWORD),
- ('MinimumWorkingSetSize', SIZE_T),
- ('MaximumWorkingSetSize', SIZE_T),
- ('ActiveProcessLimit', DWORD),
- ('Affinity', ULONG_PTR),
- ('PriorityClass', DWORD),
- ('SchedulingClass', DWORD)
- ]
-
-# see http://msdn.microsoft.com/en-us/library/ms684156%28VS.85%29.aspx
-class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(Structure):
- _fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION),
- ('IoInfo', IO_COUNTERS),
- ('ProcessMemoryLimit', SIZE_T),
- ('JobMemoryLimit', SIZE_T),
- ('PeakProcessMemoryUsed', SIZE_T),
- ('PeakJobMemoryUsed', SIZE_T)]
-
-# XXX Magical numbers like 8 should be documented
-JobObjectBasicAndIoAccountingInformation = 8
-
-# ...like magical number 9 comes from
-# http://community.flexerasoftware.com/archive/index.php?t-181670.html
-# I wish I had a more canonical source
-JobObjectExtendedLimitInformation = 9
-
-class JobObjectInfo(object):
- mapping = { 'JobObjectBasicAndIoAccountingInformation': 8,
- 'JobObjectExtendedLimitInformation': 9
- }
- structures = { 8: JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION,
- 9: JOBOBJECT_EXTENDED_LIMIT_INFORMATION
- }
- def __init__(self, _class):
- if isinstance(_class, basestring):
- assert _class in self.mapping, 'Class should be one of %s; you gave %s' % (self.mapping, _class)
- _class = self.mapping[_class]
- assert _class in self.structures, 'Class should be one of %s; you gave %s' % (self.structures, _class)
- self.code = _class
- self.info = self.structures[_class]()
-
-
-QueryInformationJobObjectProto = WINFUNCTYPE(
- BOOL, # Return type
- HANDLE, # hJob
- DWORD, # JobObjectInfoClass
- LPVOID, # lpJobObjectInfo
- DWORD, # cbJobObjectInfoLength
- LPDWORD # lpReturnLength
- )
-
-QueryInformationJobObjectFlags = (
- (1, 'hJob'),
- (1, 'JobObjectInfoClass'),
- (1, 'lpJobObjectInfo'),
- (1, 'cbJobObjectInfoLength'),
- (1, 'lpReturnLength', None)
- )
-
-_QueryInformationJobObject = QueryInformationJobObjectProto(
- ('QueryInformationJobObject', windll.kernel32),
- QueryInformationJobObjectFlags
- )
-
-class SubscriptableReadOnlyStruct(object):
- def __init__(self, struct):
- self._struct = struct
-
- def _delegate(self, name):
- result = getattr(self._struct, name)
- if isinstance(result, Structure):
- return SubscriptableReadOnlyStruct(result)
- return result
-
- def __getitem__(self, name):
- match = [fname for fname, ftype in self._struct._fields_
- if fname == name]
- if match:
- return self._delegate(name)
- raise KeyError(name)
-
- def __getattr__(self, name):
- return self._delegate(name)
-
-def QueryInformationJobObject(hJob, JobObjectInfoClass):
- jobinfo = JobObjectInfo(JobObjectInfoClass)
- result = _QueryInformationJobObject(
- hJob=hJob,
- JobObjectInfoClass=jobinfo.code,
- lpJobObjectInfo=addressof(jobinfo.info),
- cbJobObjectInfoLength=sizeof(jobinfo.info)
- )
- if not result:
- raise WinError()
- return SubscriptableReadOnlyStruct(jobinfo.info)
-
-def test_qijo():
- from killableprocess import Popen
-
- popen = Popen('c:\\windows\\notepad.exe')
-
- try:
- result = QueryInformationJobObject(0, 8)
- raise AssertionError('throw should occur')
- except WindowsError, e:
- pass
-
- try:
- result = QueryInformationJobObject(0, 1)
- raise AssertionError('throw should occur')
- except NotImplementedError, e:
- pass
-
- result = QueryInformationJobObject(popen._job, 8)
- if result['BasicInfo']['ActiveProcesses'] != 1:
- raise AssertionError('expected ActiveProcesses to be 1')
- popen.kill()
-
- result = QueryInformationJobObject(popen._job, 8)
- if result.BasicInfo.ActiveProcesses != 0:
- raise AssertionError('expected ActiveProcesses to be 0')
-
-if __name__ == '__main__':
- print "testing."
- test_qijo()
- print "success!"
diff --git a/addon-sdk/source/python-lib/mozrunner/winprocess.py b/addon-sdk/source/python-lib/mozrunner/winprocess.py
deleted file mode 100644
index 16666b0eb..000000000
--- a/addon-sdk/source/python-lib/mozrunner/winprocess.py
+++ /dev/null
@@ -1,379 +0,0 @@
-# A module to expose various thread/process/job related structures and
-# methods from kernel32
-#
-# The MIT License
-#
-# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
-#
-# Additions and modifications written by Benjamin Smedberg
-# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
-# <http://www.mozilla.org/>
-#
-# More Modifications
-# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
-# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
-#
-# By obtaining, using, and/or copying this software and/or its
-# associated documentation, you agree that you have read, understood,
-# and will comply with the following terms and conditions:
-#
-# Permission to use, copy, modify, and distribute this software and
-# its associated documentation for any purpose and without fee is
-# hereby granted, provided that the above copyright notice appears in
-# all copies, and that both that copyright notice and this permission
-# notice appear in supporting documentation, and that the name of the
-# author not be used in advertising or publicity pertaining to
-# distribution of the software without specific, written prior
-# permission.
-#
-# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
-# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
-# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
-# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE
-from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD, \
- c_buffer, c_ulong, byref
-from qijo import QueryInformationJobObject
-
-LPVOID = c_void_p
-LPBYTE = POINTER(BYTE)
-LPDWORD = POINTER(DWORD)
-LPBOOL = POINTER(BOOL)
-
-def ErrCheckBool(result, func, args):
- """errcheck function for Windows functions that return a BOOL True
- on success"""
- if not result:
- raise WinError()
- return args
-
-
-# AutoHANDLE
-
-class AutoHANDLE(HANDLE):
- """Subclass of HANDLE which will call CloseHandle() on deletion."""
-
- CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE)
- CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32))
- CloseHandle.errcheck = ErrCheckBool
-
- def Close(self):
- if self.value and self.value != HANDLE(-1).value:
- self.CloseHandle(self)
- self.value = 0
-
- def __del__(self):
- self.Close()
-
- def __int__(self):
- return self.value
-
-def ErrCheckHandle(result, func, args):
- """errcheck function for Windows functions that return a HANDLE."""
- if not result:
- raise WinError()
- return AutoHANDLE(result)
-
-# PROCESS_INFORMATION structure
-
-class PROCESS_INFORMATION(Structure):
- _fields_ = [("hProcess", HANDLE),
- ("hThread", HANDLE),
- ("dwProcessID", DWORD),
- ("dwThreadID", DWORD)]
-
- def __init__(self):
- Structure.__init__(self)
-
- self.cb = sizeof(self)
-
-LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
-
-# STARTUPINFO structure
-
-class STARTUPINFO(Structure):
- _fields_ = [("cb", DWORD),
- ("lpReserved", LPWSTR),
- ("lpDesktop", LPWSTR),
- ("lpTitle", LPWSTR),
- ("dwX", DWORD),
- ("dwY", DWORD),
- ("dwXSize", DWORD),
- ("dwYSize", DWORD),
- ("dwXCountChars", DWORD),
- ("dwYCountChars", DWORD),
- ("dwFillAttribute", DWORD),
- ("dwFlags", DWORD),
- ("wShowWindow", WORD),
- ("cbReserved2", WORD),
- ("lpReserved2", LPBYTE),
- ("hStdInput", HANDLE),
- ("hStdOutput", HANDLE),
- ("hStdError", HANDLE)
- ]
-LPSTARTUPINFO = POINTER(STARTUPINFO)
-
-SW_HIDE = 0
-
-STARTF_USESHOWWINDOW = 0x01
-STARTF_USESIZE = 0x02
-STARTF_USEPOSITION = 0x04
-STARTF_USECOUNTCHARS = 0x08
-STARTF_USEFILLATTRIBUTE = 0x10
-STARTF_RUNFULLSCREEN = 0x20
-STARTF_FORCEONFEEDBACK = 0x40
-STARTF_FORCEOFFFEEDBACK = 0x80
-STARTF_USESTDHANDLES = 0x100
-
-# EnvironmentBlock
-
-class EnvironmentBlock:
- """An object which can be passed as the lpEnv parameter of CreateProcess.
- It is initialized with a dictionary."""
-
- def __init__(self, dict):
- if not dict:
- self._as_parameter_ = None
- else:
- values = ["%s=%s" % (key, value)
- for (key, value) in dict.iteritems()]
- values.append("")
- self._as_parameter_ = LPCWSTR("\0".join(values))
-
-# CreateProcess()
-
-CreateProcessProto = WINFUNCTYPE(BOOL, # Return type
- LPCWSTR, # lpApplicationName
- LPWSTR, # lpCommandLine
- LPVOID, # lpProcessAttributes
- LPVOID, # lpThreadAttributes
- BOOL, # bInheritHandles
- DWORD, # dwCreationFlags
- LPVOID, # lpEnvironment
- LPCWSTR, # lpCurrentDirectory
- LPSTARTUPINFO, # lpStartupInfo
- LPPROCESS_INFORMATION # lpProcessInformation
- )
-
-CreateProcessFlags = ((1, "lpApplicationName", None),
- (1, "lpCommandLine"),
- (1, "lpProcessAttributes", None),
- (1, "lpThreadAttributes", None),
- (1, "bInheritHandles", True),
- (1, "dwCreationFlags", 0),
- (1, "lpEnvironment", None),
- (1, "lpCurrentDirectory", None),
- (1, "lpStartupInfo"),
- (2, "lpProcessInformation"))
-
-def ErrCheckCreateProcess(result, func, args):
- ErrCheckBool(result, func, args)
- # return a tuple (hProcess, hThread, dwProcessID, dwThreadID)
- pi = args[9]
- return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID
-
-CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32),
- CreateProcessFlags)
-CreateProcess.errcheck = ErrCheckCreateProcess
-
-# flags for CreateProcess
-CREATE_BREAKAWAY_FROM_JOB = 0x01000000
-CREATE_DEFAULT_ERROR_MODE = 0x04000000
-CREATE_NEW_CONSOLE = 0x00000010
-CREATE_NEW_PROCESS_GROUP = 0x00000200
-CREATE_NO_WINDOW = 0x08000000
-CREATE_SUSPENDED = 0x00000004
-CREATE_UNICODE_ENVIRONMENT = 0x00000400
-
-# flags for job limit information
-# see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx
-JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
-JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000
-
-# XXX these flags should be documented
-DEBUG_ONLY_THIS_PROCESS = 0x00000002
-DEBUG_PROCESS = 0x00000001
-DETACHED_PROCESS = 0x00000008
-
-# CreateJobObject()
-
-CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type
- LPVOID, # lpJobAttributes
- LPCWSTR # lpName
- )
-
-CreateJobObjectFlags = ((1, "lpJobAttributes", None),
- (1, "lpName", None))
-
-CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32),
- CreateJobObjectFlags)
-CreateJobObject.errcheck = ErrCheckHandle
-
-# AssignProcessToJobObject()
-
-AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type
- HANDLE, # hJob
- HANDLE # hProcess
- )
-AssignProcessToJobObjectFlags = ((1, "hJob"),
- (1, "hProcess"))
-AssignProcessToJobObject = AssignProcessToJobObjectProto(
- ("AssignProcessToJobObject", windll.kernel32),
- AssignProcessToJobObjectFlags)
-AssignProcessToJobObject.errcheck = ErrCheckBool
-
-# GetCurrentProcess()
-# because os.getPid() is way too easy
-GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type
- )
-GetCurrentProcessFlags = ()
-GetCurrentProcess = GetCurrentProcessProto(
- ("GetCurrentProcess", windll.kernel32),
- GetCurrentProcessFlags)
-GetCurrentProcess.errcheck = ErrCheckHandle
-
-# IsProcessInJob()
-try:
- IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type
- HANDLE, # Process Handle
- HANDLE, # Job Handle
- LPBOOL # Result
- )
- IsProcessInJobFlags = ((1, "ProcessHandle"),
- (1, "JobHandle", HANDLE(0)),
- (2, "Result"))
- IsProcessInJob = IsProcessInJobProto(
- ("IsProcessInJob", windll.kernel32),
- IsProcessInJobFlags)
- IsProcessInJob.errcheck = ErrCheckBool
-except AttributeError:
- # windows 2k doesn't have this API
- def IsProcessInJob(process):
- return False
-
-
-# ResumeThread()
-
-def ErrCheckResumeThread(result, func, args):
- if result == -1:
- raise WinError()
-
- return args
-
-ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type
- HANDLE # hThread
- )
-ResumeThreadFlags = ((1, "hThread"),)
-ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32),
- ResumeThreadFlags)
-ResumeThread.errcheck = ErrCheckResumeThread
-
-# TerminateProcess()
-
-TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type
- HANDLE, # hProcess
- UINT # uExitCode
- )
-TerminateProcessFlags = ((1, "hProcess"),
- (1, "uExitCode", 127))
-TerminateProcess = TerminateProcessProto(
- ("TerminateProcess", windll.kernel32),
- TerminateProcessFlags)
-TerminateProcess.errcheck = ErrCheckBool
-
-# TerminateJobObject()
-
-TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type
- HANDLE, # hJob
- UINT # uExitCode
- )
-TerminateJobObjectFlags = ((1, "hJob"),
- (1, "uExitCode", 127))
-TerminateJobObject = TerminateJobObjectProto(
- ("TerminateJobObject", windll.kernel32),
- TerminateJobObjectFlags)
-TerminateJobObject.errcheck = ErrCheckBool
-
-# WaitForSingleObject()
-
-WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type
- HANDLE, # hHandle
- DWORD, # dwMilliseconds
- )
-WaitForSingleObjectFlags = ((1, "hHandle"),
- (1, "dwMilliseconds", -1))
-WaitForSingleObject = WaitForSingleObjectProto(
- ("WaitForSingleObject", windll.kernel32),
- WaitForSingleObjectFlags)
-
-INFINITE = -1
-WAIT_TIMEOUT = 0x0102
-WAIT_OBJECT_0 = 0x0
-WAIT_ABANDONED = 0x0080
-WAIT_FAILED = 0xFFFFFFFF
-
-# GetExitCodeProcess()
-
-GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type
- HANDLE, # hProcess
- LPDWORD, # lpExitCode
- )
-GetExitCodeProcessFlags = ((1, "hProcess"),
- (2, "lpExitCode"))
-GetExitCodeProcess = GetExitCodeProcessProto(
- ("GetExitCodeProcess", windll.kernel32),
- GetExitCodeProcessFlags)
-GetExitCodeProcess.errcheck = ErrCheckBool
-
-def CanCreateJobObject():
- # Running firefox in a job (from cfx) hangs on sites using flash plugin
- # so job creation is turned off for now. (see Bug 768651).
- return False
-
-### testing functions
-
-def parent():
- print 'Starting parent'
- currentProc = GetCurrentProcess()
- if IsProcessInJob(currentProc):
- print >> sys.stderr, "You should not be in a job object to test"
- sys.exit(1)
- assert CanCreateJobObject()
- print 'File: %s' % __file__
- command = [sys.executable, __file__, '-child']
- print 'Running command: %s' % command
- process = Popen(command)
- process.kill()
- code = process.returncode
- print 'Child code: %s' % code
- assert code == 127
-
-def child():
- print 'Starting child'
- currentProc = GetCurrentProcess()
- injob = IsProcessInJob(currentProc)
- print "Is in a job?: %s" % injob
- can_create = CanCreateJobObject()
- print 'Can create job?: %s' % can_create
- process = Popen('c:\\windows\\notepad.exe')
- assert process._job
- jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInformation')
- print 'Job info: %s' % jobinfo
- limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
- print 'LimitFlags: %s' % limitflags
- process.kill()
-
-if __name__ == '__main__':
- import sys
- from killableprocess import Popen
- nargs = len(sys.argv[1:])
- if nargs:
- if nargs != 1 or sys.argv[1] != '-child':
- raise AssertionError('Wrong flags; run like `python /path/to/winprocess.py`')
- child()
- else:
- parent()
diff --git a/addon-sdk/source/python-lib/mozrunner/wpk.py b/addon-sdk/source/python-lib/mozrunner/wpk.py
deleted file mode 100644
index 6c92f5d4e..000000000
--- a/addon-sdk/source/python-lib/mozrunner/wpk.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from ctypes import sizeof, windll, addressof, c_wchar, create_unicode_buffer
-from ctypes.wintypes import DWORD, HANDLE
-
-PROCESS_TERMINATE = 0x0001
-PROCESS_QUERY_INFORMATION = 0x0400
-PROCESS_VM_READ = 0x0010
-
-def get_pids(process_name):
- BIG_ARRAY = DWORD * 4096
- processes = BIG_ARRAY()
- needed = DWORD()
-
- pids = []
- result = windll.psapi.EnumProcesses(processes,
- sizeof(processes),
- addressof(needed))
- if not result:
- return pids
-
- num_results = needed.value / sizeof(DWORD)
-
- for i in range(num_results):
- pid = processes[i]
- process = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION |
- PROCESS_VM_READ,
- 0, pid)
- if process:
- module = HANDLE()
- result = windll.psapi.EnumProcessModules(process,
- addressof(module),
- sizeof(module),
- addressof(needed))
- if result:
- name = create_unicode_buffer(1024)
- result = windll.psapi.GetModuleBaseNameW(process, module,
- name, len(name))
- # TODO: This might not be the best way to
- # match a process name; maybe use a regexp instead.
- if name.value.startswith(process_name):
- pids.append(pid)
- windll.kernel32.CloseHandle(module)
- windll.kernel32.CloseHandle(process)
-
- return pids
-
-def kill_pid(pid):
- process = windll.kernel32.OpenProcess(PROCESS_TERMINATE, 0, pid)
- if process:
- windll.kernel32.TerminateProcess(process, 0)
- windll.kernel32.CloseHandle(process)
-
-if __name__ == '__main__':
- import subprocess
- import time
-
- # This test just opens a new notepad instance and kills it.
-
- name = 'notepad'
-
- old_pids = set(get_pids(name))
- subprocess.Popen([name])
- time.sleep(0.25)
- new_pids = set(get_pids(name)).difference(old_pids)
-
- if len(new_pids) != 1:
- raise Exception('%s was not opened or get_pids() is '
- 'malfunctioning' % name)
-
- kill_pid(tuple(new_pids)[0])
-
- newest_pids = set(get_pids(name)).difference(old_pids)
-
- if len(newest_pids) != 0:
- raise Exception('kill_pid() is malfunctioning')
-
- print "Test passed."