Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 3 | # Low-level QEMU shell on top of QMP. |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 4 | # |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 5 | # Copyright (C) 2009, 2010 Red Hat Inc. |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 6 | # |
| 7 | # Authors: |
| 8 | # Luiz Capitulino <lcapitulino@redhat.com> |
| 9 | # |
| 10 | # This work is licensed under the terms of the GNU GPL, version 2. See |
| 11 | # the COPYING file in the top-level directory. |
| 12 | # |
| 13 | # Usage: |
| 14 | # |
| 15 | # Start QEMU with: |
| 16 | # |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 17 | # # qemu [...] -qmp unix:./qmp-sock,server |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 18 | # |
| 19 | # Run the shell: |
| 20 | # |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 21 | # $ qmp-shell ./qmp-sock |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 22 | # |
| 23 | # Commands have the following format: |
| 24 | # |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 25 | # < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 26 | # |
| 27 | # For example: |
| 28 | # |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 29 | # (QEMU) device_add driver=e1000 id=net1 |
| 30 | # {u'return': {}} |
| 31 | # (QEMU) |
John Snow | e2f9a65 | 2015-07-01 14:25:49 -0400 | [diff] [blame] | 32 | # |
| 33 | # key=value pairs also support Python or JSON object literal subset notations, |
| 34 | # without spaces. Dictionaries/objects {} are supported as are arrays []. |
| 35 | # |
| 36 | # example-command arg-name1={'key':'value','obj'={'prop':"value"}} |
| 37 | # |
| 38 | # Both JSON and Python formatting should work, including both styles of |
| 39 | # string literal quotes. Both paradigms of literal values should work, |
| 40 | # including null/true/false for JSON and None/True/False for Python. |
| 41 | # |
| 42 | # |
| 43 | # Transactions have the following multi-line format: |
| 44 | # |
| 45 | # transaction( |
| 46 | # action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ] |
| 47 | # ... |
| 48 | # action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ] |
| 49 | # ) |
| 50 | # |
| 51 | # One line transactions are also supported: |
| 52 | # |
| 53 | # transaction( action-name1 ... ) |
| 54 | # |
| 55 | # For example: |
| 56 | # |
| 57 | # (QEMU) transaction( |
| 58 | # TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1 |
| 59 | # TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0 |
| 60 | # TRANS> ) |
| 61 | # {"return": {}} |
| 62 | # (QEMU) |
| 63 | # |
| 64 | # Use the -v and -p options to activate the verbose and pretty-print options, |
| 65 | # which will echo back the properly formatted JSON-compliant QMP that is being |
| 66 | # sent to QEMU, which is useful for debugging and documentation generation. |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 67 | |
| 68 | import qmp |
Stefan Hajnoczi | ff9ec34 | 2014-01-29 12:17:31 +0100 | [diff] [blame] | 69 | import json |
John Snow | 6092c3e | 2015-04-29 15:14:02 -0400 | [diff] [blame] | 70 | import ast |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 71 | import readline |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 72 | import sys |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 73 | import pprint |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 74 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 75 | class QMPCompleter(list): |
| 76 | def complete(self, text, state): |
| 77 | for cmd in self: |
| 78 | if cmd.startswith(text): |
| 79 | if not state: |
| 80 | return cmd |
| 81 | else: |
| 82 | state -= 1 |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 83 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 84 | class QMPShellError(Exception): |
| 85 | pass |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 86 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 87 | class QMPShellBadPort(QMPShellError): |
| 88 | pass |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 89 | |
John Snow | 6092c3e | 2015-04-29 15:14:02 -0400 | [diff] [blame] | 90 | class FuzzyJSON(ast.NodeTransformer): |
| 91 | '''This extension of ast.NodeTransformer filters literal "true/false/null" |
| 92 | values in an AST and replaces them by proper "True/False/None" values that |
| 93 | Python can properly evaluate.''' |
| 94 | def visit_Name(self, node): |
| 95 | if node.id == 'true': |
| 96 | node.id = 'True' |
| 97 | if node.id == 'false': |
| 98 | node.id = 'False' |
| 99 | if node.id == 'null': |
| 100 | node.id = 'None' |
| 101 | return node |
| 102 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 103 | # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and |
| 104 | # _execute_cmd()). Let's design a better one. |
| 105 | class QMPShell(qmp.QEMUMonitorProtocol): |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 106 | def __init__(self, address, pp=None): |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 107 | qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) |
| 108 | self._greeting = None |
| 109 | self._completer = None |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 110 | self._pp = pp |
John Snow | 30bd681 | 2015-04-29 15:14:03 -0400 | [diff] [blame] | 111 | self._transmode = False |
| 112 | self._actions = list() |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 113 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 114 | def __get_address(self, arg): |
| 115 | """ |
| 116 | Figure out if the argument is in the port:host form, if it's not it's |
| 117 | probably a file path. |
| 118 | """ |
| 119 | addr = arg.split(':') |
| 120 | if len(addr) == 2: |
| 121 | try: |
| 122 | port = int(addr[1]) |
| 123 | except ValueError: |
| 124 | raise QMPShellBadPort |
| 125 | return ( addr[0], port ) |
| 126 | # socket path |
| 127 | return arg |
| 128 | |
| 129 | def _fill_completion(self): |
| 130 | for cmd in self.cmd('query-commands')['return']: |
| 131 | self._completer.append(cmd['name']) |
| 132 | |
| 133 | def __completer_setup(self): |
| 134 | self._completer = QMPCompleter() |
| 135 | self._fill_completion() |
| 136 | readline.set_completer(self._completer.complete) |
| 137 | readline.parse_and_bind("tab: complete") |
| 138 | # XXX: default delimiters conflict with some command names (eg. query-), |
| 139 | # clearing everything as it doesn't seem to matter |
| 140 | readline.set_completer_delims('') |
| 141 | |
John Snow | 6092c3e | 2015-04-29 15:14:02 -0400 | [diff] [blame] | 142 | def __parse_value(self, val): |
| 143 | try: |
| 144 | return int(val) |
| 145 | except ValueError: |
| 146 | pass |
| 147 | |
| 148 | if val.lower() == 'true': |
| 149 | return True |
| 150 | if val.lower() == 'false': |
| 151 | return False |
| 152 | if val.startswith(('{', '[')): |
| 153 | # Try first as pure JSON: |
| 154 | try: |
| 155 | return json.loads(val) |
| 156 | except ValueError: |
| 157 | pass |
| 158 | # Try once again as FuzzyJSON: |
| 159 | try: |
| 160 | st = ast.parse(val, mode='eval') |
| 161 | return ast.literal_eval(FuzzyJSON().visit(st)) |
| 162 | except SyntaxError: |
| 163 | pass |
| 164 | except ValueError: |
| 165 | pass |
| 166 | return val |
| 167 | |
John Snow | a7430a0 | 2015-04-29 15:14:01 -0400 | [diff] [blame] | 168 | def __cli_expr(self, tokens, parent): |
| 169 | for arg in tokens: |
John Snow | 6092c3e | 2015-04-29 15:14:02 -0400 | [diff] [blame] | 170 | (key, _, val) = arg.partition('=') |
| 171 | if not val: |
| 172 | raise QMPShellError("Expected a key=value pair, got '%s'" % arg) |
| 173 | |
| 174 | value = self.__parse_value(val) |
| 175 | optpath = key.split('.') |
Fam Zheng | cd159d0 | 2014-02-12 11:05:13 +0800 | [diff] [blame] | 176 | curpath = [] |
| 177 | for p in optpath[:-1]: |
| 178 | curpath.append(p) |
| 179 | d = parent.get(p, {}) |
| 180 | if type(d) is not dict: |
| 181 | raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) |
| 182 | parent[p] = d |
| 183 | parent = d |
| 184 | if optpath[-1] in parent: |
| 185 | if type(parent[optpath[-1]]) is dict: |
| 186 | raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) |
| 187 | else: |
John Snow | 6092c3e | 2015-04-29 15:14:02 -0400 | [diff] [blame] | 188 | raise QMPShellError('Cannot set "%s" multiple times' % key) |
Fam Zheng | cd159d0 | 2014-02-12 11:05:13 +0800 | [diff] [blame] | 189 | parent[optpath[-1]] = value |
John Snow | a7430a0 | 2015-04-29 15:14:01 -0400 | [diff] [blame] | 190 | |
| 191 | def __build_cmd(self, cmdline): |
| 192 | """ |
| 193 | Build a QMP input object from a user provided command-line in the |
| 194 | following format: |
| 195 | |
| 196 | < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] |
| 197 | """ |
| 198 | cmdargs = cmdline.split() |
John Snow | 30bd681 | 2015-04-29 15:14:03 -0400 | [diff] [blame] | 199 | |
| 200 | # Transactional CLI entry/exit: |
| 201 | if cmdargs[0] == 'transaction(': |
| 202 | self._transmode = True |
| 203 | cmdargs.pop(0) |
| 204 | elif cmdargs[0] == ')' and self._transmode: |
| 205 | self._transmode = False |
| 206 | if len(cmdargs) > 1: |
| 207 | raise QMPShellError("Unexpected input after close of Transaction sub-shell") |
| 208 | qmpcmd = { 'execute': 'transaction', |
| 209 | 'arguments': { 'actions': self._actions } } |
| 210 | self._actions = list() |
| 211 | return qmpcmd |
| 212 | |
| 213 | # Nothing to process? |
| 214 | if not cmdargs: |
| 215 | return None |
| 216 | |
| 217 | # Parse and then cache this Transactional Action |
| 218 | if self._transmode: |
| 219 | finalize = False |
| 220 | action = { 'type': cmdargs[0], 'data': {} } |
| 221 | if cmdargs[-1] == ')': |
| 222 | cmdargs.pop(-1) |
| 223 | finalize = True |
| 224 | self.__cli_expr(cmdargs[1:], action['data']) |
| 225 | self._actions.append(action) |
| 226 | return self.__build_cmd(')') if finalize else None |
| 227 | |
| 228 | # Standard command: parse and return it to be executed. |
John Snow | a7430a0 | 2015-04-29 15:14:01 -0400 | [diff] [blame] | 229 | qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } |
| 230 | self.__cli_expr(cmdargs[1:], qmpcmd['arguments']) |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 231 | return qmpcmd |
| 232 | |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 233 | def _print(self, qmp): |
| 234 | jsobj = json.dumps(qmp) |
| 235 | if self._pp is not None: |
| 236 | self._pp.pprint(jsobj) |
| 237 | else: |
| 238 | print str(jsobj) |
| 239 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 240 | def _execute_cmd(self, cmdline): |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 241 | try: |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 242 | qmpcmd = self.__build_cmd(cmdline) |
Fam Zheng | cd159d0 | 2014-02-12 11:05:13 +0800 | [diff] [blame] | 243 | except Exception, e: |
| 244 | print 'Error while parsing command line: %s' % e |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 245 | print 'command format: <command-name> ', |
| 246 | print '[arg-name1=arg1] ... [arg-nameN=argN]' |
| 247 | return True |
John Snow | 30bd681 | 2015-04-29 15:14:03 -0400 | [diff] [blame] | 248 | # For transaction mode, we may have just cached the action: |
| 249 | if qmpcmd is None: |
| 250 | return True |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 251 | if self._verbose: |
| 252 | self._print(qmpcmd) |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 253 | resp = self.cmd_obj(qmpcmd) |
| 254 | if resp is None: |
| 255 | print 'Disconnected' |
| 256 | return False |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 257 | self._print(resp) |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 258 | return True |
| 259 | |
| 260 | def connect(self): |
| 261 | self._greeting = qmp.QEMUMonitorProtocol.connect(self) |
| 262 | self.__completer_setup() |
| 263 | |
| 264 | def show_banner(self, msg='Welcome to the QMP low-level shell!'): |
| 265 | print msg |
| 266 | version = self._greeting['QMP']['version']['qemu'] |
| 267 | print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) |
| 268 | |
John Snow | 30bd681 | 2015-04-29 15:14:03 -0400 | [diff] [blame] | 269 | def get_prompt(self): |
| 270 | if self._transmode: |
| 271 | return "TRANS> " |
| 272 | return "(QEMU) " |
| 273 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 274 | def read_exec_command(self, prompt): |
| 275 | """ |
| 276 | Read and execute a command. |
| 277 | |
| 278 | @return True if execution was ok, return False if disconnected. |
| 279 | """ |
| 280 | try: |
| 281 | cmdline = raw_input(prompt) |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 282 | except EOFError: |
| 283 | print |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 284 | return False |
| 285 | if cmdline == '': |
| 286 | for ev in self.get_events(): |
| 287 | print ev |
| 288 | self.clear_events() |
| 289 | return True |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 290 | else: |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 291 | return self._execute_cmd(cmdline) |
| 292 | |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 293 | def set_verbosity(self, verbose): |
| 294 | self._verbose = verbose |
| 295 | |
Luiz Capitulino | 11217a7 | 2010-10-28 13:28:37 -0200 | [diff] [blame] | 296 | class HMPShell(QMPShell): |
| 297 | def __init__(self, address): |
| 298 | QMPShell.__init__(self, address) |
| 299 | self.__cpu_index = 0 |
| 300 | |
| 301 | def __cmd_completion(self): |
| 302 | for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'): |
| 303 | if cmd and cmd[0] != '[' and cmd[0] != '\t': |
| 304 | name = cmd.split()[0] # drop help text |
| 305 | if name == 'info': |
| 306 | continue |
| 307 | if name.find('|') != -1: |
| 308 | # Command in the form 'foobar|f' or 'f|foobar', take the |
| 309 | # full name |
| 310 | opt = name.split('|') |
| 311 | if len(opt[0]) == 1: |
| 312 | name = opt[1] |
| 313 | else: |
| 314 | name = opt[0] |
| 315 | self._completer.append(name) |
| 316 | self._completer.append('help ' + name) # help completion |
| 317 | |
| 318 | def __info_completion(self): |
| 319 | for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'): |
| 320 | if cmd: |
| 321 | self._completer.append('info ' + cmd.split()[1]) |
| 322 | |
| 323 | def __other_completion(self): |
| 324 | # special cases |
| 325 | self._completer.append('help info') |
| 326 | |
| 327 | def _fill_completion(self): |
| 328 | self.__cmd_completion() |
| 329 | self.__info_completion() |
| 330 | self.__other_completion() |
| 331 | |
| 332 | def __cmd_passthrough(self, cmdline, cpu_index = 0): |
| 333 | return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments': |
| 334 | { 'command-line': cmdline, |
| 335 | 'cpu-index': cpu_index } }) |
| 336 | |
| 337 | def _execute_cmd(self, cmdline): |
| 338 | if cmdline.split()[0] == "cpu": |
| 339 | # trap the cpu command, it requires special setting |
| 340 | try: |
| 341 | idx = int(cmdline.split()[1]) |
| 342 | if not 'return' in self.__cmd_passthrough('info version', idx): |
| 343 | print 'bad CPU index' |
| 344 | return True |
| 345 | self.__cpu_index = idx |
| 346 | except ValueError: |
| 347 | print 'cpu command takes an integer argument' |
| 348 | return True |
| 349 | resp = self.__cmd_passthrough(cmdline, self.__cpu_index) |
| 350 | if resp is None: |
| 351 | print 'Disconnected' |
| 352 | return False |
| 353 | assert 'return' in resp or 'error' in resp |
| 354 | if 'return' in resp: |
| 355 | # Success |
| 356 | if len(resp['return']) > 0: |
| 357 | print resp['return'], |
| 358 | else: |
| 359 | # Error |
| 360 | print '%s: %s' % (resp['error']['class'], resp['error']['desc']) |
| 361 | return True |
| 362 | |
| 363 | def show_banner(self): |
| 364 | QMPShell.show_banner(self, msg='Welcome to the HMP shell!') |
| 365 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 366 | def die(msg): |
| 367 | sys.stderr.write('ERROR: %s\n' % msg) |
| 368 | sys.exit(1) |
| 369 | |
| 370 | def fail_cmdline(option=None): |
| 371 | if option: |
| 372 | sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option) |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 373 | sys.stderr.write('qemu-shell [ -v ] [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n') |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 374 | sys.exit(1) |
| 375 | |
| 376 | def main(): |
Luiz Capitulino | 11217a7 | 2010-10-28 13:28:37 -0200 | [diff] [blame] | 377 | addr = '' |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 378 | qemu = None |
| 379 | hmp = False |
| 380 | pp = None |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 381 | verbose = False |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 382 | |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 383 | try: |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 384 | for arg in sys.argv[1:]: |
| 385 | if arg == "-H": |
| 386 | if qemu is not None: |
| 387 | fail_cmdline(arg) |
| 388 | hmp = True |
| 389 | elif arg == "-p": |
| 390 | if pp is not None: |
| 391 | fail_cmdline(arg) |
| 392 | pp = pprint.PrettyPrinter(indent=4) |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 393 | elif arg == "-v": |
| 394 | verbose = True |
Daniel P. Berrange | fa779b6 | 2012-08-15 11:33:47 +0100 | [diff] [blame] | 395 | else: |
| 396 | if qemu is not None: |
| 397 | fail_cmdline(arg) |
| 398 | if hmp: |
| 399 | qemu = HMPShell(arg) |
| 400 | else: |
| 401 | qemu = QMPShell(arg, pp) |
| 402 | addr = arg |
| 403 | |
| 404 | if qemu is None: |
| 405 | fail_cmdline() |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 406 | except QMPShellBadPort: |
| 407 | die('bad port number in command-line') |
| 408 | |
| 409 | try: |
| 410 | qemu.connect() |
| 411 | except qmp.QMPConnectError: |
| 412 | die('Didn\'t get QMP greeting message') |
| 413 | except qmp.QMPCapabilitiesError: |
| 414 | die('Could not negotiate capabilities') |
| 415 | except qemu.error: |
Luiz Capitulino | 11217a7 | 2010-10-28 13:28:37 -0200 | [diff] [blame] | 416 | die('Could not connect to %s' % addr) |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 417 | |
| 418 | qemu.show_banner() |
John Snow | 1ceca07 | 2015-04-29 15:14:04 -0400 | [diff] [blame] | 419 | qemu.set_verbosity(verbose) |
John Snow | 30bd681 | 2015-04-29 15:14:03 -0400 | [diff] [blame] | 420 | while qemu.read_exec_command(qemu.get_prompt()): |
Luiz Capitulino | 9bed0d0 | 2010-10-27 17:57:51 -0200 | [diff] [blame] | 421 | pass |
| 422 | qemu.close() |
Luiz Capitulino | cedebda | 2009-11-26 22:59:09 -0200 | [diff] [blame] | 423 | |
| 424 | if __name__ == '__main__': |
| 425 | main() |