| #!/usr/bin/python |
| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import functools |
| import logging |
| import os |
| import signal |
| import sys |
| |
| # We want to use correct version of libraries even when executed through symlink. |
| path = os.path.realpath(__file__) |
| path = os.path.normpath(os.path.join(os.path.dirname(path), '..', '..')) |
| sys.path.insert(0, path) |
| del path |
| |
| from chromite.buildbot import constants |
| |
| |
| def _rindex(haystack, needle): |
| if needle not in haystack: |
| raise ValueError('%s not found' % needle) |
| return len(haystack) - haystack[::-1].index(needle) - 1 |
| |
| |
| def FindTarget(target, argv): |
| # Compatibility for badly named scripts that can't yet be fixed. |
| if target.endswith('.py'): |
| target = os.path.splitext(target)[0] |
| |
| # Turn the path into something we can import from the chromite tree. |
| target = target.split(os.sep) |
| target = target[_rindex(target, 'chromite'):] |
| # Our bin dir is just scripts stuff. |
| if target[1] == 'bin': |
| target[1] = 'scripts' |
| |
| module = __import__('.'.join(target)) |
| # __import__ gets us the root of the namespace import; walk our way up. |
| for attr in target[1:]: |
| module = getattr(module, attr) |
| |
| if hasattr(module, 'main'): |
| # This might seem pointless using functools here; we do it since down the |
| # line, the allowed 'main' prototypes will change. Thus we define the |
| # FindTarget api to just return an invokable, allowing the consumer |
| # to not know nor care about the specifics. |
| return functools.partial(module.main, argv) |
| return None |
| |
| |
| class _ShutDownException(SystemExit): |
| |
| def __init__(self, signal, message): |
| self.signal = signal |
| # Setup a usage mesage primarily for any code that may intercept it |
| # while this exception is crashing back up the stack to us. |
| SystemExit.__init__(self, message) |
| |
| |
| def _DefaultHandler(signum, frame): |
| # Don't double process sigterms; just trigger shutdown from the first |
| # exception. |
| signal.signal(signum, signal.SIG_IGN) |
| raise _ShutDownException( |
| signum, "Received signal %i; shutting down" % (signum,)) |
| |
| |
| if __name__ == '__main__': |
| target = os.path.abspath(sys.argv[0]) |
| name = os.path.basename(target) |
| target = FindTarget(target, sys.argv[1:]) |
| if target is None: |
| print >>sys.stderr, ("Internal error detected in wrapper.py: no main " |
| "functor found in module %r." % (name,)) |
| sys.exit(100) |
| |
| # Set up basic logging information for all modules that use logging. |
| # Note a script target may setup default logging in it's module namespace |
| # which will take precedence over this. |
| logging.basicConfig( |
| level=logging.DEBUG, |
| format=constants.LOGGER_FMT, |
| datefmt=constants.LOGGER_DATE_FMT) |
| |
| |
| signal.signal(signal.SIGTERM, _DefaultHandler) |
| |
| ret = 1 |
| try: |
| ret = target() |
| except _ShutDownException, e: |
| print >>sys.stderr, ("%s: Signaled to shutdown: caught %i signal." % |
| (name, e.signal,)) |
| except SystemExit, e: |
| # Right now, let this crash through- longer term, we'll update the scripts |
| # in question to not use sys.exit, and make this into a flagged error. |
| raise |
| except Exception, e: |
| print >>sys.stderr, ("%s: Unhandled exception:" % (name,)) |
| raise |
| |
| if ret is None: |
| ret = 0 |
| sys.exit(ret) |