/usr/bin/mini-buildd-tool is in python-mini-buildd 1.0.33.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | #!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from __future__ import absolute_import
from __future__ import print_function
import sys
import os
import locale
import urllib
import urllib2
import argparse
import ConfigParser
import logging
import argcomplete
import mini_buildd.misc
import mini_buildd.setup
import mini_buildd.api
LOG = logging.getLogger("mini_buildd")
mini_buildd.misc.setup_console_logging(logging.DEBUG)
def host_completer(prefix, **kwargs): # pylint: disable=unused-argument
hosts = []
dput_cf = ConfigParser.RawConfigParser()
dput_cf.read(os.path.expanduser("~/.dput.cf"))
for section in dput_cf.sections():
def _join(host, user):
if user:
return "{u}@{h}".format(u=user, h=host)
else:
return host
def _add(host, user=None): # pylint: disable=unused-argument
# pylint: disable=cell-var-from-loop
if section.startswith("mini-buildd-"):
hosts.append(_join(section[12:], user))
else:
hosts.append(_join(section, user))
try:
host = dput_cf.get(section, "x_mini_buildd_host")
_add(host)
_add(host, "admin")
try:
users = dput_cf.get(section, "x_mini_buildd_users")
if not users:
raise Exception()
except:
users = os.getenv("USER")
for u in users.split(" "):
_add(host, u)
except:
pass
return hosts
def host_from_dput(section):
dput_cf = ConfigParser.RawConfigParser()
dput_cf.read(os.path.expanduser("~/.dput.cf"))
try:
return dput_cf.get("mini-buildd-" + section, "x_mini_buildd_host")
except:
return dput_cf.get(section, "x_mini_buildd_host")
PARSER = argparse.ArgumentParser(prog="mini-buildd-tool",
description="Command line tool to run API calls.",
epilog="Note: Uses 'python-keyring' to persist passwords (see '~/.local/share/python_keyring/')",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# PARSER.add_argument("--version", action="version", version=mini_buildd.__version__)
# python 2 compat (just use above for python 3)
PARSER.add_argument("--version", nargs=0, action=mini_buildd.misc.ArgparseVersion2StdoutAction)
PARSER.add_argument("-v", "--verbose", dest="verbosity", action="count", default=0,
help="lower log level. Give twice for max logs")
PARSER.add_argument("-q", "--quiet", dest="terseness", action="count", default=0,
help="tighten log level. Give twice for min logs")
PARSER.add_argument("-O", "--output", action="store",
default="plain", choices=["plain", "html", "python"],
help="output type")
PARSER.add_argument("-R", "--reset-save-policy", action="store_true",
help="reset save policy of used keyring (to 'ask')")
PARSER.add_argument("-P", "--protocol", action="store",
default="http", choices=["http", "https"],
help="protocol to use. Note: mini-buildd 1.0.x only speaks http -- you may use this in case you have manually set up a https proxy, though.")
PARSER.add_argument("host", action="store",
metavar="HOST",
help="target host, either '[user@]host:port', or '[user@]DPUT_TARGET'").completer = host_completer
def print_daemon_messages(headers, host):
"Output daemon messages to stderr"
msgs_header = "x-mini-buildd-message"
for msg in [v for k, v in headers.items() if msgs_header == k[:len(msgs_header)]]:
# msg line in unicode, use preferred encoding for print only (avoid mixing unicode/str in .format)
msg_line = "[{h}] {m}".format(h=host, m=mini_buildd.misc.b642u(msg))
print(msg_line.encode(locale.getpreferredencoding(), "backslashreplace"), file=sys.stderr)
print("", file=sys.stderr)
def cmd_call(args):
# Compute actual user and host to use: '[user@]host:port' or '[user@]DPUT_TARGET'
user, dummy, host = args.host.rpartition("@")
try:
host = host_from_dput(host)
except:
pass
# Log in if user given explicitely, or required by the command
if user or args.command_class.AUTH != mini_buildd.api.Command.NONE:
mini_buildd.misc.web_login(host, user, KEYRING)
# Compute api call parameters
http_args = {}
for k in [k for k in args.__dict__.keys() if k not in ["terseness", "verbosity", "host", "reset_save_policy", "protocol", "command_class", "func"]]:
# Under wheezy, value of version may be None if not given; urllib.urlencode() later will convert this value to string "None" (bummer).
# Strangely, this only happens when the ArgparseVersion2StdoutAction() workaround for --version is used (see above).
# So we need to check for None right here and use the empty string to make this compatible with wheezy.
# For 1.1.x/python3: Both the ArgparseVersion2StdoutAction() workaround above and this one must be removed.
http_args[k] = "" if args.__dict__[k] is None else args.__dict__[k]
# Confirm if required by this call
if args.command_class.CONFIRM:
if not http_args["confirm"]:
http_args["confirm"] = raw_input("Repeat command name '{c}' to confirm: ".format(c=args.command_class.COMMAND))
if not http_args["confirm"]:
raise Exception("{c}: Not confirmed, skipped.".format(c=args.command))
# Do the api call
call_url = "{p}://{b}/mini_buildd/api?{a}".format(p=args.protocol, b=host, a=urllib.urlencode(http_args))
LOG.info("API call URL: {u}".format(u=call_url))
response = urllib2.urlopen(call_url)
# Output daemon messages to stderr
if LOG.getEffectiveLevel() <= logging.WARNING:
print_daemon_messages(response.headers, args.host)
# Output result to stdout
result = response.read()
if sys.stdout.isatty() and http_args["output"] == "plain":
# On plain output to a tty, try to encode to the preferred locale
encoding = response.headers["content-type"].partition("charset=")[2]
sys.stdout.write(unicode(result, encoding if encoding else "UTF-8").encode(locale.getpreferredencoding(), "backslashreplace"))
else:
sys.stdout.write(result)
# Unfortunaetely, we cannot group the commands (yet), see http://bugs.python.org/issue14037
SUBPARSERS = PARSER.add_subparsers(title="API commands")
for cmd, cmd_cls in mini_buildd.api.COMMANDS:
if cmd != mini_buildd.api.COMMAND_GROUP:
cmd_parser = SUBPARSERS.add_parser(cmd, help=cmd_cls.docstring())
for cmd_args, cmd_kvargs in cmd_cls.ARGUMENTS:
cmd_parser.add_argument(*cmd_args, **cmd_kvargs)
if cmd_cls.CONFIRM:
cmd_parser.add_argument("--confirm", action="store", default="", metavar="COMMAND",
help="this command needs user confirmation; this option allows to force-bypass that, by explicitly repeating the command")
cmd_parser.set_defaults(func=cmd_call, command=cmd, command_class=cmd_cls)
# Parse and run
argcomplete.autocomplete(PARSER)
ARGS = PARSER.parse_args()
LOG.setLevel(logging.WARNING - (10 * (min(2, ARGS.verbosity) - min(2, ARGS.terseness))))
if LOG.getEffectiveLevel() <= logging.DEBUG:
mini_buildd.setup.DEBUG = ["exception"]
mini_buildd.misc.clone_log("keyring")
try:
# Convenience: If the old creds file is around, remove it
OLD_CREDS_FILE = os.path.join(os.getenv("HOME"), ".mini-buildd-tool.credentials")
if os.path.exists(OLD_CREDS_FILE):
os.remove(OLD_CREDS_FILE)
LOG.warn("Obsoleted creds file removed: {f}".format(f=OLD_CREDS_FILE))
# Generate global keyring object
KEYRING = mini_buildd.misc.Keyring("mini-buildd")
if ARGS.reset_save_policy:
KEYRING.reset_save_policy()
# Run the command
ARGS.func(ARGS)
except urllib2.HTTPError as e:
print_daemon_messages(e.headers, ARGS.host)
mini_buildd.setup.log_exception(LOG, ARGS.host, e)
sys.exit(1)
except Exception as e:
mini_buildd.setup.log_exception(LOG, "{u}".format(u=ARGS.host), e)
sys.exit(2)
|