133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org#!/usr/bin/env python 233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# 333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# Copyright 2012 the V8 project authors. All rights reserved. 433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# Redistribution and use in source and binary forms, with or without 533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# modification, are permitted provided that the following conditions are 633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# met: 733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# 833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# * Redistributions of source code must retain the above copyright 933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# notice, this list of conditions and the following disclaimer. 1033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# * Redistributions in binary form must reproduce the above 1133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# copyright notice, this list of conditions and the following 1233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# disclaimer in the documentation and/or other materials provided 1333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# with the distribution. 1433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# * Neither the name of Google Inc. nor the names of its 1533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# contributors may be used to endorse or promote products derived 1633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# from this software without specific prior written permission. 1733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# 1833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 3033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 3133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport os 3233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport subprocess 3333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport sys 3433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 3533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 3633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgPIDFILE = "/tmp/v8-distributed-testing-server.pid" 3733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgROOT = os.path.abspath(os.path.dirname(sys.argv[0])) 3833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 3933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 4033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgdef _PrintUsage(): 4133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("""Usage: python %s COMMAND 4233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 4333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgWhere COMMAND can be any of: 4433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org start Starts the server. Forks to the background. 4533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org stop Stops the server. 4633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org restart Stops, then restarts the server. 4733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org setup Creates or updates the environment for the server to run. 4833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org update Alias for "setup". 4933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org trust <keyfile> Adds the given public key to the list of trusted keys. 5033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org help Displays this help text. 5133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ % sys.argv[0]) 5233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 5333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 5433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgdef _IsDaemonRunning(): 5533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org return os.path.exists(PIDFILE) 5633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 5733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 5833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgdef _Cmd(cmd): 5933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org code = subprocess.call(cmd, shell=True) 6033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if code != 0: 6133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Command '%s' returned error code %d" % (cmd, code)) 6233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(code) 6333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 6433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 6533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgdef Update(): 6633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Create directory for private data storage. 6733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org data_dir = os.path.join(ROOT, "data") 6833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not os.path.exists(data_dir): 6933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.makedirs(data_dir) 7033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 7133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Create directory for trusted public keys of peers (and self). 7233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org trusted_dir = os.path.join(ROOT, "trusted") 7333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not os.path.exists(trusted_dir): 7433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.makedirs(trusted_dir) 7533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 7633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Install UltraJSON. It is much faster than Python's builtin json. 7733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org try: 7833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org import ujson #@UnusedImport 7933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org except ImportError: 8033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Install pip if it doesn't exist. 8133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org code = subprocess.call("which pip > /dev/null", shell=True) 8233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if code != 0: 8333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org apt_get_code = subprocess.call("which apt-get > /dev/null", shell=True) 8433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if apt_get_code == 0: 8533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Installing pip...") 8633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("sudo apt-get install python-pip") 8733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 8833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Please install pip on your machine. You can get it at: " 8933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org "http://www.pip-installer.org/en/latest/installing.html " 9033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org "or via your distro's package manager.") 9133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 9233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Using pip to install UltraJSON...") 9333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("sudo pip install ujson") 9433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 9533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Make sure we have a key pair for signing binaries. 9633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org privkeyfile = os.path.expanduser("~/.ssh/v8_dtest") 9733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not os.path.exists(privkeyfile): 9833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("ssh-keygen -t rsa -f %s -N '' -q" % privkeyfile) 9933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org fingerprint = subprocess.check_output("ssh-keygen -lf %s" % privkeyfile, 10033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org shell=True) 10133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org fingerprint = fingerprint.split(" ")[1].replace(":", "")[:16] 10233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pubkeyfile = os.path.join(trusted_dir, "%s.pem" % fingerprint) 10333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if (not os.path.exists(pubkeyfile) or 10433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.path.getmtime(pubkeyfile) < os.path.getmtime(privkeyfile)): 10533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("openssl rsa -in %s -out %s -pubout" % (privkeyfile, pubkeyfile)) 10633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org with open(pubkeyfile, "a") as f: 10733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org f.write(fingerprint + "\n") 10833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org datafile = os.path.join(data_dir, "mypubkey") 10933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org with open(datafile, "w") as f: 11033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org f.write(fingerprint + "\n") 11133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 11233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Check out or update the server implementation in the current directory. 11333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org testrunner_dir = os.path.join(ROOT, "testrunner") 11433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if os.path.exists(os.path.join(testrunner_dir, "server/daemon.py")): 11533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("cd %s; svn up" % testrunner_dir) 11633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 11733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org path = ("http://v8.googlecode.com/svn/branches/bleeding_edge/" 11833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org "tools/testrunner") 11933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("svn checkout --force %s %s" % (path, testrunner_dir)) 12033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 12133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Update this very script. 12233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org path = ("http://v8.googlecode.com/svn/branches/bleeding_edge/" 12333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org "tools/test-server.py") 12433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org scriptname = os.path.abspath(sys.argv[0]) 12533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("svn cat %s > %s" % (path, scriptname)) 12633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 12733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Check out or update V8. 12833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org v8_dir = os.path.join(ROOT, "v8") 12933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if os.path.exists(v8_dir): 13033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("cd %s; git fetch" % v8_dir) 13133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 13233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _Cmd("git clone git://github.com/v8/v8.git %s" % v8_dir) 13333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 13433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Finished.") 13533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 13633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 13733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# Handle "setup" here, because when executing that we can't import anything 13833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# else yet. 13933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgif __name__ == "__main__" and len(sys.argv) == 2: 14033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if sys.argv[1] in ("setup", "update"): 14133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if _IsDaemonRunning(): 14233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Please stop the server before updating. Exiting.") 14333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 14433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org Update() 14533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(0) 14633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Other parameters are handled below. 14733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 14833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 14933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org#========================================================== 15033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# At this point we can assume that the implementation is available, 15133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# so we can import it. 15233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgtry: 15333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org from testrunner.server import constants 15433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org from testrunner.server import local_handler 15533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org from testrunner.server import main 15633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgexcept Exception, e: 15733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print(e) 15833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Failed to import implementation. Have you run 'setup'?") 15933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 16033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 16133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 16233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgdef _StartDaemon(daemon): 16333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not os.path.isdir(os.path.join(ROOT, "v8")): 16433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("No 'v8' working directory found. Have you run 'setup'?") 16533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 16633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon.start() 16733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 16833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 16933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgif __name__ == "__main__": 17033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if len(sys.argv) == 2: 17133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org arg = sys.argv[1] 17233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if arg == "start": 17333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon = main.Server(PIDFILE, ROOT) 17433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _StartDaemon(daemon) 17533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org elif arg == "stop": 17633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon = main.Server(PIDFILE, ROOT) 17733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon.stop() 17833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org elif arg == "restart": 17933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon = main.Server(PIDFILE, ROOT) 18033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon.stop() 18133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _StartDaemon(daemon) 18233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org elif arg in ("help", "-h", "--help"): 18333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _PrintUsage() 18433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org elif arg == "status": 18533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not _IsDaemonRunning(): 18633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Server not running.") 18733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 18833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print(local_handler.LocalQuery([constants.REQUEST_STATUS])) 18933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 19033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Unknown command") 19133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _PrintUsage() 19233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(2) 19333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org elif len(sys.argv) == 3: 19433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org arg = sys.argv[1] 19533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if arg == "approve": 19633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org filename = sys.argv[2] 19733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not os.path.exists(filename): 19833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("%s does not exist.") 19933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 20033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org filename = os.path.abspath(filename) 20133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if _IsDaemonRunning(): 20233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org response = local_handler.LocalQuery([constants.ADD_TRUSTED, filename]) 20333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 20433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org daemon = main.Server(PIDFILE, ROOT) 20533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org response = daemon.CopyToTrusted(filename) 20633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Added certificate %s to trusted certificates." % response) 20733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 20833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Unknown command") 20933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _PrintUsage() 21033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(2) 21133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 21233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print("Unknown command") 21333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org _PrintUsage() 21433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(2) 21533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(0) 216