|
|
1.1 root 1: #!/usr/bin/python
2: #
1.1.1.3 ! root 3: # Low-level QEMU shell on top of QMP.
1.1 root 4: #
1.1.1.3 ! root 5: # Copyright (C) 2009, 2010 Red Hat Inc.
1.1 root 6: #
7: # Authors:
8: # Luiz Capitulino <[email protected]>
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: #
1.1.1.3 ! root 17: # # qemu [...] -qmp unix:./qmp-sock,server
1.1 root 18: #
19: # Run the shell:
20: #
1.1.1.3 ! root 21: # $ qmp-shell ./qmp-sock
1.1 root 22: #
23: # Commands have the following format:
24: #
1.1.1.3 ! root 25: # < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
1.1 root 26: #
27: # For example:
28: #
1.1.1.3 ! root 29: # (QEMU) device_add driver=e1000 id=net1
! 30: # {u'return': {}}
! 31: # (QEMU)
1.1 root 32:
33: import qmp
34: import readline
1.1.1.3 ! root 35: import sys
1.1 root 36:
1.1.1.3 ! root 37: class QMPCompleter(list):
! 38: def complete(self, text, state):
! 39: for cmd in self:
! 40: if cmd.startswith(text):
! 41: if not state:
! 42: return cmd
! 43: else:
! 44: state -= 1
1.1 root 45:
1.1.1.3 ! root 46: class QMPShellError(Exception):
! 47: pass
! 48:
! 49: class QMPShellBadPort(QMPShellError):
! 50: pass
! 51:
! 52: # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
! 53: # _execute_cmd()). Let's design a better one.
! 54: class QMPShell(qmp.QEMUMonitorProtocol):
! 55: def __init__(self, address):
! 56: qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
! 57: self._greeting = None
! 58: self._completer = None
! 59:
! 60: def __get_address(self, arg):
! 61: """
! 62: Figure out if the argument is in the port:host form, if it's not it's
! 63: probably a file path.
! 64: """
! 65: addr = arg.split(':')
! 66: if len(addr) == 2:
! 67: try:
! 68: port = int(addr[1])
! 69: except ValueError:
! 70: raise QMPShellBadPort
! 71: return ( addr[0], port )
! 72: # socket path
! 73: return arg
! 74:
! 75: def _fill_completion(self):
! 76: for cmd in self.cmd('query-commands')['return']:
! 77: self._completer.append(cmd['name'])
! 78:
! 79: def __completer_setup(self):
! 80: self._completer = QMPCompleter()
! 81: self._fill_completion()
! 82: readline.set_completer(self._completer.complete)
! 83: readline.parse_and_bind("tab: complete")
! 84: # XXX: default delimiters conflict with some command names (eg. query-),
! 85: # clearing everything as it doesn't seem to matter
! 86: readline.set_completer_delims('')
1.1 root 87:
1.1.1.3 ! root 88: def __build_cmd(self, cmdline):
! 89: """
! 90: Build a QMP input object from a user provided command-line in the
! 91: following format:
! 92:
! 93: < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
! 94: """
! 95: cmdargs = cmdline.split()
! 96: qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
! 97: for arg in cmdargs[1:]:
! 98: opt = arg.split('=')
! 99: try:
! 100: value = int(opt[1])
! 101: except ValueError:
! 102: value = opt[1]
! 103: qmpcmd['arguments'][opt[0]] = value
! 104: return qmpcmd
! 105:
! 106: def _execute_cmd(self, cmdline):
! 107: try:
! 108: qmpcmd = self.__build_cmd(cmdline)
! 109: except:
! 110: print 'command format: <command-name> ',
! 111: print '[arg-name1=arg1] ... [arg-nameN=argN]'
! 112: return True
! 113: resp = self.cmd_obj(qmpcmd)
! 114: if resp is None:
! 115: print 'Disconnected'
! 116: return False
! 117: print resp
! 118: return True
! 119:
! 120: def connect(self):
! 121: self._greeting = qmp.QEMUMonitorProtocol.connect(self)
! 122: self.__completer_setup()
1.1 root 123:
1.1.1.3 ! root 124: def show_banner(self, msg='Welcome to the QMP low-level shell!'):
! 125: print msg
! 126: version = self._greeting['QMP']['version']['qemu']
! 127: print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
1.1 root 128:
1.1.1.3 ! root 129: def read_exec_command(self, prompt):
! 130: """
! 131: Read and execute a command.
! 132:
! 133: @return True if execution was ok, return False if disconnected.
! 134: """
1.1 root 135: try:
1.1.1.3 ! root 136: cmdline = raw_input(prompt)
1.1 root 137: except EOFError:
138: print
1.1.1.3 ! root 139: return False
! 140: if cmdline == '':
! 141: for ev in self.get_events():
! 142: print ev
! 143: self.clear_events()
! 144: return True
1.1 root 145: else:
1.1.1.3 ! root 146: return self._execute_cmd(cmdline)
! 147:
! 148: class HMPShell(QMPShell):
! 149: def __init__(self, address):
! 150: QMPShell.__init__(self, address)
! 151: self.__cpu_index = 0
! 152:
! 153: def __cmd_completion(self):
! 154: for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
! 155: if cmd and cmd[0] != '[' and cmd[0] != '\t':
! 156: name = cmd.split()[0] # drop help text
! 157: if name == 'info':
! 158: continue
! 159: if name.find('|') != -1:
! 160: # Command in the form 'foobar|f' or 'f|foobar', take the
! 161: # full name
! 162: opt = name.split('|')
! 163: if len(opt[0]) == 1:
! 164: name = opt[1]
! 165: else:
! 166: name = opt[0]
! 167: self._completer.append(name)
! 168: self._completer.append('help ' + name) # help completion
! 169:
! 170: def __info_completion(self):
! 171: for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
! 172: if cmd:
! 173: self._completer.append('info ' + cmd.split()[1])
! 174:
! 175: def __other_completion(self):
! 176: # special cases
! 177: self._completer.append('help info')
! 178:
! 179: def _fill_completion(self):
! 180: self.__cmd_completion()
! 181: self.__info_completion()
! 182: self.__other_completion()
! 183:
! 184: def __cmd_passthrough(self, cmdline, cpu_index = 0):
! 185: return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
! 186: { 'command-line': cmdline,
! 187: 'cpu-index': cpu_index } })
! 188:
! 189: def _execute_cmd(self, cmdline):
! 190: if cmdline.split()[0] == "cpu":
! 191: # trap the cpu command, it requires special setting
1.1 root 192: try:
1.1.1.3 ! root 193: idx = int(cmdline.split()[1])
! 194: if not 'return' in self.__cmd_passthrough('info version', idx):
! 195: print 'bad CPU index'
! 196: return True
! 197: self.__cpu_index = idx
! 198: except ValueError:
! 199: print 'cpu command takes an integer argument'
! 200: return True
! 201: resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
! 202: if resp is None:
! 203: print 'Disconnected'
! 204: return False
! 205: assert 'return' in resp or 'error' in resp
! 206: if 'return' in resp:
! 207: # Success
! 208: if len(resp['return']) > 0:
! 209: print resp['return'],
! 210: else:
! 211: # Error
! 212: print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
! 213: return True
! 214:
! 215: def show_banner(self):
! 216: QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
! 217:
! 218: def die(msg):
! 219: sys.stderr.write('ERROR: %s\n' % msg)
! 220: sys.exit(1)
! 221:
! 222: def fail_cmdline(option=None):
! 223: if option:
! 224: sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
! 225: sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
! 226: sys.exit(1)
! 227:
! 228: def main():
! 229: addr = ''
! 230: try:
! 231: if len(sys.argv) == 2:
! 232: qemu = QMPShell(sys.argv[1])
! 233: addr = sys.argv[1]
! 234: elif len(sys.argv) == 3:
! 235: if sys.argv[1] != '-H':
! 236: fail_cmdline(sys.argv[1])
! 237: qemu = HMPShell(sys.argv[2])
! 238: addr = sys.argv[2]
! 239: else:
! 240: fail_cmdline()
! 241: except QMPShellBadPort:
! 242: die('bad port number in command-line')
! 243:
! 244: try:
! 245: qemu.connect()
! 246: except qmp.QMPConnectError:
! 247: die('Didn\'t get QMP greeting message')
! 248: except qmp.QMPCapabilitiesError:
! 249: die('Could not negotiate capabilities')
! 250: except qemu.error:
! 251: die('Could not connect to %s' % addr)
! 252:
! 253: qemu.show_banner()
! 254: while qemu.read_exec_command('(QEMU) '):
! 255: pass
! 256: qemu.close()
1.1 root 257:
258: if __name__ == '__main__':
259: main()
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.